ASP.NET Internals

O ASP.NET introduziu uma nova forma de desenvolver aplicações Web, onde temos uma produtividade muito maior em relação ao ASP Clássico e, além disso, uma base sólida e comum de tipos que nos permite desenvolver uma aplicação muito mais robusta e performática. Além disso, o ASP.NET fornece uma flexibilidade muito grande onde, através de alguns objetos, você pode estender ou mudar o comportamento do processamento de uma determinada requisição.

Com toda essa facilidade, alguns pontos ainda são bem obscuros, principalmente no que diz respeito ao processo de recebimento e execução de um pedido. Apesar de não ser uma matéria obrigatória para o desenvolvimento de aplicações ASP.NET, a idéia deste artigo é explorar e entender todo o processamento de uma requisição para um recurso ASP.NET, começando desde o IIS até o processo interno, referente ao runtime do ASP.NET.

Internet Information Services, também conhecido como IIS, é o conjunto de serviços de internet para servidores e sistemas operacionais Windows. Entre os serviços disponibilizados por ele temos: FTP, SMTP, NNTP e HTTP/HTTPS. A sua primeira versão foi lançada e disponibilizada de forma livre como add-on para o Windows NT 3.51. A versão 3.0 introduziu o ASP (Active Server Pages), que é um ambiente para a escrita de páginas dinâmicas, baseada em scripting.

Em 2002 a Microsoft lançou o .NET Framework e com ele o ASP.NET, que é uma nova tecnologia para páginas Web, baseada na plataforma .NET. Em paralelo a Microsoft foi também criando versões do IIS e, atualmente, o IIS evoluiu até a versão 7.0, lançada recentemente. Com isso, o IIS sofreu algumas mudanças em sua arquitetura interna para suportar a execução de páginas ASP.NET. A intenção desta seção é explicar o funcionamento interno do IIS quando uma requisição é recebida. As versões que serão exploradas aqui são: IIS 5.x (Windows XP Professional ou Windows 2000), IIS 6.0 (Windows Server 2003) e IIS 7.0 (Windows Vista Enterprise ou Windows Server 2008).

Quando falamos de recursos estáticos, como é o caso de páginas HTML, imagens, CSS, etc., o IIS recebe a requisição para o mesmo e, se encontrado, recupera o conteúdo do arquivo solicitado e devolve o conteúdo para o cliente que o requisitou via stream de bytes via protocolo HTTP. Agora, quando estamos falando de uma tecnologia server-side, esta por sua vez, tem uma implementação particular no servidor, como é o caso do ASP.NET, que tem um acoplamento muito forte junto ao IIS.

Mas como o IIS consegue determinar o que é conteúdo estático e dinâmico? É neste momento que entra em cena um recurso chamado extensões ISAPI (Internet Server Application Programming Interface). Extensões ISAPI são módulos implementados como DLLs Win32 em que o IIS utiliza para processar determinados recursos. Para cada recurso há um mapeamento, onde você especifica a relação entre cada recurso e um determinado ISAPI. Para efetuar a associação, você especifica a extensão do arquivo e informa qual será o ISAPI correspondente e, quando a requisição para esse recurso (arquivo com a extensão previamente configurada) chegar ao IIS, ele utilizará a extensão ISAPI especificada para aquela extensão de arquivo para efetuar o processamento da requisição.

Através da imagem abaixo podemos analisar todas as extensões de arquivos e suas respectivas extensões ISAPI definidas dentro do IIS:

Figura 1 – Arquivos e suas respectivas extensões ISAPI.

Como podemos notar na imagem acima, quando chegar requisições para páginas *.asp, o ISAPI asp.dll é executado. Quando instalamos o .NET Framework na máquina, algumas configurações são feitas e armazenadas dentro do metabase do IIS. Entre essas configurações, várias extensões de arquivos intrínsicos da plataforma .NET e ASP.NET são mapeadas para o ISAPI aspnet_isapi.dll, que está contido dentro do diretório onde o .NET Framework foi instalado que, na maioria das vezes, é %windir%Microsoft.NETFrameworkVersao. Quando requisições chegarem para arquivos com extensão *.aspx, *.asmx, *.asax, *.ashx, *.ascx, etc., elas serão redirecionadas para o ISAPI aspnet_isapi.dll. O ISAPI aspnet_isapi.dll é o ponto de entrada para o processamento de recursos .NET/ASP.NET.

