Integrando Windows Live ID ao ASP.NET

Como todos sabem, o Windows Live ID é a evolução do Passport. Antigamente, para integrar uma aplicação ASP.NET ao sistema de autenticação do Passport, além de uma SDK que voce precisa entender e customizar, havia custos envolvidos, o que fez com que a terceira forma de autenticação suportada pelo ASP.NET não vingasse.

Atualmente, o Windows Live ID torna-se muito menos complexo e mais simples de acoplar à aplicações ASP.NET e, além disso, não é mais necessário pagar para utilizá-lo. Tudo o que precisa ser feito é um cadastro prévio, que consiste nas informações a respeito da aplicação que fará o uso do Windows Live ID. Para um passo à passo de como configurá-lo, podemos seguir o um artigo que neste endereço.

Além disso, ainda temos (ainda em CTP) o Windows Live Tools, que adiciona alguns controles na barra de ferramentas do Visual Studio .NET. Este CTP contempla, entre vários controles, os controles IDLoginView, IDLoginStatus e a classe LiveMembershipProvider. Esta última, herda diretamente da classe SqlMembershipProvider e traz a possibilidade de integrar uma credencial Live a um usuário dentro da estrutura de segurança do ASP.NET.

Novo comportamento do EventValidation

Há algum tempo eu comentei a respeito de alguns cuidados que precisamos ter com o EventValidation. Um outro cenário em que este mesmo erro está propício a acontecer, é quando estamos dentro de uma página muito complexa ou com muitas informações e que a sua renderização demora para acontecer por inteira.

O EventValidation se baseia em um campo oculto chamado __EVENTVALIDATION para validar o controle que gerou o postback. O próprio runtime do ASP.NET embuti este controle no final da página (pouco antes da tag de fechamento do formulário (</form>)), durante o processo de renderização. O grande problema que temos aqui é que as vezes, a renderização pode ocorrer parcialmente e, caso um controle que cause postback apareça para o usuário e ele clicar, um postback será efetuado sem o envio do campo oculto __EVENTVALIDATION, pois ele ainda não foi renderizado.

O Service Pack 1 do .NET Framework 3.5 resolve isso, ou seja, agora a renderização deste campo (e de todos os outros campos ocultos “auto-gerados”), por padrão, passa a ser efetuada no ínicio do formulário, permitindo assim efetuar postbacks sem esperar a página renderizar por inteira.

E, para finalizar, é importante dizer que voce pode controlar isso através do atributo renderAllHiddenFieldsAtTopOfForm do elemento pages, no arquivo Web.config:

<pages renderAllHiddenFieldsAtTopOfForm=”true|false” />

RedirectMode

O Service Pack 1 do .NET Framework 3.5 incluiu um novo recurso, onde voce poderá definir qual será a forma de redirecionamento quando estiver com o customErrors habilitado. Agora temos um atributo chamado RedirectMode, onde voce poderá definir dois valores:

  • ResponseRedirect: exibe a página de erro mundando a Url original da página que foi solicitada.
  • ResponseRewrite: exibe a página de erro sem mudar a Url da página original que foi solicitada.

Abaixo um simples exemplo:

<customErrors mode=”On” redirectMode=”ResponseRewrite”>
    <error statusCode=”500″ redirect=”Erro.aspx”/>
</customErrors>

Async Handler

Desde a versão 1.x do ASP.NET podemos facilmente criar um handler para permitir a execução de algum código. O idéia por detrás deste handler, é justamente servir como alvo para alguma requisição. Um exemplo muito típico é a extração de arquivos do banco de dados, arquivos do disco, geração automática de imagens, etc..

Havia alguns detalhes necessários na versão 1.x que, se não realizados, não funcionava adequadamente. A partir da versão 2.0, a Microsoft incluiu um novo tipo de arquivo, com extensão ASHX, chamado de Generic Handler. Esse arquivo traz uma classe que, por padrão, implementa a interface IHttpHandler, cabendo aos desenvolvedores implementar o método ProcessRequest para tratar o pedido.

O problema é que o processamento deste handler estará ocupando uma thread do ThreadPool que, por sua vez, serve as requisições para as páginas da aplicação. Caso o código a ser realizado dentro deste handler for um processo custoso, podemos comprometer as outras requisições que nada tem a ver com ele (este artigo explica mais detalhes sobre este comportamento).

Para permitir que o handler também faça o uso do processamento assíncrono, podemos fazer com que o mesmo implemente a interface IHttpAsyncHandler e, ao invés de implementar o método ProcessRequest, devemos implementar os métodos BeginProcessRequest e EndProcessRequest. O exemplo abaixo utiliza os métodos (BeginRead e EndRead) para leitura assíncrona de um arquivo do disco:

public class ImageAsyncHandler : IHttpAsyncHandler
{
    private struct InnerState : IDisposable
    {
        public HttpContext HttpContext;
        public FileStream Stream;

