Sumário
Introdução
Neste artigo irei ensinar uma técnica em asp.net para embutir javascript em uma página HTML como um recurso embutido (embedded resource), utilizando nossos próprios componentes.
Muitas vezes precisamos escrever nossos controles em asp, e quando vamos disponibilizar nossa biblioteca de controles nos deparamos com a necessidade de enviar todo o código que foi feito em javascript e nossos controles compilados não são instalados corretamente, algum diretório da nossa estrutura é modificado pelo usuário, algum diretório não é criado, logo, nossos controles deixam de funcionar como gostaríamos por causa de uma instalação errada ou uma permissão de arquivo.
Um recurso interessante que vejo nos controles asp é a possibilidade de embutirmos todo o nosso código javascript dentro da DLL do controle, isso mesmo, compilar e embutir o nosso javascript e depois utilizar ele com um recurso não mais precisando fazer a criação de diretórios, ou distribuir nossos arquivos .js junto com nosso controle.
O que é Recurso Embutido (Embedded Resource)?
Recurso embutido é quando inserimos algum arquivo dentro de nossa aplicação e o mesmo é compilado e enviado junto com a aplicação dentro do mesmo arquivo, seja um .exe ou .DLL.
Exemplos práticos de arquivos embutidos são quando criamos aplicações Windows no Visual Studio, se repararmos quando colocamos uma imagem em um “form” é criado um arquivo com a extensão .resx, este arquivo é usado para embutir o recurso, no caso uma imagem.
Outro arquivo que o Visual Studio cria é o “Resources.resx” este arquivo é criado quando inserimos imagens, textos, áudio, qualquer arquivo que iremos usar como um recurso em nossa aplicação.
Em asp isso também é possível, neste arquivo irei ensinar como fazer com javascript, mas isso não quer dizer que é possível apenas com javascript, pode ser utilizado para css, imagens e até mesmo páginas inteiras. Mas o nosso foco é javascript.
O que veremos?
Iremos ver desde o modo mais simples de embutir um recurso:
Até o tratamento com o nosso próprio handler.
Iremos ver aqui também como registrar este recurso dentro da tag HEAD e não na tag BODY
Ok! Chega de lero-lero e vamos ao que interessa, se você chegou até aqui é porque já está habituado com o visual Studio. Vou assumir que você tem conhecimento em desenvolvimento web.
Iniciando
Para o nosso exemplo iremos criar dois projetos, uma aplicação web e uma aplicação de controle.
Passos:
- Abra o Visual Studio e crie uma nova aplicação web (ASP.Net Web Application), a minha eu chamei de EmbeddingJavascript;
- Adicione um segundo novo projeto à sua solução, do tipo “ASP.Net Server Control” o meu eu chamei de EmbeddingJavascriptMyControl.
- No projeto “EmbeddingJavascriptMyControl” adicione um novo item do tipo “ASP.Net Server Control” e de o nome de “MyControl”
Agora já temos dois projetos em nossa solução veja se a sua está parecida com a minha:
Agora vamos à montagem dos códigos e configurações necessárias para fazer o nosso script embutido.
Abra sua classe “MyControl.cs” no método RenderContents iremos criar o nosso componente, um componente simples que irá pedir um texto qualquer com um botão para ser clicado.
Veja o código
protected override void RenderContents(HtmlTextWriter output) { string html = @"Digite algo: <input type='text' id='txtAlgo' /> <input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' />"; output.Write(html); }
Reparem na linha cinco do código que temos um onclick=’CallAlert(txtNome.value);’ para não dar erro em nossa aplicação temos que definir o script e criar a função js CallAlert.
Então mãos a obra.
No projeto “EmbeddingJavascriptMyControl” Adicione um novo item do tipo “JScript File” o meu eu chamei de “MyControl.js” nele iremos digitar o nosso código para a função CallAlert
/** * Exibe uma mensagem de alerta * @param m (string) mensagem a ser exibida */ function CallAlert(m) { if(m=='') alert("Informe a mensagem a ser exibida"); else alert(m); }
Feito isso voltaremos ao nosso projeto “EmbeddingJavascript”, siga os passos descritos para colocar o nosso componente na página Default.aspx
- Faça a referencia ao projeto “EmbeddingJavascriptMyControl”;
- Feito a referencia abra o arquivo Default.aspx, nele iremos colocar uma diretiva para que possamos ter o nosso controle na página.
<%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl" TagPrefix="myctl" %>
Repare na propriedade TagPrefix, é por ela que iremos acessar os nossos controles criados no namespace que foi carregado pela propriedade Namespace, é uma atalho, para não ter que ficar digitando todo o namespace.
Dentro da tag body iremos declarar agora nosso controle MyControl, para isso temos que usar a tag “myctl”, veja a declaração:
<myctl:MyControl runat="server" ID="myControl" />
Código completo da página Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl" TagPrefix="myctl" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Acessando recursos embutidos</title> </head> <body> <form id="form1" runat="server"> <div> <myctl:MyControl runat="server" ID="myControl" /> </div> </form> </body> </html>
Ok! Vamos agora executar nossa aplicação e clicar no botão que será criado no navegador.
🙁 Puxa. Primeiro erro, “CallAlert não está definido”.
Obvio, ainda não o definimos. Vamos fazer algumas configurações para que o nosso arquivo javascript seja carregado de forma correta.
Voltaremos ao nosso projeto “EmbeddingJavascriptMyControl” selecione o arquivo “MyControl.js” botão direito, “Propriedades” ou “Properties” na janela que abriu, em “Build Action” marque “Embedded Resource”
Agora vamos executar nossa aplicação e clicar no botão novamente… e … erro de novo, o mesmo erro “CallAlert não está definido”. Isso acontece porque não basta marcar como “Embedded Resource”, ainda temos mais duas alterações a fazer para que nosso recurso funcione. Então vamos a elas.
Primeira Alteração
Abra o arquivo AssemblyInfo.cs e nele digite a seguinte linha
[assembly: System.Web.UI.WebResource("EmbeddingJavascriptMyControl.MyControl.js", "application/x-javascript")]
Esta linha dirá ao Assembly onde está o recurso embutido, mas muita atenção aqui a dois pontos:
- É case sensitive (Sensível ao Caso), diferencia MAIÚSCULAS de minúsculas;
- Onde informamos “EmbeddingJavascriptMyControl.MyControl.js” temos que prestar atenção a nossa estrutura de diretório, se nossos arquivos .js irão ficar em um diretório chamado “Resource/js/arquivo.js” temos que informar o caminho completo do arquivo, assim: “EmbeddingJavascriptMyControl.Resource.js.arquivo.js”
O arquivo AssemblyInfo.js fica em embaixo do nó “Properties” do nosso projeto “EmbeddingJavascriptMyControl”
Segunda Alteração
Voltaremos ao nosso projeto “EmbeddingJavascript”, abra o arquivo “Default.aspx”.
Antes da nossa declaração <myctl:MyControl… cole a seguinte linha
<asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="True" runat="server"> <Scripts> <asp:ScriptReference Assembly="EmbeddingJavascriptMyControl" Name="EmbeddingJavascriptMyControl.MyControl.js" /> </Scripts> </asp:ScriptManager>
O Código da página deve ter ficado parecido com este:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl" TagPrefix="myctl" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Acessando recursos embutidos</title> </head> <body> <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" EnablePartialRendering="True" runat="server"> <Scripts> <asp:ScriptReference Assembly="EmbeddingJavascriptMyControl" Name="EmbeddingJavascriptMyControl.MyControl.js" /> </Scripts> </asp:ScriptManager> <myctl:MyControl runat="server" ID="myControl" /> </div> </form> </body> </html>
Agora podemos executar nossa aplicação e clicar no botão que tudo irá funcionar.
Abra o código fonte da página, vamos ver o que foi gerado.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><title> Acessando recursos embutidos </title></head> <body> <form name="form1" method="post" action="default.aspx" id="form1"> <div> <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEyNjU1NTIyMDlkZBHjFb7Tj9qnQw+tUeeM3n2rSAIt" /> </div> <script type="text/javascript"> //<![CDATA[ var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } //]]> </script> <script src="/WebResource.axd?d=vJiRxykuUPDnEnitlfulpUyNW8r5H6f6ZTXVc0STOzayuJB5KQYvdOM6qDn1woYY3aHrZvYEH3kaqgMXLvHN0f1Rgw81&t=634245118914809245" type="text/javascript"></script> <script src="/ScriptResource.axd?d=pQSfU-Gluj5fWKvwHugAhrYCplBo6HWtAZk18MYQFen7LCnH5eACZQlY2hQFj_mG8wBOBn8gzXjwh__AP4GvZAYiRAXNVfqsrwJ_47pioSwJ0Nt50nZsPwYb_ImjAVVyDYqnF5Mrxws1Hb-eiA7hx6mpB7JzD1kjO5Z0nisDG9Htu64q0&t=fffffffff9d85fa6" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ if (typeof(Sys) === 'undefined') throw new Error('A estrutura do cliente do ASP.NET Ajax falhou ao ser carregada.'); //]]> </script> <script src="/ScriptResource.axd?d=NjPhQE1Bg_p8r6kqRbkzTPoHZEUVZljs8n4CqKl-4K_SKhwe4jodATh4CnF0I6areXjIfo1RXiLf4npDOWzM3aFzuXVJ8J5aUvDNXTQM0aIaoAgK-1desXxMafETtHLNsh1PEt5-FelvmW-JH-0-0vJV7Y2ziFETXay4wFRNjRdh_c1F0&t=fffffffff9d85fa6" type="text/javascript"></script> <script src="/ScriptResource.axd?d=BZRoaxvjoo_kLM6d5_RbZyrPibF04d5B4H_E9ve7x6R9w7j4h8eSLh1dELDU0HnMVzRTtC11C31fvdqZm20-B7I3y0joB9f7DDGCZbLa5ydhKnkJqYlCPlFvCw83WLuQCOYPbAJ9hEEPRfOGyysCkjJ9Pal1xeF5TbJH3LC4OYpMqqSQ0&t=ffffffffb915d82c" type="text/javascript"></script> <div> <script type="text/javascript"> //<![CDATA[ Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1')); Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90); //]]> </script> <span id="myControl">Digite algo: <input type='text' id='txtAlgo' /> <input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' /></span> </div> <script type="text/javascript"> //<![CDATA[ Sys.Application.initialize(); //]]> </script> </form> </body> </html>
Podemos reparar que desta forma funciona, mas, não creio que atenda a nossa necessidade, alem de que é gerada muita coisa desnecessária em nossa página, vamos fazer uma análise de tudo que aconteceu na nossa página.
Veja quantas linhas de código foram geradas apenas para mostrar uma mensagem de alerta.
<script src="/WebResource.axd?d=vJiRxykuUPDnEnitlfulpUyNW8r5H6f6ZTXVc0STOzayuJB5KQYvdOM6qDn1woYY3aHrZvYEH3kaqgMXLvHN0f1Rgw81&t=634245118914809245" type="text/javascript"></script> <script src="/ScriptResource.axd?d=pQSfU-Gluj5fWKvwHugAhrYCplBo6HWtAZk18MYQFen7LCnH5eACZQlY2hQFj_mG8wBOBn8gzXjwh__AP4GvZAYiRAXNVfqsrwJ_47pioSwJ0Nt50nZsPwYb_ImjAVVyDYqnF5Mrxws1Hb-eiA7hx6mpB7JzD1kjO5Z0nisDG9Htu64q0&t=fffffffff9d85fa6" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ if (typeof(Sys) === 'undefined') throw new Error('A estrutura do cliente do ASP.NET Ajax falhou ao ser carregada.'); //]]> </script> <script src="/ScriptResource.axd?d=NjPhQE1Bg_p8r6kqRbkzTPoHZEUVZljs8n4CqKl-4K_SKhwe4jodATh4CnF0I6areXjIfo1RXiLf4npDOWzM3aFzuXVJ8J5aUvDNXTQM0aIaoAgK-1desXxMafETtHLNsh1PEt5-FelvmW-JH-0-0vJV7Y2ziFETXay4wFRNjRdh_c1F0&t=fffffffff9d85fa6" type="text/javascript"></script> <script src="/ScriptResource.axd?d=BZRoaxvjoo_kLM6d5_RbZyrPibF04d5B4H_E9ve7x6R9w7j4h8eSLh1dELDU0HnMVzRTtC11C31fvdqZm20-B7I3y0joB9f7DDGCZbLa5ydhKnkJqYlCPlFvCw83WLuQCOYPbAJ9hEEPRfOGyysCkjJ9Pal1xeF5TbJH3LC4OYpMqqSQ0&t=ffffffffb915d82c" type="text/javascript"></script> <div> <script type="text/javascript"> //<![CDATA[ Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1')); Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90); //]]> </script>
Imagine se tivéssemos muitos controles carregados na página, o tanto de código que seria carregado.
E se precisássemos chamar algum método antes da tag body para que nosso controle funcione perfeitamente?
Outro problema, é que o utilizador de nossos componentes teria que saber a estrutura de nossos recursos e teria que se lembrar de ficar colocando o Script Manager e configurá-lo de forma adequada em todas as páginas, caso contrário daria erros. Isso não é legal.
Mas como eu prometi, vamos resolver todos estes problemas com o uso de Handlers e Modules.
Então, vamos a segunda parte do artigo.
Usando Handlers e Modules com recursos embutidos
Antes de iniciar vamos descobrir para que servem e o que são os handlers e modules.
O que são handlers (manipuladores)?
Os handlers, ou manipuladores, normalmente processam as requisições e não têm interatividade com o usuário, como páginas ou controles, e normalmente são responsáveis por camadas lógicas dentro da aplicação.
Podemos utilizar um handler quando precisamos executar algo no servidor sem a necessidade de intervenção do usuário final.
Os handlers implementam a interface IHttpHandler, o HttpHandler é responsável por atender as requisições do browser e a interface IHttpHandler possui as assinaturas necessárias para que estas requisições sejam atendidas.
O que são modules (módulos)?
Os módulos HTTP são executados antes e depois do handler (manipulador) e nos fornece métodos para interagir com a requisição (HttpRequest). Os módulos devem implementar a interface System.Web.IHttpModule.
São normalmente sincronizados com os eventos da classe System.Web.IHttpModule (implementado dentro do Global.asax.cs ou. vb).
A seguir uma lista de eventos que devem ser considerados na implementação do módulo:
- BeginRequest
- AuthenticateRequest
- AuthorizeRequest
- ResolveRequestCache
- AcquireRequestState
- PreRequestHandlerExecute
- PostRequestHandlerExecute
- ReleaseRequestState
- UpdateRequestCache
- EndRequest
- * PreSendRequestHeaders
- * PreSendRequestContent
- * error
Os eventos identificados por um asterisco (*) pode ocorrer a qualquer momento dentro da requisição, todos os outros estão listados em sua ordem de chamada.
Agora que sabemos com o que iremos trabalhar, vamos dar uma limpada na nossa página “Default.aspx” remova a tag do Script Manager, não iremos usar mais ela. O código deverá ter ficado assim:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EmbeddingJavascript._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ Register Assembly="EmbeddingJavascriptMyControl" Namespace="EmbeddingJavascriptMyControl" TagPrefix="myctl" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Acessando recursos embutidos</title> </head> <body> <form id="form1" runat="server"> <div> <myctl:MyControl runat="server" ID="myControl" /> </div> </form> </body> </html>
Voltaremos ao nosso projeto “EmbeddingJavascriptMyControl” , adicionaremos uma nova classe ao nosso projeto, a minha eu chamei de “ResourceHandler.cs”
Segue o código completo da classe:
#region Using using System; using System.IO; using System.Web; using System.Web.UI; using System.Text; using System.Text.RegularExpressions; using System.IO.Compression; using System.Web.Caching; using System.Net; using System.Collections.Generic; using System.Reflection; #endregion namespace EmbeddingJavascriptMyControl { public class ResourceHandler : IHttpHandler { private const int DAYS_IN_CACHE = 30; public void ProcessRequest(HttpContext context) { string path = context.Request.QueryString["r"]; path = "EmbeddingJavascriptMyControl." + path + ".js"; string content = string.Empty; if (!string.IsNullOrEmpty(path)) { if (context.Cache[path] == null) { string[] scripts = path.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (string script in scripts) { content += RetrieveScript(script) + Environment.NewLine; } content = StripWhitespace(content); context.Cache.Insert(path, content, null, Cache.NoAbsoluteExpiration, new TimeSpan(DAYS_IN_CACHE, 0, 0, 0)); } } content = (string)context.Cache[path]; if (!string.IsNullOrEmpty(content)) { context.Response.Write(content); SetHeaders(content.GetHashCode(), context); Compress(context); } } /// <summary> /// carrega o script em memória e depois devolve ao objeto chamador /// </summary> /// <param name="resourceFullName">caminho completo do recurso</param> private static string RetrieveScript(string resourceFullName) { string script = null; //get from resource Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFullName); if (stream != null) { using (StreamReader sr = new StreamReader(stream)) { script = sr.ReadToEnd(); sr.Close(); } } return script; } /// <summary> /// limpa o arquivo javascrip e retorna /// </summary> /// <param name="body">script</param> private static string StripWhitespace(string body) { /* * aqui eu retorno o o script diretamente * facilita para a depuração. * mas você pode tratar, se estiver em modo debug, não limpa * compilou, limpa */ //remove newLine e comentários com // string[] lines = body.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); StringBuilder emptyLines = new StringBuilder(); foreach (string line in lines) { string s = line.Trim(); if (s.Length > 0 && !s.StartsWith("//")) { if (s.Contains("//")) s = s.Substring(0, s.IndexOf("//")); emptyLines.AppendLine(s.Trim()); } } body = emptyLines.ToString(); // remove C styles comments body = Regex.Replace(body, "/\\*.*?\\*/", String.Empty, RegexOptions.Compiled | RegexOptions.Singleline); //// trim left body = Regex.Replace(body, "^\\s*", String.Empty, RegexOptions.Compiled | RegexOptions.Multiline); //// trim right body = Regex.Replace(body, "\\s*[\\r\\n]", "\r\n", RegexOptions.Compiled | RegexOptions.ECMAScript); // remove whitespace beside of left curly braced body = Regex.Replace(body, "\\s*{\\s*", "{", RegexOptions.Compiled | RegexOptions.ECMAScript); // remove whitespace beside of coma body = Regex.Replace(body, "\\s*,\\s*", ",", RegexOptions.Compiled | RegexOptions.ECMAScript); // remove whitespace beside of semicolon body = Regex.Replace(body, "\\s*;\\s*", ";", RegexOptions.Compiled | RegexOptions.ECMAScript); // remove newline after keywords //body = Regex.Replace(body, "\\r\\n(?<=\\b(abstract|boolean|break|byte|case|catch|char|class|const|continue|default|delete|do|double|else|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|while|with)\\r\\n)", " ", RegexOptions.Compiled | RegexOptions.ECMAScript); return body; } /// <summary> /// Isso fará com que o navegador e o servidor mantenham a saída /// em cache melhorando o desempenho. /// </summary> private static void SetHeaders(int hash, HttpContext context) { HttpCachePolicy cache = context.Response.Cache; DateTime modifiedDate = new DateTime(GetAssemblyTime(Assembly.GetExecutingAssembly())).ToUniversalTime(); DateTime nowDate = DateTime.Now.ToUniversalTime().AddSeconds(-1); if (modifiedDate > nowDate) { modifiedDate = nowDate; } cache.SetLastModified(modifiedDate); cache.SetOmitVaryStar(true); cache.SetVaryByCustom("r"); cache.SetExpires(DateTime.UtcNow.AddDays(365)); cache.SetValidUntilExpires(true); cache.SetRevalidation(HttpCacheRevalidation.AllCaches); cache.SetCacheability(HttpCacheability.Public); cache.SetLastModifiedFromFileDependencies(); cache.SetMaxAge(new TimeSpan(DAYS_IN_CACHE, 0, 0, 0)); cache.SetETag("\"" + hash.ToString() + "\""); context.Response.ContentType = "text/javascript"; context.Response.Cache.VaryByHeaders["Accept-Encoding"] = true; } private static long GetAssemblyTime(Assembly assembly) { AssemblyName assemblyName = assembly.GetName(); return File.GetLastWriteTime(new Uri(assemblyName.CodeBase).LocalPath).Ticks; } #region Compression private const string GZIP = "gzip"; private const string DEFLATE = "deflate"; private static void Compress(HttpContext context) { if (context.Request.UserAgent != null && context.Request.UserAgent.Contains("MSIE 6")) return; if (IsEncodingAccepted(GZIP)) { context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress); SetEncoding(GZIP); } else if (IsEncodingAccepted(DEFLATE)) { context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress); SetEncoding(DEFLATE); } } /// <summary> /// Verifica os cabeçalhos da solicitação para ver se a /// codificação é aceita pelo cliente. /// </summary> private static bool IsEncodingAccepted(string encoding) { return HttpContext.Current.Request.Headers["Accept-encoding"] != null && HttpContext.Current.Request.Headers["Accept-encoding"].Contains(encoding); } /// <summary> /// Adiciona a codificação ao cabeçalho de resposta /// </summary> /// <param name="encoding"></param> private static void SetEncoding(string encoding) { HttpContext.Current.Response.AppendHeader("Content-encoding", encoding); } #endregion public bool IsReusable { get { return false; } } } public class ResourceModule : IHttpModule { #region IHttpModule Members void IHttpModule.Dispose() { } void IHttpModule.Init(HttpApplication context) { context.PostRequestHandlerExecute += new EventHandler(context_BeginRequest); } #endregion void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; if (app.Context.CurrentHandler is Page && !app.Request.RawUrl.Contains("serviceframe")) { if (!app.Context.Request.Url.Scheme.Contains("https")) { app.Response.Filter = new WebResourceFilter(app.Response.Filter); } } } #region Stream filter private class WebResourceFilter : Stream { public WebResourceFilter(Stream sink) { _sink = sink; } private Stream _sink; #region Properites public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return true; } } public override void Flush() { _sink.Flush(); } public override long Length { get { return 0; } } private long _position; public override long Position { get { return _position; } set { _position = value; } } #endregion #region Methods public override int Read(byte[] buffer, int offset, int count) { return _sink.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return _sink.Seek(offset, origin); } public override void SetLength(long value) { _sink.SetLength(value); } public override void Close() { _sink.Close(); } public override void Write(byte[] buffer, int offset, int count) { byte[] data = new byte[count]; Buffer.BlockCopy(buffer, offset, data, 0, count); string html = System.Text.Encoding.Default.GetString(buffer); int index = 0; List<string> list = new List<string>(); Regex regex = new Regex("<script\\s*src=\"((?=[^\"]*(webresource.axd|scriptresource.axd))[^\"]*)\"\\s*type=\"text/javascript\"[^>]*>[^<]*(?:</script>)?", RegexOptions.IgnoreCase); foreach (Match match in regex.Matches(html)) { if (index == 0) index = html.IndexOf(match.Value); string relative = match.Groups[1].Value; list.Add(relative); html = html.Replace(match.Value, string.Empty); } if (index > 0) { string script = "<script type=\"text/javascript\" src=\"js.axd?path={0}\"></script>"; string path = string.Empty; foreach (string s in list) { path += HttpUtility.UrlEncode(s) + ","; } html = html.Insert(index, string.Format(script, path)); } byte[] outdata = System.Text.Encoding.Default.GetBytes(html); _sink.Write(outdata, 0, outdata.GetLength(0)); } #endregion } #endregion } }
Adicione outra classe ao seu projeto, a minha eu chamei de “JavascriptManager” será responsável por adicionar a tag script nas páginas que usam o nosso controle.
Código da classe:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.UI.HtmlControls; using System.Web.UI; namespace EmbeddingJavascriptMyControl { public static class JavascriptManager { /// <summary> /// Use este método para incluir um arquivo javascript na página /// </summary> /// <param name="_page">página que o arquivo deverá ser incluído</param> /// <param name="resourceName">caminho script</param> public static void IncludeResource(Page _page, string resourceName) { //aqui eu usei o nome EmbeddingJavascriptMyControl, mas você poderá usar o nome que quiser resourceName = "/EmbeddingJavascriptMyControl.axd?r=" + resourceName; HtmlGenericControl script = new HtmlGenericControl("script"); script.Attributes.Add("type", "text/javascript"); script.Attributes.Add("src", _page.ResolveClientUrl(resourceName)); _page.Header.Controls.Add(script); } } }
Ufa! Já estamos quase lá. O bom disso é que só iremos ter o trabalho uma vez, e poderemos usar em todo o nosso projeto de componentes, podemos ter um ou cem componentes, sempre serão iguais e usarão os mesmos métodos.
Agora precisamos ajustar o nosso MyControl.cs para que ele faça a referência ao script que o controla.
Para isso, iremos sobrescrever o evento “OnInit”.
protected override void OnInit(EventArgs e) { base.OnInit(e); JavascriptManager.IncludeResource(Page,"MyControl"); }
Você devará ter isso em todos os controles que você criar, eu tenho por hábito criar os controles e com os mesmos nomes dos arquivos.js, como eu fiz com o MyControl.cs e MyControl.js, assim eu sei para que uso cada arquivo js, mesmo que fiquem em diretórios separados.
Feito isso, já podemos executar a nossa aplicação. Abra o código fonte da página e veja como ficou mais limpo. Economizamos algumas linhas e memória no navegador do cliente.
Percebam também que o nosso script foi registrado na tag head. Isso nos ajudará muito quando fizermos controles mais complexos e dinâmicos.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><title> Acessando recursos embutidos </title><script type="text/javascript" src="/EmbeddingJavascriptMyControl.axd?r=MyControl"></script></head> <body> <form name="form1" method="post" action="default.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEzNjgwMjk4NDBkZMpc3l1d4uAY38/msqun4petNKEJ" /> </div> <div> <span id="myControl">Digite algo: <input type='text' id='txtAlgo' /> <input type='button' value='Clique aqui' onclick='CallAlert(txtAlgo.value);' /></span> </div> </form> </body> </html>
Certo. Mas se você clicar no botão … dará o mesmo erro de sempre. “CallAlert não está definido”. Isso irá acontecer porque o nosso script é referenciado pela nossa url
<script type=”text/javascript” src=”/EmbeddingJavascriptMyControl.axd?r=MyControl“></script>
Veja que definimos um url diferente para o src de nosso script, é aqui que iremos usar o nosso handler e o module que criamos. É ele, o handler, quem será responsável por tratar o script e carregar no navegador, mas precisamente no cache do navegador.
Então vamos lá colocar o handler e o módulo para funcionar.
Voltamos ao nosso projeto “EmbeddingJavascript” abra o arquivo “Web.config” nele precisamos configurar os nossos handlers e modules
Localize dentro do Web.config a chave <httpHandlers> antes da tag de fechamento iremos colocar o nosso handler
A minha ficou assim:
<httpHandlers> <remove verb="*" path="*.asmx"/> <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add path="*/EmbeddingJavascriptMyControl.axd" verb="*" type ="EmbeddingJavascriptMyControl.ResourceHandler"/> </httpHandlers>
Lembra do src do javascript? “/EmbeddingJavascriptMyControl.axd” é aqui que informamos quem será o manipulador do nosso handler.
Agora segue a declaração do módulo dentro do arquivo web.config. Declare entre as tags <httpModules> </httpModules>
<httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="EmbeddingJavascriptMyControlResourceModule" type="EmbeddingJavascriptMyControl.ResourceModule"/> </httpModules>
Pronto. Agora pode executar a aplicação e clicar no botão. Se fez tudo correto. Vai funcionar. 🙂
Esta dica não serve apenas para javascript, com alguns ajustes no código do “ResourceHandler” você poderá usar para tratar imagens, css, e outros arquivos que forem necessários ao funcionamento do seu componente, mas nunca se esqueça de marcar como “Embedded Resource”.
A alteração que fizemos no web.config também podemos automatizá-la. Mas isso fica para um próximo artigo. Este já está muito extenso.
Segue o fonte do exemplo que eu usei no artigo [download id=”20″]
É isso ai pessoal 🙂
Até o próximo
? Marcelo