O metabase do IIS é uma espécie de “base de dados” que armazenam as informações de configurações do mesmo em formato XML (IIS 6.0) ou binário (IIS 5.x ou anteriores). Várias informações e configurações do IIS estão dentro de base e também dentro do registry do Windows. Todas as configurações que fazemos através da console do IIS são tipicamente armazenadas neste repositório.

Antes de explorarmos um pouco mais o ISAPI do ASP.NET, precisamos entender o que é um worker process. Basicamente, o worker process trata-se de um pequeno executável que serve de host para o CLR – Common Language Runtime – que se faz necessário, pois uma aplicação ASP.NET necessita do .NET Framework para ser executada. A comunicação e o fluxo de informações entre o worker process e o ISAPI irá depender da versão do IIS que você está utilizando. Analisaremos a seguir como o processamento ocorre para as versões 5.x, 6.0 e 7.0 do IIS.

Process Model – IIS 5.x

Como vimos anteriormente, a extensão ISAPI e o worker process são as principais partes do processamento da requisição. Além deles ainda há um terceiro processo, chamado de inetinfo.exe. Esse, por sua vez, é que carrega e serve de host para o ISAPI do ASP.NET (aspnet_isapi.dll).

Uma vez que a requisição para um recurso .NET/ASP.NET chega, o ISAPI é responsável por invocar o worker process que, nesta versão do IIS, é o aspnet_wp.exe que controlará a execução e fluxo da requisição. Neste caso, haverá uma única instância deste processo para todas as aplicações ASP.NET, e o isolamento delas é feito através de AppDomains, ou seja, para cada diretório virtual um AppDomain é criado dentro do worker process.

AppDomains

Historicamente, um processo é criado para isolar uma aplicação que está sendo executada dentro do mesmo computador. Cada aplicação é carregada dentro de um processo separado, que isola uma aplicação das outras. Esse isolamento é necessário porque os endereços são relativos à um determinado processo.

O código gerenciado passa por um processo de verificação que deve ser executado antes de rodar. Esse processo determina se o código pode acessar endereços de memória inválidos ou executar alguma outra ação que poderia causar alguma falha no processo. O código que passa por essa verificação é chamado de type-safe e permite ao CLR fornecer um grande nível de isolamento, como um processo, só que com muito mais performance.

AppDomain ou domínio de aplicação é um ambiente isolado, seguro e versátil que o CLR pode utilizar para isolar aplicações. Você pode rodar várias aplicações em um único processo Win32 com o mesmo nível de isolamento que existiria em um processo separado para cada uma dessas aplicações, sem ter o overhead que existe quando é necessário fazer uma chamada entre esses processos. Além disso, um AppDomain é seguro, já que o CLR garante que um AppDomain não conseguirá acessar os dados que estão contidos em outro AppDomain. Essa habilidade de poder rodar várias aplicações dentro de um único processo aumenta consideravelmente a escalabilidade do servidor.

Um AppDomain é criado para servir de container para uma aplicação gerenciada. A inicialização de um AppDomain consiste em algumas tarefas, como por exemplo, a criação da memória heap, onde todos os reference-types são alocados e de onde o lixo é coletado e a criação de um pool de threads, que pode ser utilizado por qualquer um dos tipos gerenciados que estão carregados dentro do processo.

O Windows não fornece uma forma de rodar aplicação .NET. Isso é feito a partir de uma CLR Host (que no caso do artigo é o aspnet_wp.exe), que é uma aplicação responsável por carregar o CLR dentro de um processo, criando AppDomains dentro do mesmo e executando o código das aplicações que desenvolvemos dentro destes AppDomains.

Quando uma aplicação é inicializada, um AppDomain é criado para ela. Esse AppDomain também é conhecido como default domain e ele somente será descarregado quando o processo terminar. Sendo assim, o Assembly inicial rodará dentro deste AppDomain e, se desejar, você pode criar novos AppDomains e carregar dentro destes outros Assemblies mas, uma vez carregado, você não pode descarregá-lo e isso somente acontecerá quando a AppDomain for descarregada.

