ASP .NET - Usando um manipulador (Handle) ASHX


Atualmente uma aplicação web não trata apenas de servir HTML para os navegadores, mas também XML e outros tipos de conteúdos; Um feed RSS é um exemplo muito usado pelas páginas atualmente.

Em ocasiões onde suas páginas desejam retornar uma imagem, uma string ou outras páginas não HTML a utilização de um manipulador ASHX pode ser uma solução que lhe dará um ganho de desempenho.Como exemplo cenários onde de utilização de um manipulador ASHX pode ser conveniente citamos:

  • Dinâmica de geração de imagem: Você pode escrever manipuladores que retornam dados orientado a imagens, criando um manipulador de ashx que retorna dados de imagem e, em seguida, usando essa URL em suas tags. por exemplo, <img alt="ícone customizado" src="Icon.ashx?usuario=bob"></img>
  • Retornando XML baseados em REST e JSON de código AJAX no cliente;
  • HTML personalizado: Retornando HTML personalizado de uma página quando o ASP.NET Web Forms ou MVC apresenta muitas restrições;
  • Neste artigo eu vou mostrar como podemos usar um manipulador ASHX em um desses cenários. Para isso eu vou usar o Visual Web Developer 2010 Express Edition.

    O nosso objetivo será criar uma página para retornar um conteúdo XML ou uma imagem para o visitante. Um objetivo bem simples...

    Uma solução trivial para retornar um conteúdo XML seria criar uma página ASPX e no evento Load da página usar o método Response.Write para retornar o XML.

    O código abaixo mostra esta solução:

    Public Class RSS
    Inherits System.Web.UI.Page

    Private Sub Page_Load(sender As Object, e As System.EventArgs)
    Response.ContentType = "text/xml"
    Response.ContentEncoding = Encoding.UTF8

    Dim sXml As String = CriaXMLString()
    Response.Write(sXml)
    Response.[End]()
    End Sub
    End Class

    public class RSS : System.Web.UI.Page
    {

       private void Page_Load(object sender, System.EventArgs e)
       {
          Response.ContentType = "text/xml";
          Response.ContentEncoding = Encoding.UTF8;

          string sXml = BuildXMLString();
          Response.Write( sXml );
          Response.End();
       }
    }

    A solução acima vai funcionar e mas não haveria uma solução melhor ?

    O problema é que a página acima herda de System.Web.UI.Page e que o manipuladora da página esta preparado para servir páginas aspx (WebForms) e dessa forma inclui uma sobrecarga de recursos não necessários apenas para servir uma simples arquivo XML.

    Uma solução alternativa é criar um manipulador ASHX que realiza o mapeamento para o engine ASP .NET e permite realizar a tarefa de forma simples e usando poucos recursos.

    Para criar um manipulador ASHX em uma aplicação ASP .NET é muito simples.

    Abra o Visual Web Developer 2010 Express e no menu File selecione New Web Site usando o template ASP .NET Web Site e selecione a linguagem Visual Basic ou C# informando o nome DemoAshx ;

    Com o projeto aberto no Visual Developer 2010 Express Edition clique no menu Project e selecione Add New Item;

    A seguir selecione o template Generic Handler e informe o nome HandlerDemo.ashx selecionando a linguagem Visual Basic ou C# (fica a seu critério);

    Feito isso será gerado o arquivo HandlerDemo.ashx com o código gerado automaticamente onde podemos notar o seguinte:

    Você não deve modificar a herança nem remover os membros gerados mas alterá-los implementando o código desejado a realizar a sua tarefa conforme veremos logo a seguir;

    Obs: É recomendado mapear uma URL antiga ou caminho para o novo arquivo ASHX. Por questão de compatibilidade e por motivo de otimização você pode desejar que o novo manipulador aponte para uma URL antiga no seu site. Para fazer isso você pode usar o mapeamento no arquivo Web.Config conforme abaixo:

    <system.web>
        <urlMappings enabled="true">
            <add url="~/Default.aspx" mappedUrl="~/Handler.ashx"/>
        </urlMappings>
        .....

    Para resolver um dos nossos problema, que é gerar conteúdo XML vamos alterar a classe gerada conforme o código abaixo:

    Public Class HandlerDemo
    Implements IHttpHandler

    Public Sub ProcessRequest(context As HttpContext)  Implements System.Web.IHttpHandler.ProcessRequest
    context.Response.ContentType = "text/xml"
    context.Response.ContentEncoding = System.Text.Encoding.UTF8

    Dim sXml As String = criarXMLString()
    context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(600))
    context.Response.Cache.SetCacheability(HttpCacheability.[Public])
    context.Response.Write(sXml)
    End Sub

    Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
    Get
    Return True
    End Get
    End Property
    End Class
    public class RSSHandler : IHttpHandler
    {
    public void ProcessRequest (HttpContext context)
    {
       context.Response.ContentType = "text/xml";
       context.Response.ContentEncoding = System.Text.Encoding.UTF8;

       string sXml = criarXMLString();

       context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(600));
       context.Response.Cache.SetCacheability(HttpCacheability.Public);
       context.Response.Write( sXml );
    }

    public bool IsReusable
    {
          get { return true; }
     }
    }

    Para resolver o problema de servir uma imagem ao visitante a classe poderia ser alterada para :

    Public Class Handler
    Implements IHttpHandler

    Public Sub ProcessRequest(context As HttpContext)  Implements System.Web.IHttpHandler.ProcessRequest           
    context.Response.ContentType = "image/jpg"
    context.Response.WriteFile("~/Deserto.jpg")
    End Sub

    Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
    Get
    Return False
    End Get
    End Property
    End Class

    public class Handler : IHttpHandler {

    public void ProcessRequest (HttpContext context)
    {

       context.Response.ContentType = "image/jpg";
        context.Response.WriteFile("~/Deserto.jpg");
    }

    public bool IsReusable {
       get {
                return false;
            }
    }

    Como eu estou com preguiça de criar a rotina criarXMLString() para gerar o conteúdo XML vou testar somente o segundo caso que é a exibição da imagem.

    Executando o web site criado iremos obter:

    Podemos incluir mais funcionalidade ao nosso exemplo da imagem, pois tudo o que ele faz e permitir obter uma imagem através de um manipulador ASHX.

    Podemos permitir que a imagem seja requisitada através de uma coleção QueryString e você pode usar Request.QueryString no manipulador da mesma maneira que usa em suas páginas ASPX.

    Vamos então modificar o código para permitir este recurso:

    Public Class Handler
    Implements IHttpHandler

    Public Sub ProcessRequest(context As HttpContext) 
    Implements System.Web.IHttpHandler.ProcessRequest

    Dim r As HttpResponse = context.Response
    r.ContentType = "image/jpg"

    Dim file As String = context.Request.QueryString("arq")
    If file = "jardim" Then
    r.WriteFile("Jardim.jpg")
    Else
    r.WriteFile("Deserto.jpg")
    End If
    End Sub

    Public ReadOnly Property IsReusable() As Boolean 
    Implements System.Web.IHttpHandler.IsReusable
    Get
    Return False
    End Get
    End Property
    End Class
    ppublic class Handler : IHttpHandler {

    public void ProcessRequest (HttpContext context) {

    HttpResponse r = context.Response;
    r.ContentType = "image/jpg";

    string file = context.Request.QueryString["arq"];
    if (file == "jardim")
    {
    r.WriteFile("Jardim.jpg");
    }
    else
    {
    r.WriteFile("Deserto.jpg");
    }
    }

    public bool IsReusable {
       get {
              return false;
              }
    }
    }

    O código acima recebe uma requisição e então retorna um arquivo diferente baseado no valor da QueryString.A string retornada pode ser :

     URL: http://localhost:59789/DemoAshx/HandlerDemo.ashx?arq=jardim
    Arquivo query string: jardim
    arquivo exibido: Jardim.jpg
    
    ou
    
    URL: http://localhost:59789/DemoAshx/HandlerDemo.ashx
    Arquivo query string: ""
    arquivo exibido: Deserto.png

    O resultado exibido para a url : http://localhost:59789/DemoAshx/HandlerDemo.ashx?arq=jardim é mostrado a seguir:

    Lembrando que embora o resultado ao usar um manipulador ASHX é o mesmo que uma página ASPX o desempenho por conta do uso dos primeiros é da ordem de 10%.

    Então quando usar um manipulador ASHX ou um página ASPX:

    Use Web Forms quando tiver que expor :

    Use um Manipulador ASHX quando tiver que expor os seguintes conteúdos:

    • Somente páginas HTML;
    • Páginas dinâmicas simples;
    • Controles ASP .NET customizados                
    • Arquivos binários
    • Arquivos XML
    • Imagens

    E estamos conversados...

    Pegue o projeto completo aqui: DemoAshx.zip

    Eu sei é apenas ASP .NET, mas eu gosto...

    Referências:


    José Carlos Macoratti