Sys is undefined : “Ajax X Hora do Sistema”. AJAX ou .NET Framework ?
Introdução:
Já faz um bom tempo que não posto nada e aqui uma explicação: Transferi-me para São Paulo e ate ajustar tudo esta sendo bem mais demorado do que previa...
Costumo sempre dar uma pesquisa pela web sobre AJAX para ver como a comunidade vem absorvendo esta funcionalidade que hoje é indispensável para qualquer aplicativo web.
Um post em um blog me chamou atenção hoje. Não pelo seu conteúdo, mas pela dúvida gerada dentro de um cenário que dificilmente acontece. Como lá só tinha o problema que apontava para o AJAX , sem a causa e explicação dos motivos resolvi escrever este pequeno post para compartilhar com a comunidade alguns fundamentos e conhecimento sobre este cenário . Afinal não adianta nada trazer problemas sem soluções e a devida pesquisa , isso pode leva a outra conclusão.
Qual é o cenário que estamos falando :
Você esta criando um aplicativo web habilitado para usar Ajax. A aplicação criada esta correta, o arquivo de configuração esta perfeito e o servidor também este corretamente configurado com os assemblys do AJAX devidamente registrado no GAC. Ao executar o aplicativo aparece a mensagem de “Sys is undefined”. Verificando a Data do servidor você percebe que a Data esta errada (com alguns anos para trás...). A solução imediata é simples : É só corrigir a Data, porem o que chamou atenção foi justamente a dúvida deixada :
“Mas por que diabos recusar uma data desatualizada, só porque ele não existia na época? Tudo bem que sabe-se lá porque alguém precisaria disso, mas e se eu precisar do meu servidor com a data em 2002 ?”
A dúvida é mais que justificada! porem não existe nenhuma relação com a existência ou não do produto na época.
A idéia deste post é entender melhor o que esta acontecendo, como funciona o AJAX ASP. NET e alguns outros detalhes do Framework .NET . Ao final descobrir as respostas e dar um “workarround” para este cenário caso seja necessário.
Para se chegar à explicação iremos precisar usar uma ferramenta(que inclusive já citei neste blog) como fundamentais para qualquer desenvolvedor:
http://linhadecodigo.com.br/cs2/blogs/fcerqueira/archive/2007/02/22/588.aspx
“Refletor (Embora muitos achem que a intenção é descompilar e ver o código fonte, estas ferramentas tem uma tarefa mais nobre : Facilitar o entendimento do .NET framework,
Ajudar a melhorar a performance e reduzir os cast, Uma grande fonte de aprendizado”
A mensagem de Erro para o usuário :
“Microsoft JScript runtime error: 'Sys.WebForms.PageRequestManager' is null or not an object”
Esta mensagem normalmente ocorre quando não temos instalado (diga-se registradas) as dlls do AJAX. Mas esta mensagem (neste nosso caso) é uma conseqüência e não origem do erro uma vez que tudo esta registrado de forma correta.
Entendendo melhor:
O AJAX é formado por um conjunto de bibliotecas que estão no lado do servidor e no lado do cliente. Quando tornamos nossos aplicativos habilitados para Ajax as requisições são interceptadas pela biblioteca que esta no servidor que executa o devido tratamento, inclusive incluído na página que retorna para o cliente os script necessários (Veja o exemplo abaixo) :
...
<script src="/AJAXEnabledWebSite1/ScriptResource.axd?d=GVl2-bmV...&t=633022221745931040" type="text/javascript"></script>
O ScriptResource.axd é um recurso introduzido no Framework 2.0 que permite trabalhar com os resouces anexados no Assembly . Sua estrutura é desta forma :
WebResource.axd?d=recurso & t =Tempo. "d" significa o recurso da Web solicitada que vem encripitado e o "t" é a data/hora para o assembly solicitado. O parâmetro “t” ajuda a identificar se houve alterações feitas o recurso.
Para quem desejar saber um pouco mais, abaixo um artigo no MSDN que trata deste assunto :
http://support.microsoft.com/kb/910442/pt-br
A mensagem de Erro de origem:
Agora conhecendo melhor esta premissa de como os script são gerados e enviados, podemos partir para a origem do problema, testando a execução dos script que retorna a mensagem real de origem abaixo :
Specified argument was out of the range of valid values.
Parameter name: utcDate
...
Exception Details: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: utcDate
...
Stack Trace:
|
[ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: utcDate]
System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate) +3299747
System.Web.HttpCachePolicy.SetLastModified(DateTime date) +47
mpletedSynchronously) +64
...
|
Ok, até aqui quase nenhuma novidade. Apenas identificamos a origem do erro, mas ainda restam duas perguntas: Porque deu este erro? Como posso contornar o problema mantendo a data do servidor (mesmo que desatualizada)? Vamos então continuar para termos as respostas J
Porque deu este erro ?
Para responder vamos usaremos dois recursos: O Refletor (para ver a lógica e codigo que o framework utiliza) e o código fonte do AJAX.NET .
Para quem não sabe o código fonte do AJAX.NET esta disponível para estudo e para debug em :
http://www.microsoft.com/downloads/details.aspx?FamilyID=ef2c1acc-051a-4fe6-ad72-f3bed8623b43&DisplayLang=en
O erro apresentado é no método UtcSetLastModified da classe System.Web.HttpCachePolicy.
Usando o Refletor temos os seguintes códigos abaixo:
public void SetLastModified(DateTime date)
{ DateTime utcDate = DateTimeUtil.ConvertToUniversalTime(date);
this.UtcSetLastModified(utcDate);
}
O Texto em vermelho grifado é o trecho de código responsável pela mensagem de erro, ou seja: caso a data passada seja maior que a data corrente ocorre uma exception.
Mas que Data é essa que é passada como parâmetro?
Agora precisamos usar o segundo recurso de analise: O Código fonte do AJAX ASP.NET.
Como estamos tratando uma requisição de resource já explicada, se buscarmos pelo titulo dos arquivos iremos encontrar um arquivo de nome: ScriptResourceHandler.cs e dentro dele temos o método abaixo :
private static DateTime GetLastWriteTime(Assembly assembly)
{
string codeBase = GetCodeBaseWithAssert(assembly);
Uri codeBaseUri = new Uri(codeBase);
if (!codeBaseUri.IsFile) return DateTime.MinValue;
string localPath = codeBaseUri.LocalPath;
FileIOPermission p = new FileIOPermission(FileIOPermissionAccess.Read, localPath);
p.Assert();
return File.GetLastWriteTime(localPath);
}
Analisando o código o leitor poderá perceber que o parâmetro passado é a data do assembly que possui o resouce a ser incluído.
Explicando o motivo de erro apresentado:
De um lado temos um assembly que foi gerado com uma determinada Data, e de outro lado temos a Data corrente do servidor .
Como a Data do assembly é superior a data Corrente do servidor temos uma inconsistência que é verificada pelo Framework! Ou seja, o erro gerado não é do AJAX e sim do próprio .NET framework que executa a verificação de data.
Respondendo a segunda pergunta: Caso seja necessário ter um servidor com data desatualizada e ainda assim executar assemblys que foram gerados com data superior a data corrente ?
Como se trata de um cenário extremante atípico precisamos fazer um “workarround” (contornar o problema) . A forma mais simples é modificar a data do assembly e para isso podemos usar váriso aplicativos para este fim, um deles é o File Touch utility http://www.jddesign.f2s.com/touchpro.htm.
Conclusão :
Objetivo deste artigo/post foi apenas detalhar algumas funcionalidades que muitas das vezes passa despercebida pela maioria dos desenvolvedores e reforçar alguns conceitos sobre o desenvolvimento na web.
Um dos pontos que sempre reforço nos trabalhos de mentoring que exerço é a necessidade de se conhecer bem os fundamentos da web e das tecnologias envolvidas para podemos ter uma compreensão melhor dos cenários e problemas que sempre irão surgir e com isso podemos fazer uma analise mais clara das origens dos problemas evitando-se com isso se chegar a conclusões que podem ser equivocadas. Claro que este cenário apresentado não é trivial e requer uma analise em maior profundidade para se tirara algumas conclusões.
Espero ter dado uma pequena parcela de ajuda para que os desenvolvedores se interessem em compreender melhor os fundamentos e não apenas os resultados.