O .NET Framework disponibiliza uma classe chamada AppDomain que representa e permite manipular AppDomains. Essa classe fornece vários métodos (alguns estáticos) que auxiliam desde a criação até o término de um AppDomain. Infelizmente isso está além do escopo do artigo pois, neste caso, o responsável pela criação dos AppDomains é o próprio host. Se desejar saber um pouco mais sobre a criação de domínios de aplicações, por favor, consulte o MSDN Library.

Se analisarmos a Figura 2, podemos notar que o ISAPI e o worker process estão em processos diferentes, e a comunicação entre eles é realizada através de named pipes que, basicamente, é um mecanismo de comunicação entre processos (IPC).

Figura 2 – IIS 5.x Process Model.

Neste modo o processo inetinfo.exe é o responsável por monitorar a porta 80, esperando por requisições de HTTP, enfileirando-as unicamente, onde aguardarão o processamento. Quando essa requisição é para um recurso ASP.NET, o processamento será delegado para o ISAPI correspondente ao ASP.NET (aspnet_isapi.dll); depois disso, ele comunica com o worker process (aspnet_wp.exe) que contém todo o runtime do ASP.NET para que seja possível processar efetivamente o recurso ASP.NET. É dentro deste worker process que todo código gerenciado (.NET) é executado. O processamento daqui em diante será tratado na próxima seção, ainda neste artigo.

É possível customizar o comportamento deste worker process. Essa customização vai desde a parte de segurança até questões de performance. Como falamos anteriormente, somente uma instância do worker process aspnet_wp.exe é criada, mas você pode especificar através do elemento processModel que está contido dentro do arquivo Machine.config alguns limites que, quando atingidos, uma nova instância deste worker process será criada para servir as requisições futuras. É importante dizer que quando uma nova instância é criada, a antiga continuará servindo as requisições pendentes mas não será “extinta”, caso utilize as configurações padrões do elemento processModel.

Como acabei de mencionar, quando a requisição é encaminhada para o worker process uma nova etapa é iniciada, que é o pipeline do ASP.NET, ou seja, é quando o código .NET é efetivamente executado. Mas essa parte esta fora do escopo dessa seção e será abordada logo na seqüência.

Process Model – IIS 6.0

Quando utilizamos o IIS 6.0 as regras mudam um pouco, justamente para melhorar a performance e, principalmente, o isolamento das aplicações que correm dentro do mesmo servidor. Como já sabemos, o IIS 6.0 é parte integrante do Windows Server 2003. Essa versão do IIS suporta dois modelos distintos de isolamento e processamento da requisição, sendo: worker process isolation mode (padrão) e IIS 5.0 isolation mode.

Como dito anteriormente, o worker process isolation mode é o modo padrão de isolamento e foi totalmente redesenhado no IIS 6.0. Nesta nova arquitetura, é possível você agrupar aplicações em application pools e assim você poderá customizar o comportamento do worker process para cada um dos application pools. Basicamente, cada application pool tem seu próprio worker process onde, neste caso, o ISAPI e o Runtime do ASP.NET são adicionados dentro do próprio worker process, podendo descartar a utilização do inetinfo.exe para servir de host para o ISAPI e, além disso, não é necessário haver um overhead para a comunicação entre processos, já que ambos estão carregados dentro do mesmo processo. Nesta versão do IIS, o worker process é o w3wp.exe.

Um dos maiores benefícios desta arquitetura é com relação ao isolamento das aplicações. Neste caso, podemos isolar cada aplicação em processos separados e, caso alguma aplicação se comporte de forma indevida, não prejudicará as outras aplicações que, por sua vez, estarão em processos diferentes. É importante dizer também que dentro de um application pool pode haver mais que um site e você pode especificar isso durante a criação do mesmo. Através da imagem abaixo, podemos entender melhor a distribuição destes processos dentro do IIS 6.0:

Figura 3 – IIS 6.0 Process Model.

