Thursday, November 20, 2008
#
Um dia desses um colega me perguntou como customizar, ou melhor, como variar o OutputCaching de uma página para cada usuário da aplicação. Entre as opções que a diretiva @OutputCache fornece, temos a opção VaryByCustom, especificando como valor deste atributo uma string qualquer, com a finalidade de definirmos uma informação, que indicará como proceder para capturar a "chave" que será utilizada pelo runtime, para determinar se já existe ou não uma versão do cache. Abaixo está a configuração desta diretiva:
<%@ OutputCache Duration="60" VaryByCustom="UserName" VaryByParam="none" %>
Quando utilizamos esta técnica, devemos sobrescrever o método GetVaryByCustomString da classe HttpApplication (leia-se Global.asax) e, lá colocar o código que irá retornar a string que servirá de "chave", para que o ASP.NET use-a para determinar se já existe ou não uma versão para ela. No nosso caso, a finalidade é criar uma versão de caching para cada usuário, portanto, temos o seguinte código:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if(custom == "UserName" && context.User.Identity.IsAuthenticated)
return context.User.Identity.Name;
return base.GetVaryByCustomString(context, custom);
}
Wednesday, November 19, 2008
#
O Visual Studio .NET 2008 traz uma funcionalidade que permite aos desenvolvedores de serviços WCF, efetuar a depuração do mesmo através do cliente, ou seja, quando estamos executando o cliente em modo de depuração, ao chamar uma operação através da instancia do proxy (via F11 - Step Into), automaticamente seremos redirecionados para o método do serviço, executando-o passo-à-passo.
Ao pressionar F11, o debugger automaticamente irá se vincular ao processo que corresponde efetua o host do serviço e, ao finalizar a execução do método, o controle voltará ao cliente. O mais importante é que a janela Call Stack traz informações tanto do cliente como do serviço.
Caso voce não esteja fazendo uso destas funcionalidades, então provavelmente isso está desabilitado. Para habilitar este recurso, basta recorrer ao utilitário vsdiag_regwcf.exe. Este utilitário está disponível a partir do Prompt de comando do Visual Studio .NET 2008 (se estiver rodando no Windows Vista, então é necessário rodar como Administrador) e, via os seguintes parametros, voce poderá interagir com ele:
Tuesday, November 18, 2008
#
Os ASP.NET Web Services fornecem uma possibilidade de efetuar a autenticação do serviço através de SOAP Headers. Para isso, bastava criar uma classe que herde da classe SoapHeader e criar as propriedades UserName/Password e, via SoapHeaderAttribute, voce vinculava este header aos métodos que exigem a autenticação do usuário para poder funcionar.
Essa configuração funciona bem, mas é vulnerável. A questão é que o envelope SOAP irá trafegar entre o cliente e o serviço de forma desprotegida e, sendo assim, qualquer um que interceptar a requisição, conseguirá extrair essas informações confidenciais. A autenticação baseada em SOAP Headers apenas são seguras se o transporte garantir a segurança; em outras palavras, isso quer dizer HTTPS. Outra alternativa ainda utilizando os tradicionais ASP.NET Web Services, seria a utilização do WSE (Web Services Enhancements), que possibilita a segurança em nível de mensagens.
O WCF, que é seguro por padrão, tem um comportamento ligeiramente diferente em relação aos ASP.NET Web Services. Para utilizar a autenticação baseada em UserName/Password que o WCF fornece sob o protocolo HTTP, será necessário utilizar um certificado. Sem a utilização deste, não seria possível garantir a integridade e confidencialidade da mensagem, comprometendo as informações e, principalmente, permitindo que alguém intercepte a mensagem e capture os dados sigilosos. Assim como os ASP.NET Web Services, se o serviço WCF for exposto via HTTPS, isso não é necessário, mas utilizar segurança baseada em transporte tem seu ponto negativo: a segurança é apenas garantida ponto-a-ponto e, caso haja intermediários entre o cliente e o serviço, não temos a garantia de que a mensagem chegará segura até o destino.
Wednesday, November 12, 2008
#
Estou trabalhando em um projeto que utiliza o Health Monitoring para notificações de que alguma falha ocorreu na aplicação. Essa configuração faz com que qualquer exceção não tratada, seja capturada pelo Health Monitoring e a seção customErrors garante uma página amigável para o usuário final.
A maioria dos erros não tratados são ocasionados por certos valores que são passados através de algum campo no formulário e, muito raramente, através de query strings (no caso deste projeto). A configuração padrão do Health Monitoring traz a stack trace do erro que ocorreu e outras informações relevantes ao problema, como o usuário, tipo da exceção, servidor, processo, etc. O problema é que, para simular o erro em um ambiente de teste ou até mesmo em produção, é necessário termos os mesmos valores especificados pelo usuário, para assim tornar essa simulação o mais próximo do que ocorreu. Para isso, podemos criar um evento customizado e fazer uso dele quando uma exceção for disparada.
Essa classe que representará o evento customizado herda diretamente da classe WebRequestErrorEvent e tem o nome de WebParametersRequestErrorEvent. Ao contrário dos eventos padrões que já estão embutidos no ASP.NET, os eventos customizados devem ser explicitamente disparados e, para poupar código, podemos criar um módulo. O módulo fornece a instancia da classe HttpApplication que nos permite interceptar o evento Error que, por sua vez, é disparado sempre quando um erro não tratado ocorre na aplicação. Com isso, basta simplesmente instanciar e invocar o método Raise do evento customizado.
Finalmente, apenas o que precisamos é configurar devidamente o Health Monitoring no arquivo Web.Config da aplicação, fazendo o mapeamento entre o eventMapping, rules e providers, como é mostrado detalhadamente através do código abaixo:
<system.web>
<healthMonitoring enabled="true" heartbeatInterval="0">
<providers>
<add
name="SimpleMailWebEventProvider"
type="System.Web.Management.SimpleMailWebEventProvider"
from="suporte@site.com.br"
to="israel@site.com.br"
buffer="false" />
</providers>
<rules>
<add
name="WebParametersRequestErrorEventModule Mail"
eventName="WebParametersRequestErrorEvent"
provider="SimpleMailWebEventProvider"
profile="Default"
minInstances="1"
maxLimit="Infinite"
minInterval="00:00:00"/>
</rules>
<eventMappings>
<add
name="WebParametersRequestErrorEvent"
type="Logger.WebParametersRequestErrorEvent, Logger"/>
</eventMappings>
</healthMonitoring>
<httpModules>
<add
name="WebParametersRequestErrorEventModule"
type="Logger.WebParametersRequestErrorEventModule, Logger"/>
</httpModules>
<system.net>
Ao receber o e-mail, tenho todos os detalhes do erro que já era fornecido pelo evento WebRequestErrorEvent, e mais os dados contextuais da requisição:
Custom event details:
Request - QueryStrings
ID: 123
Nome: Israel Aece
E-mail: israel@projetando.net
Request - Forms
__VIEWSTATE: /wEPDwUJOTQ4N...m+ks3yA==
__EVENTVALIDATION: /wEWBALs0J+...ewcwK+BGBh
txtValor: 123,45
txtNome: Jack Bauer
Button1: Button
Download: WebParametersRequestErrorEvent e WebParametersRequestErrorEventModule.
Friday, November 07, 2008
#
Quando fazemos uma referencia a um serviço WCF, e ele possuir um certificado definido para assegurar a troca de informações entre o cliente o e serviço, a IDE do Visual Studio ou o utilitário svcutil.exe, adicionanão um elemento chamado identity/certificate com o atributo encodedValue. O valor deste atributo é uma série de letras e números mas, o que isso representa?
Essa informação trata-se da chave pública do certificado, codificada no padrão Base64. Ela será utilizada pelo runtime do WCF para criptografar as informações que serão trocadas, mais precisamente, as credenciais. Quando essa informação não existir no cliente, a configuração do serviço deverá permitir a negociação ou informar a referencia para o certificado que, geralmente, esta armazenado em um dos repositórios do cliente.
Monday, November 03, 2008
#
É muito comum envolvermos objetos que utilizam recursos custosos dentro de um bloco using, fazendo com que o método Dispose do mesmo seja disparado independemente de exceções que sejam disparadas durante a execução. Vale lembrar que somente podemos envolver objetos que implementam a interface IDisposable.
Quando fazemos a referencia para algum serviço WCF em uma aplicação cliente, automaticamente a IDE do Visual Studio (ou através do utilitário svcutil.exe), se encarregará de criar uma classe que herda diretamente da classe abstrata, chamada ClientBase<TChannel>. Essa classe implementa tudo o que é necessário para possibilitar a comunicação entre cliente e o serviço (em outras palavras, trata-se do proxy) e, que por sua vez, faz uso de objetos caros (channels, etc.) e que precisam ser brevemente liberados. Como a classe ClientBase<TChannel> implementa a interface IDisposable, isso quer dizer que podemos envolver a instancia do proxy em um bloco using, para que o método Dispose seja automaticamente disparado.
Se envolvermos o proxy em um bloco using (lembre-se de que ele será transformado em try/finally), é necessário tomarmos um certo cuidado. Não porque o método Dispose não será chamado, mas sim onde que a execução deste método afetará a aplicação cliente. Vamos supor que algum erro ocorra durante a execução da operação e, como já era de se esperar, o bloco finally será disparado, chamando o método Dispose do proxy. Neste caso, o método Dispose não faz mais nada a não ser invocar o método Close. O problema aqui é que o método Close poderá exigir algumas atividades extras, necessitando fazer alguma outra comunicação com o serviço e, se neste momento algum erro ocorrer, a exceção que chegará ao cliente será:
System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
..., mascarando assim, o real problema. Finalmente, a opção para contornar esse possível problema, é a chamada do método Abort, que encerra imediatamente a comunicação entre o cliente o serviço, assim como é demonstrado neste link.
Sunday, November 02, 2008
#
A Microsoft criou o WCF e implementou nele os protocolos de segurança, transações e sessões confiáveis baseando-se nas primeiras versões destes padrões, que são gerenciados pela OASIS.
Com o .NET Framework 3.5, a Microsoft atualizou o WCF, disponibilizando agora a implementação mais atualizada destes protocolos através de dois novos bindings: WS2007HttpBinding e WS2007FederationBinding.
Friday, October 31, 2008
#
Quando precisamos de escalabilidade em nossas aplicações, é muito comum recorrermos a técnicas de cluster, failover ou load balancing para que seja possível atender as muitas requisições que chegam para a mesma.
Serviços WCF não são diferentes. Serviços que estão disponíveis através do protocolo HTTP, se comportam como aplicações ASP.NET. Uma vez que eles são expostos, podem ser acessados por milhares de clientes, afetando a sua performance. Para habilitar a técnica de load balancing para serviços WCF (HTTP), é importante que voce se atente a que funcionalidades que o seu serviço WCF utiliza e, principalmente, qual a forma de gerenciamento de instancia configurada.
Quando o serviço é configurado no modo PerCall, cada requisição criará uma instancia do serviço, executará a operação e, quando finalizar, a instancia criada será descartada; uma nova requisição, recria um novo objeto, executa a operação e o descarta e, assim sucessivamente. Já quando o modo é definido como PerSession, a idéia é manter uma instancia do serviço dedicada para cada proxy, atendendo a todas as operações oriundas daquele cliente.
Caso o serviço esteja em um ambiente de load balancing com o modo PerSession definido, podemos ter alguns problemas e, entre eles, a garantia de que a requisição será sempre atendida por um determinado servidor. Neste caso, perdemos o estado do serviço, trazendo resultados inesperados para este modo. O BasicHttpBinding não suporta sessão, ao contrário do WSHttpBinding que, por sua vez, emula o suporte a sessão mas que, é ineficaz neste ambiente, já que o WCF não traz suporte de gerenciamento de estado/sessão "cross-machine" como é o caso do ASP.NET. Finalmente, o modo Single também necessita um cuidado especial, já que apenas haverá uma única instancia para atender a todas as requisições. Onde será armazenada a instancia da classe que representa o serviço?
Sempre que possível, em um ambiente de alta escalabilidade, opte pela opção PerCall. Mas quando houver a necessidade de armazenar informações entre as requisições do cliente, uma alternativa para serviços PerCall, é incluir um identificador no contrato (ou extraí-lo das informações contextuais) para salvar e/ou carregar o estado e, como repositório, deve ser utilizado um recurso compartilhado, como uma base de dados.
Thursday, October 30, 2008
#
Por curiosidade, abri o .NET Reflector com o .NET Framework 4.0 e comecei a explorar as classes que ele disponibiliza e, logo percebi um novo namespace: System.IO.MemoryMappedFiles. Dentro deste namespace, entre as poucas classes, temos a classe MemoryMappedFile. É importante dizer que é uma versão CTP, sem a garantia de que isso prevalecerá na versão final.
A finalidade dela é mapear um espaço da memória para o conteúdo de um determinado arquivo (ou algum outro recurso), criando um objeto que servirá de "ponte" entre a aplicação e o arquivo físico, criandos "views" deste arquivo e, como principal benefício, permitirá acessar "seções" do arquivo, sem carregá-lo completamente para a memória. Abaixo um exemplo simples que faz a utilização desta classe:
using System.IO;
using System.IO.MemoryMappedFiles
using (MemoryMappedFile mmf =
MemoryMappedFile.CreateFromFile(new FileStream("C:\Teste.txt", FileMode.Open)))
{
byte[] buffer = new byte[5];
mmf.CreateViewStream(120, 5).Read(buffer, 0, 5);
Console.WriteLine(Encoding.Default.GetString(buffer));
}
O primeiro parametro do método CreateViewStream é a posição inicial dentro do arquivo e, o segundo, a quantidade de caracteres que voce quer extrair.
Há algum tempo eu escrevi sobre a possibilidade de criar serviços REST em WCF. Visando incrementar esses tipos de serviços, a Microsoft disponibilizou recentemente o Windows Communication Foundation REST Stater Kit. Este kit fornece templates de projeto e diversas funcionalidades para serviços REST criados sob WCF. As funcionalidades fornecidas pelo kit facilita o uso do protocolo HTTP dentro do serviço e encapsula algumas funcionalidades expostas pelo ASP.NET. Entre as principais funcionalidades, temos caching, tratamento/manipulador de erros, página de suporte, estensibilidade, etc.
Todas as funcionalidades estão contidas no assembly Microsoft.ServiceModel.Web.dll. O caching funciona de forma muito parecida com o caching do ASP.NET/ASP.NET Web Services, pois foi criado um atributo chamado WebCacheAttribute que, podemos aplicar em algum método de nosso serviço. Exemplo:
[WebGet(UriTemplate = "")]
[WebCache(Duration = 30)]
[OperationContract]
string RecuperarAlgumaInformacao();
Outra possibilidade que temos, é a criação de uma página de suporte, que traz informações relacionadas aos métodos que o serviço disponibiliza. Para acessá-la, basta colocar "/help" no final da URL do serviço que, automaticamente, uma "página amigável" será exibida com essas informações. Vale lembrar que voce poderá customizar a descrição de um determinado método, através do atributo WebHelpAttribute, como é mostrado abaixo:
[WebGet(UriTemplate = "")]
[WebHelp(Comment = "Retorna uma informação qualquer.")]
[OperationContract]
string RecuperarAlgumaInformacao();
Alguns novos tipos também foram estendidos para suportar essas novas funcionalidades. Entre esses tipos, temos as classes WebServiceHost2, WebServiceHost2Factory e WebHttpBehavior2. Esses tipos são semelhantes aos existentes no .NET Framework 3.5, mais precisamente, dentro do Assembly System.ServiceModel.Web.dll. O WebServiceHost2 possui uma propriedade chamada Interceptors que, como o próprio nome diz, permite-nos acoplar um "interceptador" e colocar ali algum código que desejamos executar durante a requisição. Para criar um interceptador, tudo o que precisamos fazer é herdar da classe abstrata RequestInterceptor e sobrescrever o método ProcessRequest, como é mostrado abaixo:
public class MeuInterceptador : RequestInterceptor
{
public MeuInterceptador() : base(true) { }
public override void ProcessRequest(ref RequestContext requestContext)
{
Message msg = requestContext.RequestMessage;
//faz algo customizado aqui
}
}
E, depois de criado, precisamos adicioná-lo na coleção de interceptors do WebServiceHost2, o que nos obriga a criar uma service factory customizada (atente-se a sua configuração), conforme é mostrado abaixo:
internal class AppServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
WebServiceHost2 w = new WebServiceHost2(serviceType, true, baseAddresses);
w.Interceptors.Add(new MeuInterceptador());
return w;
}
}