        public void Dispose()
        {
            if (this.Stream != null)
                this.Stream.Dispose();
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

    public IAsyncResult BeginProcessRequest(HttpContext context,
        AsyncCallback cb, object extraData)
    {
        string fileName =
            context.Server.MapPath(context.Request.QueryString[“FileName”]);

        FileStream fs = new FileStream(fileName, FileMode.Open);
        int length = (int)fs.Length;

        return fs.BeginRead(new byte[length], 0, length, cb,
            new InnerState()
            {
                HttpContext = context
                , Stream = fs
            });
    }

    public void ProcessRequest(HttpContext context) { }

    public void EndProcessRequest(IAsyncResult result)
    {
        using (InnerState state = (InnerState)result.AsyncState)
        {
            int length = state.Stream.EndRead(result);
            state.Stream.Position = 0;
           
            byte[] temp = new byte[length];
            state.Stream.Read(temp, 0, length);

            state.HttpContext.Response.OutputStream.Write(temp, 0, length);
        }
    }
}

Esse processo não mudará nada o resultado final do handler, mas trabalhará de uma forma muito mais otimizada, garantido que threads criadas para servir a aplicação não fiquem ocupadas executando um processamento mais custoso. Caso a imagem ou os dados venham de um banco de dados, podemos combinar essa técnica com os métodos assíncronos do ADO.NET 2.0.

Pré-Compilando Web Application Projects

Uma das grandes vantagens que o ASP.NET trouxe em relação ao ASP Clássico é que as páginas não são mais interpretadas, mas sim compiladas. Quando compiladas, não me refiro apenas ao código VB.NET/C# que escrevemos no CodeBehind, mas sim a aplicação como um todo, inclusive (e principalmente) os markups (conteúdo HTML da página ASPX).

Quando utilizamos projetos do tipo Web Application Project, há uma característica do ASP.NET que é um retardo no processamento da primeira requisição. Isso se dá pelo fato de que o ASP.NET precisa fazer o parser das páginas ASPX e transformá-las em código MSIL e, se irmos mais a fundo, notaremos que essa versão compilada estará armazenada dentro do diretório Temporary ASP.NET Files, que é criado dentro em %windir%Microsoft.NETFrameworkVersao quando instalamos o .NET Framework na máquina.

Podemos apagar todo o conteúdo deste diretório para um exemplo. Ao fazer o deploy de uma aplicação baseada em uma template do tipo Web Application Project, nada terá dentro do diretório Temporary ASP.NET Files até a primeira requisição para essa aplicação. Assim que, qualquer página for chamada, já iremos notar uma pasta com o mesmo nome do diretório virtual criada por esse processo. Isso é mostrado a partir da imagem abaixo:

Para o exemplo, temos uma página simples com um TextBox, um Label e um Button. Ao clicar no botão, o conteúdo digitado no TextBox (propriedade Text) é atributo a propriedade Text do Label.

Se abrirmos a DLL no Reflector, podemos comprovar a compilação em código MSIL, pois todo o markup foi totalmente compilado. Na verdade teremos dois Assemblies: um contendo classes (herdando diretamente da classe Page) que representam as páginas ASPX e, dentro delas, o código que colocamos no CodeBehind juntamente com a declaração dos controles; já dentro do segundo Assembly, teremos o código responsável pelo criação e montagem da hierarquia de controles da página e, por sua vez, essa classe herdará da sua correspondente que estará definida no Assembly anterior. A imagem abaixo ilustra esse processo:

O método FrameworkInitialize é chamado pelo próprio runtime do ASP.NET e, dentro dele, há a chamada para o método BuildControlTree que tem o importante papel de criar a hierarquia de controles dentro da respectiva página.

Os projetos do tipo Web Site já possuem (maiores informações aqui), ao fazer a publicação do mesmo, uma possibilidade para efetuar a pré-compilação. Até mesmo em tempo de desenvolvimento, esses tipos de projetos são pré-compilados. Já para os projetos do tipo Web Application Project, voce pode também adiantar esse processo, evitando que o primeiro usuário pague o preço pela demora. Para isso, basta voce recorrer ao utilitário chamado aspnet_compiler.exe que, em uma de suas opções, voce poderá especificar um diretório virtual, para que o mesmo crie a versão compilado do mesmo. A sintaxe para isso (utilizando a partir do prompt do Visual Studio .NET) é:

C:>aspnet_compiler -v /WebAppTest

Profile e ASP.NET Web Application

O Profile é uma das novas funcionalidades que foram criadas a partir do ASP.NET 2.0. A sua utilidade, todos conhecem e utilizam normalmente em qualquer projeto do tipo Web Site.

Com a vinda do Web Application Project, um dos problemas já conhecidos é com relação exatamente à compilação das propriedades do Profile que especificamos no arquivo Web.Config, criando um wrapper para o acesso tipado. Esses tipos de projetos não são capazes de identificar e compilar tais propriedades, obrigando ao “acesso manual”.

Se quiser evitar o trabalho manual para estes casos, voce pode recorrer ao Web Profile Generator ou ao Web Profile Builder. A diferença é que o segundo também trabalha com o Visual Studio 2008. Ambos, depois de instalados, basta seguir os passos que estão no arquivo ReadMe.txt para gerar uma classe tipada, refletindo as propriedades criadas no arquivo Web.Config.

TransferRequest

Há algum tempo atrás eu mencionei aqui um grande problema que existe quando utilizamos o método Transfer da classe HttpServerUtility, principalmente quando falamos de redirecionamento para páginas restritas. Isso ocorre porque este método não executa o processo total dentro do ASP.NET pipeline. Esse foi um dos motivos que levou o Paulo Morgado a criar os Page Modules.

Para sanar este problema, a versão 3.5 do ASP.NET incluiu um novo método na classe HttpServerUtitlity chamado TransferRequest. A sua utilização é identica ao método Transfer, só que possui um comportamento totalmente diferente, ou seja, permite que a requisição para o recurso solicitado seja executado passando por todo o pipeline do ASP.NET e, sendo assim, o processo de autorização será novamente executado. Além disso, é necessário que a aplicação corra no IIS 7.0 pois, do contrário, uma exceção será atirada.

Só que para aqueles que querem utilizar esse recurso e fazem o uso de variáveis de sessão, é importante ler a nota que o Luis Abreu diz aqui.