Na imagem acima podemos reparar um novo integrante, que é o HTTP Protocol Stack, também chamado de http.sys. Este componente é implementado como um kernel-mode device driver e faz parte do subsistema de comunicação do Windows. Quando um novo site é criado, o IIS registra o site junto ao http.sys, que então passa a receber requisições HTTP para este site. Uma vez recebida a requisição para esse site, o http.sys encaminha a requisição para a aplicação correspondente e, depois de processada, devolve a resposta para quem requisitou. Um detalhe importante é a criação do worker process. Há um componente auxiliar que tem exatamente essa responsabilidade, ou seja, criar e gerenciar a vida do worker process do application pool. Esse componente é chamado de WWW Service.

Por questões de compatibilidade, o IIS 6.0 permite a mesma forma de isolamento do IIS 5.x. Isso permitirá rodar aplicações que foram desenhadas para as versões anteriores do IIS. Quando o IIS 6.0 estiver rodando em IIS 5.0 isolation mode o processamento de requisições é identico ao IIS 5.x, e as novas funcionalidades fornecidas pelo IIS 6.0 estarão desabilitadas. Para determinar se este modo de isolamento está ou não habilitado, há um flag dentro do metabase do IIS chamado de IIs5IsolationModeEnabled que deve estar definido como True.

Para finalizar, o processo inetinfo.exe tem uma comportamento diferente dentro do IIS 6.0. Esse comportamento poderá variar de acordo com o modo de isolamento escolhido para o IIS rodar. Se optou por worker process isolation mode, o inetinfo.exe servirá de host para o metabase do IIS e também para servir outros serviços “não Web”, como por exemplo, FTP, SMTP e NNTP. Agora, quando o IIS estiver em IIS 5.0 isolation mode, o inetinfo.exe trabalha da mesma forma que no IIS 5.x.

Assim como o IIS 5.x, daqui em diante o processo acontece dentro do runtime do ASP.NET e será explorado na próxima seção deste artigo.

Process Model – IIS 7.0

Como pudemos perceber, nas versões anteriores do IIS, o ASP.NET foi implementado utilizando uma extensão ISAPI, ou seja, através daqueles mapeamentos realizados no IIS, podemos vincular um recurso ASP.NET/.NET a uma determinada extensão ISAPI que, por sua vez, processará a requisição. Agora, a arquitetura de processamento do IIS 7.0 consiste em uma linha de módulos – nativos e gerenciados (escritos utilizando .NET) – que, baseado no recurso solicitado, executam determinadas tarefas durante a requisição e resposta, tarefas que podem ser adicionadas de forma plug-and-play.

Essa arquitetura é muito flexível, pois permite a você utilizar recursos ASP.NET para todo e qualquer tipo de requisição, ou seja, há a possibilidade de utilizar o forms authentication para páginas ASP. Um outro benefício é a questão de duplicação de funcionalidades, por exemplo: quando há uma requisição para algum recurso ASP.NET nas versões anteriores do IIS, a autenticação acontecia no pipeline do IIS e ASP.NET.

É importante dizer que o IIS 7.0 mantém compatibilidade com o IIS 6.0 e permite rodar em classic application pool mode que representa o worker process isolation mode da versão 6.0. Neste caso, a requisição para recursos ASP.NET é passado por alguns passos nativos dentro do IIS e, em seguida, direcionados para o ISAPI aspnet_isapi.dll para executar o código .NET. Como falamos anteriormente, o ponto negativo deste processo é a repetição de alguns passos, como é o caso de autenticação e autorização.

A imagem abaixo ilustra o processamento da requisição dentro do IIS 7.0, seguida da descrição de cada um dos passos que são executados até que o processamento da página seja executado. Podemos reparar que entra em cena um novo componente, chamado de WPAS (Windows Process Activation Service), que será discutido mais adiante, ainda neste artigo.

Figura 4 – IIS 7.0 Process Model.
  • 1 – Quando o cliente efetua uma requisição HTTP, o HTTP.sys intercepta a requisição.
  • 2 – HTTP.sys contata o WPAS para obter as informações do repositório de dados do IIS.
  • 3 – WPAS requisita as informações de configuração do repositório do IIS.
  • 4 – WWW Service recebe as informações de configuração, tais como o application pool e as configurações do site.
  • 5 – WWW Service utiliza essa informação para configurar o HTTP.sys.
  • 6 – WPAS inicia o worker process para o application pool para que a requisição possa ser feita.
  • 7 – O worker process processa e retorna a resposta para o HTTP.sys.
  • 8 – O cliente recebe a resposta.

O trabalho que era realizado pelo serviço W3 Service no IIS 6.0 foi agora dividido em dois: W3Service e WPAS. No IIS 6.0, o W3Service gerenciava a administração e configuração HTTP, gerenciamento do processo e monitoramente de performance. Já na versão 7.0, ele não gerencia mais o worker process. Ao invés disso, o W3Service serve como listerner para HTTP. Com este papel, o W3Service é responsável por configurar e atualizar o HTTP.sys quando mudanças acontecerem e ainda notificar o WPAS quando uma requisição entrar na fila.

O WPAS (Windows Process Activation Service), um novo serviço adicionado nesta versão, gerencia a ativação e o tempo de vida dos worker processes. O WPAS generaliza o modelo de processamento do IIS 6.0, removendo a dependência do protocolo HTTP. Isso permite expor, além de simples páginas ASP.NET, serviços WCF que utilizam ou não o protocolo HTTP, possibilitando assim toda a infraestrutura e utilizando o IIS como hosting para serviços WCF.

Podemos ainda notar na imagem acima um novo elemento chamado applicationHost.config. Este arquivo é baseado em XML (lembram do metabase?), encontra-se no seguinte endereço: %windir%System32InetSrvconfig e é o arquivo que contém as principais configurações para o IIS 7.0, tais como sites, aplicações, diretórios virtuais, etc.. Este arquivo possui duas grandes seções:

  • system.applicationHost: inclui as informações para o serviço de ativação, como a lista de application pools, logging, listeners e sites. Essas configurações são centralizadas e podem somente existir neste arquivo.

  • system.webServer: esta seção inclui todas as configurações do servidor Web, como por exemplo, a lista de módulos e filtros ISAPI, ASP, CGI, etc.. Essas configurações podem ser definidas (se permitidas) em qualquer arquivo Web.Config de aplicações que rodam no servidor.

Agora que entendendo a parte de processo da requisição no IIS 7.0 de uma forma macro, é importante mostrar como o processo ocorre dentro do worker process. Como foi mencionado acima, o IIS 7.0 é todo modularizado e fortemente integrado com o runtime do ASP.NET. Isso permite a nós desenvolvedores estender funcionalidades do próprio IIS em linguagem .NET ao invés de C++. Como também mencionamos acima, o IIS 7.0 pode rodar de duas formas, sendo: a forma integrada ao ASP.NET e a forma clássica, como o IIS 6.0.

Quando uma requisição ASP.NET chega para uma das versões anteriores do IIS, primeiramente ele é processado pelo IIS e, identificado que existe um ISAPI correspondente para esse recurso, o IIS encaminha a requisição para este ISAPI, que é aspnet_isapi.dll que, por sua vez, hospeda o runtime do ASP.NET para efetivamente processar a página. Quando a requisição trata de um recurso estático, como imagens, arquivos HTML ou até mesmo ASP, o próprio IIS efetua o processamento ou delega o mesmo para extensões ISAPI correspondentes, caso existam. Como já foi dito, a limitação neste caso é que não podemos utilizar recursos fornecidos pelo .NET/ASP.NET para requisições/arquivos “não ASP.NET”, além também da duplicação de alguns passos, como pode ser visualizado através da figura abaixo:

Figura 5 – Modelo clássico de requisições no IIS 7.0.

Já no modo integrado, que é a nova forma de processamento do IIS 7.0, quando uma requisição para qualquer recurso chega, ela é processada pelo IIS, com módulos nativos e módulos ASP.NET, sendo capaz assim, de fornecer o processamento em qualquer estágio. Isso possibilita utilizarmos módulos do ASP.NET como o Forms Authentication ou o Output Cache ser usado por outros tipos de requisições, como o ASP 3.0 ou arquivos estáticos.

Isso permite dar uma flexibilidade muito grande, já que permite “plugar” diretamente módulos (escritos em .NET) em diversos estágios do processamento da requisição, rodando antes ou depois das funcionalidades do IIS. Um exemplo disso é a possibilidade de criar uma autenticação customizada, validando as credenciais fornecidas pela autenticação Basic (que somente trabalha com contas no Windows) em uma base de dados SQL Server, utilizando o conceito de membership. A imagem abaixo ilustra esta nova forma de processamento, que é uma característica do IIS 7.0:

Figura 6 – Modelo integrado de requisições no IIS 7.0.

Créditos: É importante dizer que as informações a respeito do IIS 7.0 foram retiradas do site oficial do produto e testadas com o Windows Vista Enterprise. Endereço: http://www.iis.net/default.aspx?tabid=7.

ASP.NET Pipeline

Uma vez que a requisição foi enviada para o servidor, o IIS a captura e inicia o seu processamento até chegar ao ISAPI que tratará a requisição. Os passos detalhados deste processo foram descritos na seção anterior. Quando o CLR é carregado, é o momento de iniciar o processamento do código .NET em si. Esta seção destina-se a entender como o processamento da requisição é feito nos bastidores do ASP.NET, analisando quando e onde os objetos mais comuns são criados, bem como os passos que são executados antes e depois do processamento efetivo da página ASP.NET requisitada.

A ponte entre o processo ISAPI e runtime do ASP.NET é feita através de alguns tipos que estão contidos dentro do namespace System.Web.Hosting. Entre esses tipos, temos a classe ISAPIRuntime que expõe alguns métodos para ser invocados a partir do mundo COM. A instância dessa classe é criada através do método Create da classe AppManagerAppDomainFactory que, por sua vez, gerencia a criação e o fechamento de application domains para a aplicação Web. Uma vez criada a instância da classe ISAPIRuntime, ela disponibiliza um método chamado ProcessRequest que é o responsável por criar, em seu interior, um objeto do tipo HttpWorkerRequest que processará a requisição corrente.

As instâncias do tipo HttpWorkerRequest retornadas pelo método estático CreateWorkerRequest da classe ISAPIWorkerRequest vão depender da versão do IIS em que a aplicação está correndo. Essas instâncias são uma implementação de alto nível, sendo uma espécie de wrapper, responsável por executar grande parte das chamadas APIs nativas, contidas dentro da classe UnsafeNativeMethods.

Ainda dentro do método ProcessRequest da classe ISAPIRuntime, depois do objeto HttpWorkerRequest devidamente criado, ele é passado (indiretamente) para método estático ProcessRequestInternal da classe HttpRuntime. Essa classe fornece uma porção de membros estáticos referentes à aplicação corrente. O método ProcessRequestInternal é responsável por criar uma instância da classe HttpContext que, como sabemos, representa e encapsula todas as informações referentes à requisição HTTP corrente, como os objetos de requisição (HttpRequest) e resposta (HttpResponse). Além disso, ele se encarrega de verificar se a requisição é ou não a primeira e, se for, inicializa várias funcionalidades e verificações, tais como: inicialização do health monitoring e tracing, verificação de permissão de acesso ao diretório temporário do ASP.NET, etc.. Depois disso, através do método estático GetApplicationInstance da classe HttpApplicationFactory, retorna uma instância da classe HttpApplication.

É importante lembrar que o objeto HttpApplication representa o arquivo Global.asax e é neste arquivo que podemos interceptar os passos executados através dos eventos disponibilizados por esta mesma classe. É no momento da criação desta classe que todos os módulos e handlers configurados na aplicação, seja a nível de servidor ou de aplicação, são carregados. Com este objeto devidamente inicializado, ele é retornado para o método ProcessRequestInternal que invoca o seu método BeginProcessRequest. Neste momento, todos os módulos são executados e, entre eles, há um módulo que materializa a requisição para um determinado objeto para, em seguida, instanciá-lo e chamar seus respectivos métodos. A materialização é realizada através de um handler que, através do recurso solicitado e o mapeamento realizado no arquivo de configuração, é criado e, em seguida, efetua o processamento da requisição.

Os módulos são como uma espécie de filtro, permitindo a nós desenvolvedores, anexar uma determinada tarefa ou verificação a qualquer etapa do processamento do pedido, antes ou depois da execução do recurso solicitado. Já os handlers são os componentes que efetivamente executam o código referente ao recurso solicitado, através do mapeamento efetuado no arquivo de configuração. A imagem abaixo ilustra como cada um desses componentes estão definidos dentro do pipeline do ASP.NET.

Figura 7 – Módulos e Handlers durante a requisição.

Cada um dos módulos que visualizamos na imagem acima trata de um módulo já criado pela Microsoft e faz parte do processamento para uma requisição para uma página ASP.NET. Se quisermos traduzir a imagem acima para os módulos e o handler específico do tratamento de uma página ASP.NET, a mesma ficaria da seguinte forma:

Figura 8 – Ordem de processamento dos módulos.

Como podemos notar, quando chega o momento de executar a página o seu respectivo handler (System.Web.UI.PageHandlerFactory) é executado para que o mesmo possa instanciar a classe correspondente a página, deixando-a pronta para que o módulo referente à execução possa processá-la, invocando o método ProcessRequest, que é o ponto inicial da execução de qualquer handler (página).

Conclusão: Com este artigo pudemos analisar e entender o que acontece nos bastidores da execução de uma página ASP.NET. Como eu disse anteriormente, isso não é uma matéria obrigatória para que você possa desenvolver aplicações ASP.NET, mas que, quando você conhece o fluxo, facilmente conseguirá customizar o processamento do mesmo, tendo conhecimento suficiente para deixar somente executar o que realmente importante, tendo um ganho considerável na aplicação, sem a necessidade de executar passos desnecessários.

WCF – Consumindo serviços no AJAX

Uma das grandes partes do .NET Framework 3.0 foi o WCF – Windows Communication Foundation. Quando ele foi lançado, várias formas de acessar os serviços WCF também foram disponibilizadas. Entre as formas, conhecidas como endpoints, podemos citar algumas, tais como: HTTP, TCP e MSMQ. Com a vinda do ASP.NET AJAX, surgiu a necessidade de consumir serviços WCF diretamente dentro deste tipo de aplicação.

Através do Visual Studio .NET 2008 e o .NET Framework 3.5, a Microsoft se preocupou com a necessidade de consumir serviços WCF no AJAX e aproveitou esta oportunidade para criar um binding. Este binding, chamado de WebHttpBinding, trata-se de um novo tipo de binding que permite a criação de um endpoint para ser consumido por aplicações AJAX. É basicamente algo semelhante ao BasicHttpBinding, com a exceção de que os dados são serializados em formato JSON (JavaScript Object Notation). É bom dizer que esta classe está dentro do namespace System.ServiceModel do Assembly System.ServiceModel.Web.dll.

Para possibilitar a chamada de serviços WCF através do AJAX é necessário, primeiramente, a criação da estrutura do serviço, ou seja, a criação da Interface, decoradas com os atributos ServiceContract e OperationContract; além disso, a criação de um arquivo *.svc para que seja possível acessá-lo. Até este momento, não há nada de diferente em relação a criação de um serviço que será exposta via protocolo HTTP, usando como host o IIS. As mudanças começam efetivamente a partir daqui, ou seja, a configuração do endpoint no arquivo Web.Config e do controle ScriptManager.

Como foi mencionado acima, devemos recorrer a utilização do binding webHttpBinding. O único detalhe que você deve se atentar é na configuração do behavior do endpoint, onde devemos informar o elemento enableWebScript para habilitar o acesso via AJAX. O trecho de código abaixo ilustra a configuração do serviço e do endpoint no arquivo Web.Config:

<system.serviceModel>
  <services>
    <service name="Usuario" behaviorConfiguration="SrvBehavior">
      <endpoint
        address="mex"
        binding="mexHttpBinding"
        contract="IMetadataExchange"/>
      <endpoint
        binding="webHttpBinding"
        behaviorConfiguration="EdpBehavior"
        contract="IUsuario"/>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="SrvBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name="EdpBehavior">
        <enableWebScript />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

Uma vez configurado o serviço, chega o momento de consumí-lo no cliente, ou melhor, através do código Javascript. O primeiro passo é definir um controle ScriptManager na página onde deseja consumir o serviço; esse controle gerencia as bibliotecas e arquivos de scripts e, entre outras funcionalidades, a geração de proxies para o consumo de serviços via AJAX. Esse controle possui uma coleção de serviços chamada Services, onde cada um dos elementos, do tipo ServiceReference, corresponde a um serviço que será exposto e consumido pelo cliente.

O próximo passo é, através do código Javascript, invocar o código no cliente. Assim como acontecia com o Client-Side Callbacks, você cria uma função que servirá como ponto de entrada e será disparada através de um evento, também client-side; na implementação desta função, você efetivamente invocará o serviço WCF, mas na verdade é método do proxy, que foi criado pelo controle ScriptManager. O proxy cria os métodos que são disponibilizados pelo serviço, mas adiciona mais três parâmetros, que são: o ponteiro para um método de callback que será disparado quando o método retornar; um ponteiro para um método que será disparado caso alguma exceção for disparada quando o método for executado e, finalmente, um objeto que servirá como contexto e será passado para todos os métodos durante a execução. O código abaixo mostra a página ASPX devidamente configurada para invocar o serviço WCF:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    
        function ExibirBoasVindas()
        {
            var proxy = new tempuri.org.IUsuario();
            proxy.BoasVindas('Israel', Resultado, null, null);
        }
        
        function Resultado(result)
        {
            alert(result);
        }
    
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/UserService.svc" />
            </Services>
        </asp:ScriptManager>
        <input
            id="Button1"
            type="button"
            value="button"
            onclick="javascript:ExibirBoasVindas();" />
    </form>
</body>
</html>

Como podemos notar, definimos o serviço WCF (arquivo *.svc) no interior do controle ScriptManager que será responsável por criar, em tempo de execução, o proxy para o consumo via AJAX. Já o tempuri.org trata-se do namespace padrão, qual você pode customizar através do da propriedade Namespace do atributo ServiceContract e ainda, se você desejar visualizar o proxy que é criado, pode acessar o serviço WCF da seguinte forma: http://localhost:1029/Projeto/UserService.svc/js, ou seja, adicionar o /js no final do endereço do serviço. Isso permitirá que você efetue o download do proxy que é gerado em tempo de execução.

Importante: Também temos uma nova factory definida dentro do namespace System.ServiceModel.Activation, chamada WebScriptServiceHostFactory que está presente no .NET Framework 3.5. Definindo essa factory no atributo ServiceHost do arquivo *.svc, elimina qualquer configuração no arquivo Web.Config da aplicação, já que ela configura todos os requisitos necessários (behaviors, binding, etc.) para o consumo do serviço via AJAX.

Chamadas entre domínios

É importante dizer que o objeto XMLHttpRequest não permite efetuar chamadas para serviços que estão fora dos limites do servidor local. Sendo assim, você não poderá referenciar serviços que estão em outros domínios ou sub-domínios e mudança de protocolos ou portas. Podemos, através da imagem abaixo, visualizar essas restrições:

Figura 1 – Invocando serviços além dos limites da aplicação.

Como podemos ver, não podemos invocar qualquer serviço que está além da aplicação local mas, se por algum motivo, desejarmos fazer isso, então devemos proceder da mesma forma que vimos no decorrer deste artigo porém, o serviço WCF local que devemos criar, servirá de proxy para o serviço que está em algum outro local.

Conclusão: Os serviços WCF fornecem uma grande flexibilidade em cenários que precisam, de alguma forma, expor informações ou componentes para que terceiros possam consumí-los. Finalmente, com a vinda do Visual Studio .NET 2008 e o .NET Framework 3.5, podemos agora invocar esses serviços através de aplicações AJAX, garantindo assim uma interatividade muito melhor, sem a necessidade de recorrer a postbacks para a execução de recursos como estes.