Israel Aece

Software Developer

Propagando Transações

Como já sabemos, desde a versão 2.0 o .NET Framework possui uma API para criar e gerenciar transações dentro da aplicação. É o assembly System.Transactions.dll que disponibiliza um conjunto de APIs que podemos criar blocos transacionais, e assim alistar recursos que precisam de "proteção" para garantir que tudo seja efetivado ou desfeito caso algum problema ocorra (independente se o problema for de infraestrutura ou regra de negócio).

A principal classe que temos para isso é a TransactionScope, que ao ser envolvida em um bloco using, no método Dispose ela realiza o commit ou o rollback, dependendo se o método Complete foi ou não chamado. É dentro deste escopo criado que o .NET gerencia, e dependendo do que está envolvido nele, ele utiliza uma transação local (LTM) ou escala para uma transação distribuída (DTC).

Aqui estamos falando de um mesmo processo que é responsável por criar a transação, alistar os recursos, e por fim, finalizar a mesma (com sucesso ou falha). Mas como fazer quando a transação é iniciada pelo processo A e precisamos envolver as tarefas executadas por outro processo (B)? Ainda dentro deste mesmo assembly, temos uma classe chamada TransactionInterop, que expõe alguns métodos para "expandir" a transação entre processos.

Basicamente ele fornece métodos que geram uma chave e métodos que permitem recriar a transação a partir da mesma. Isso nos permitirá na aplicação A gerar a chave e importar na aplicação B, e com isso, ambas aplicações estarão envolvidas na mesma transação. É através do método estático GetTransmitterPropagationToken que geramos um array de bytes que representa o token de identificação da transação. Para exemplificar, vamos gerar este token a partir da aplicação A e armazenar este token um arquivo no disco para compartilhar com a aplicação B. No código abaixo estou removendo os logs para poupar espaço.

//Aplicação A
using (var ts = new TransactionScope())
{
    File.WriteAllBytes(@"C:\Temp\TransactionToken.bin",
        TransactionInterop.GetTransmitterPropagationToken(Transaction.Current));

    ts.Complete();
}

Do outro lado, utilizamos o método (também estático) GetTransactionFromTransmitterPropagationToken da classe TransactionInterop. Ele recebe como parâmetro um array de bytes (que estamos extraindo do arquivo) e recria a transação. Aqui o detalhe importante é reparar que o resultado deste método está sendo passado diretamente para um dos construtores da classe TransactionScope, que faz uso desta transação (criada pela outra aplicação) e envolve nela tudo o que está em seu próprio bloco using.

//Aplicação B
using (var ts =
    new TransactionScope(
        TransactionInterop.GetTransactionFromTransmitterPropagationToken(
            File.ReadAllBytes(@"C:\Temp\TransactionToken.bin"))))
{
    ts.Complete();
}

Se repararmos as imagens abaixo, notamos que na a aplicação A possui o identificador de transação distribuída (DI) zerado, e quando a aplicação B se envolve na mesma transação, o identificador da transação distribuída na aplicação A reflete o mesmo identificador da aplicação B, ou seja, ambas aplicações estão envolvidas na mesma transação. Por fim, é possível ver na última imagem o log do DTC, indicando que a mesma foi promovida à uma transação distribuída.





Curiosidade: sabemos que o WCF fornece suporte à transações em serviços, podendo o cliente iniciar a mesma e o serviço participar. O id da transação viaja do cliente para o serviço através de headers do envelope SOAP, e quando o serviço recebe a informação, ele recria a transação utilizando os recursos (classes e métodos) que vimos neste artigo.

Validando XML com Schemas da NF-e

No post anterior comentei como configurar os certificados para realizar consultas nos serviços da NF-e. Naquele exemplo estamos montando o XML manualmente que corresponde ao corpo da mensagem enviada ao serviço. Montar o XML manualmente é uma tarefa complicada, já que estamos sujeito à erros de digitação e, consequentemente, o serviço irá rejeitar a requisição por não estar dentro do formato esperado.

Mas qual é esse formato? Felizmente temos schemas (arquivos XSD) que são fornecidos pelo governo para que possamos validar as mensagens localmente antes de fazermos a requisição. O .NET Framework já fornece classe nativas para tal validação, e tudo o que precisamos fazer, é submeter o XML (sendo um arquivo ou até mesmo uma string) para que a validação possa ser realizada.

É através da classe XmlReaderSettings que devemos configurar o arquivo XSD a ser utilizado e o namespace que será utilizado para varrer o XML e validá-lo. Apesar de informar apenas um arquivo XSD, internamente ele referencia outros schemas que o complementam, e é importante que eles existam fisicamente no mesmo local para que a validação possa ser realizada. 

Esta classe também fornece um evento chamado ValidationEventHandler, que podemos assinar e recebermos as notificações dos problemas que ocorrerem durante a validação. Com os argumentos deste evento, podemos extrair a severidade e mensagem do erro. O código abaixo ilustra a criação e configuração desta classe:

var config = new XmlReaderSettings()
{
    ValidationType = ValidationType.Schema
};

config.Schemas.Add("http://www.portalfiscal.inf.br/nfe", "consStatServ_v3.10.xsd");
config.ValidationEventHandler += (s, e) =>
{
    Console.WriteLine("{0}: {1}", e.Severity, e.Message);
};

Depois dela devidamente configurada, informamos a mesma durante a criação do XmlReader. No exemplo abaixo, estou optando por validar o conteúdo XML que está em uma string, mas nada impede do conteúdo vir de um arquivo no disco.

var mensagem = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
            "<consStatServ xmlns=\"http://www.portalfiscal.inf.br/nfe\" versao=\"3.10\">" +
            "<tpAmb>1</tpAmb>" +
            "<cUFx>35</cUFx>" +
            "<xServ>STATUS</xServ>" +
            "</consStatServ>";

using (var reader = XmlReader.Create(new StringReader(mensagem), config))
    while (reader.Read()) { }

Propositalmente eu alterei o elemento cUF para cUFx, que é um elemento que não é suportado pelo XSD. Ao rodar a aplicação, a mensagem abaixo é exibida indicando o problema:

Error: The element 'consStatServ' in namespace 'http://www.portalfiscal.inf.br/n
fe' has invalid child element 'cUFx' in namespace 'http://www.portalfiscal.inf.b
r/nfe'. List of possible elements expected: 'cUF' in namespace 'http://www.porta
lfiscal.inf.br/nfe'.

Consumindo Serviços da NF-e com WCF

Todas as empresas, quando vendem uma mercadoria são obrigadas a emitir nota fiscal. Até pouco tempo atrás, as notas não eram padronizadas e cada empresa utilizava em modelo diferente. O governo decidiu então criar um mecanismo para que fosse possível a emissão de forma eletrônica.

Com isso cada estado brasileiro criou uma porção de web services que permite a emissão, autorização, cancelamento, consulta de status, etc. Como é possível perceber nas URLs dos serviços, eles utilizam o ASP.NET Web Services (ASMX) para expor estas funcionalidades.

Como sabemos, o WCF é o pilar de comunicação dentro do .NET Framework, e mantém compatibilidade com os antigos serviços desenvolvidos em ASMX. Isso quer dizer que é possível consumir estes serviços com a API do WCF. Para que isso seja possível, alguns cuidados precisam ser tomados para garantir que o acesso será permitido.

Os serviços são protegidos e para acessá-los é necessário apresentar um certificado digital válido. Os certificados podem ser adquiridos por entidades certificadores, tais como Serasa, Certsign, VeriSign, etc. E vale dizer que até mesmo o WSDL, documento que descreve as funcionalidades do serviço, também está protegido, e precisamos também apresentar um certificado para simplesmente adicionar a referência no Visual Studio.

Vale lembrar que existem alguns tipos de certificados, e no meu caso, estou utilizando um certifcado de cartão A3 (e-CNPJ) que está protegido por um PIN (senha) que é necessário informá-la sempre que o certificado está prestes a ser utilizado. Como estou com ele devidamente instalado, ao acessar a URL do serviço no navegador, já aparece uma lista com os possíveis certificados, assim como podemos notar na imagem abaixo. Uma vez que o certificado é escolhido, então a senha será exigida e, consequentemente, teremos acesso à página padrão do ASP.NET que descreve o serviço.



Da mesma forma que acontece acima, o Visual Studio também exigirá o certificado ao realizar o "Add Service Reference", e informando corretamente, o download do WSDL será realizado e a referência para o serviço será configurada. Atente-se aqui que não estamos utilizando a opção "Add Web Reference", pois ela recorre a API antiga (SoapHttpClientProtocol) para acesso e não utiliza o WCF.

Apesar da ferramenta já criar uma configuração padrão, ela não é válida para o acesso. Precisamos ajustar para informar ao WCF que ele precisa utilizar um certificado antes de fazer a requisição ao serviço. Estamos optando por utilizar um binding customizado para que seja possível adequar a segurança e a serialização de acordo com as exigências da SEFAZ.

Entre as customizações, temos que utilizar a versão 1.2 do SOAP. Além disso, informar que o transporte será baseado em HTTPS e que é necessário um certificado do cliente para apresentar ao serviço para ter acesso ao recurso. A configuração do certificado é feita através de um behavior e associado ao endpoint do serviço. Abaixo temos o resumo do arquivo de configuração válido para realizar o acesso. Por questões de simplicidade, estou utilizando o serviço que retorna o status (operante ou não) dos demais serviços da SEFAZ.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="NFe">
          <textMessageEncoding messageVersion="Soap12" />
          <httpsTransport requireClientCertificate="true" />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="NFe">
          <clientCredentials>
            <clientCertificate storeName="My"
                               storeLocation="CurrentUser"
                               x509FindType="FindBySubjectName"
                               findValue="ISRAEL AECE ME:........."/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint address="https://nfe.fazenda.sp.gov.br/ws/nfestatusservico2.asmx"
                binding="customBinding"
                bindingConfiguration="NFe"
                behaviorConfiguration="NFe"
                contract="ServiceReference1.NfeStatusServico2Soap12"
                name="ServiceReference1.NfeStatusServico2Soap12" />
    </client>
  </system.serviceModel>
</configuration>

Uma vez que a configuração está concluída, podemos acessar o serviço e visualizarmos o retorno. Note que o código C# nada sabe sobre as configurações de certificado. É importante ressaltar que como este certificado tem um PIN, um prompt irá aparecer automaticamente para a digitação do mesmo. Na sequencia é possível visualizar o resultado que foi devolvido pelo serviço.

using (var client = new NfeStatusServico2Soap12Client())
{
    var corpo = new XmlDocument();
    var mensagem = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
                "<consStatServ xmlns=\"http://www.portalfiscal.inf.br/nfe\" versao=\"3.10\">" +
                "<tpAmb>1</tpAmb>" +
                "<cUF>35</cUF>" +
                "<xServ>STATUS</xServ>" +
                "</consStatServ>";

    corpo.LoadXml(mensagem);

    var resultado = client.nfeStatusServicoNF2(new nfeStatusServicoNF2Request()
    {
        nfeCabecMsg = new nfeCabecMsg()
        {
            cUF = "35",
            versaoDados = "3.10"
        },
        nfeDadosMsg = corpo
    });

    Console.WriteLine(resultado.nfeStatusServicoNF2Result.OuterXml);
}

<retConsStatServ versao="3.10" xmlns="http://www.portalfiscal.inf.br/nfe"><tpAmb
>1</tpAmb><verAplic>SP_NFE_PL_008f</verAplic><cStat>107</cStat><xMotivo>Serviço
em Operaçao</xMotivo><cUF>35</cUF><dhRecbto>2015-03-19T08:31:31-03:00</dhRecbto>
<tMed>1</tMed></retConsStatServ>

Protegendo Formulários com reCaptcha

É comum termos formulários disponíveis em áreas públicas de sites. Esses formulários servem para que os usuários possam, geralmente, realizar cadastros, assinar newsletters, etc. Por estar público me refiro à não ser necessário se identificar para o site (login se senha) para poder ter acesso aquele recurso, e como já era de se esperar, alguém de má fé (ou não) pode começar a criar robôs para realizar diversas requisições de forma automática ao site, simulando ser um usuário e, consequentemente, tendo acesso ao recurso que é oferecido após o preenchimento do formulário.

Desconsiderando quem utiliza de má fé, quando clientes começam a desenvolver robôs para acessar um determinado recurso, é provável que seja o momento de criar algum serviço (API) para consumo direto por outros sistemas. Isso vai ao contrário do escopo deste artigo, ou seja, veremos aqui como fazer uso do reCAPTCHA, que é uma tecnologia oferecida gratuitamente pelo Google para proteger aplicações Web de robôs que tentam se passar por pessoas para acessar os recursos.

O primeiro passo para fazer uso, é cadastrar a aplicação (necessário uma conta do Google para isso). Ao fazer isso, um par de chaves será criado para fazermos uso em nossa aplicação. Abaixo está as configurações da aplicação de criei para o teste. A primeira chave (Site Key) é embutida no próprio HTML da aplicação, enquanto a segunda (Secret Key) deve ser mantida em sigilo e será utilizada para validar a resposta que o usuário informou, e isso será feito através do C# neste exemplo.



Para exibir o controle que faz toda a inteligência, basta adicionarmos a referência para um arquivo Javascript e adicionar um DIV no HTML, posicionando-o no local da tela que deseja que ele seja exibido. Repare que há um atributo chamado data-sitekey, que é onde colocaremos o Site Key gerado para a nossa aplicação. O HTML gerado deve ficar parecido com o código abaixo (estou omitindo o restante por questões de espaço e relevância):

<html>
<head>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
    <form>
        <div class="g-recaptcha" data-sitekey="6LfJq...........66s"></div>
 </form>
</body>

Ao executar a aplicação, teremos o formulário como vemos abaixo. Ao clicar no checkbox, uma pequena janela se abre para informar o texto que é apresentado. Quando você digita e clica no botão Confirmar, o componente gera um novo campo chamado g-recaptcha-response, que será submetido para o servidor e lá é onde iremos verificar junto ao Google se o que foi digitado está correto ou não. As imagens abaixo ilustram as três etapas antes de submeter o formulário para o servidor:





Agora chega o momento de validarmos o que foi digitado pelo usuário do lado do servidor. Essa validação garantirá que os dados digitados são válidos e correspondem a chave gerada para a nossa aplicação. O Google fornece uma URL que permite fazermos uma requisição (GET), passando alguns parâmetros para a validação, sendo eles: secret, response e remoteip. O primeiro é a chave privada (Secret Key) que foi gerada para nosso site; o segundo parâmetro, response, é todo o conteúdo postado no campo g-recaptcha-response e, finalmente, o terceiro parâmetro é o endereço IP do cliente, mas trata-se de um parâmetro opcional.

Para realizar esta requisição, vamos utilizar a classe HttpClient de forma assíncrona para termos uma melhor performance no servidor. O resultado, codificado em JSON, pode ser convertido em um objeto para manipular de forma mais intuitiva no C#.

Basicamente estamos gerando a URL de acordo com os parâmetros exigidos, e através de uma requisição GET, vamos até o Google para validação do valor informado pelo usuário. Se o resultado for positivo (success = true), então seguimos adiante para armazenar o e-mail em nossa base de dados. Por questões de simplicidade, não estou recuperando os possíveis erros que podem acontecer durante a validação pelo Google, mas a resposta da requisição também contempla uma coleção chamada error-codes, que como o próprio nome diz, retorna os erros encontrados.

public class NewsletterController : Controller
{
    private const string ChaveSecreta = "6LfJqw.............06U1uL";
    private const string UrlDeValidacao =
        "https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}&remoteip={2}";

    [HttpPost]
    public async Task<ActionResult> Assinar(FormCollection fc)
    {
        var email = fc["email"];
        var respostaDoUsuario = fc["g-recaptcha-response"];
        var houveFalha = !(await ValidarCaptcha(respostaDoUsuario));

        if (!houveFalha)
        {
            //Incluir E-mail na Base de Dados
        }

        ViewBag.HouveFalha = houveFalha;
        return View();
    }

    private async Task<bool> ValidarCaptcha(string respostaDoUsuario)
    {
        var url =
            string.Format(UrlDeValidacao, ChaveSecreta, respostaDoUsuario, Request.UserHostAddress);

        using (var cliente = new HttpClient())
        {
            var resposta = await cliente.GetAsync(url);
            var resultado = await resposta.Content.ReadAsAsync<Resultado>();

            return resultado.Success;
        }
    }
}

public class Resultado
{
    public bool Success { get; set; }
}

BufferManager

O WCF faz uso internamente de uma classe chamada BufferManager. Como o próprio nome sugere, ela faz o gerenciamento de memória durante as requisições, e ao invés de ficar criando array de bytes para ler ou escrever uma mensagem, o BufferManager gerencia um pool de buffers para fazer uma melhor reutilização quando o recurso não é mais necessário.

Quando a requisição é lida e a memória reservada para isso não é mais necessária, este gestor recicla a mesma ao invés de descartar, evitando assim ter todo o custo de criação e alocação quando uma nova requisição chegar. A não utilização de um gestor de memória pode degradar a performance do serviço, do host e, consequentemente, da máquina onde tudo isso é executado.

Os bindings já fornecem propriedades para criar e gerenciar o BufferManager, a saber: MaxBufferPoolSize e MaxBufferSize. A primeira indica a quantidade máxima de memória (em bytes) que ele terá, enquanto a segunda, indicará o máximo de memória que cada item dentro do pool poderá ter, também em bytes. É importante dizer que a propriedade MaxBufferSize deve ter o mesmo valor da propriedade MaxReceivedMessageSize.

Engana-se quem acha que esta classe só traz benefícios. Se ela for mal configurada, a memória pode não ser devolvida/reciclada, e em pouco tempo, esgotará os recursos disponíveis na máquina. O algoritmo utilizado por esta classe, cria os pools de acordo com o tamanho das mensagens que precisam ser manipuladas, e se não havendo nenhum pool que atenda a mesma ou a mensagem é maior que o valor estipulado na configuração, o WCF aloca memória diretamente na HEAP, ou seja, não gerenciada pelo BufferManager.

Se isso ocorrer com frequência, ao longo do tempo os recursos estarão esgotados e as mensagens não serão mais processadas. É comum ver cenários onde todos as configurações são definidas com seus valores máximos (int.MaxValue), e isso poderá impactar consideravelmente o serviço. É importante dimensionar as configurações de acordo com a necessidade de cada serviço, baseando-se, principalmente, na demanda e tamanho das mensagens que chegam até ele.

Há uma forma de informar ao runtime do WCF para não utilizer o pool de buffers. Para isso basta configurar a propriedade MaxBufferPoolSize com o valor 0 (zero). Isso fará com que o WCF crie um buffer para cada requisição diretamente na memória HEAP, e fica a cargo do Garbage Collector gerenciar o seu tempo de vida. Para confirmar isso, no interior da classe BufferManager, se o valor informado for zero, a classe GCBufferManager é criada, e caso contrário, a classe PooledBufferManager é utilizada.

Se criarmos um pequeno exemplo de um serviço que retorna uma coleção de objetos (5000), podemos perceber no resumo do profiler que foi aplicado à execução, uma diferença significativa da quantidade de bytes que foram alocados quando utilizamos a propriedade MaxBufferPoolSize com valor diferente de zero e com valor igual a zero.



O buffer é útil quando falamos de mensagens pequenas. Quando se tratam de mensagens que ultrapassam 64KB, opte por não utilizar este recurso.

Validando a Assinatura Digital da NF-e

Atualmente as empresas emitem as notas fiscais em formato eletrônico. Para isso, foi desenvolvido um projeto que especifica e rege a emissão destas notas em ambiente nacional, que é conhecido como Nota Fiscal Eletrônica ou, simplesmente, NF-e. Este projeto faz uso de diversas tecnologias para garantir a segurança e, principalmente, a integridade da mesma.

A integridade é a garantia de que o arquivo XML que a representa não tenha sido alterado desde a sua emissão. Quando uma empresa emite uma nota fiscal, a SEFAZ gera um arquivo XML digitalmente assinado, utilizando o certificado digital da empresa emitente. Como sabemos, esse arquivo XML é a nota fiscal em formato eletrônico, e essa assinatura está embutida nele. A assinatura digital nada mais é que um cálculo matemático que é realizado sobre todo o arquivo XML, e se qualquer mudança ocorrer neste depois da assinatura, a mesma se tornará inválida.

Se você recebe este arquivo e quer garantir que ele não tenha sido alterado, então podemos recorrer à classes que estão dentro do .NET Framework para realizar esta validação. Basicamente devemos carregar o conteúdo XML para o objeto XmlDocument, e em seguida, procurar pelo elemento <Signature /> que é armazena o hash calculado e o certificado serializado que foi utilizado para realizar a assinatura.

var xml = new XmlDocument();
xml.Load("00000000000000000000000000000000000000000000.xml");

var nfe = (XmlElement)xml.GetElementsByTagName("NFe")[0];
var signature = (XmlElement)nfe.GetElementsByTagName("Signature")[0];
var signer = new SignedXml();

signer.LoadXml(signature);
Console.WriteLine(signer.CheckSignature());

Depois de carregado e separado os elementos que serão avaliados, recorrermos a classe SignedXml para validar a assinatura, onde devemos submeter o elemento que contém a assinatura para o método LoadXml, e em seguida, invocar o método CheckSignature, que retorna um valor boleano indicando se ela está ou não válida.

O overload do método CheckSignature utilizado acima (sem parâmetros) analisa o elemento <Signature /> tentando extrair os certificados de forma automática. Caso queira ter um maior controle sobre os certificados utilizados para analisar a assinatura, podemos utilizar um código mais rebuscado para atingir este objetivo:

private static bool ValidateWithCertificate(SignedXml signer)
{
    var keyInfoEnumerator = signer.KeyInfo.GetEnumerator();

    while (keyInfoEnumerator.MoveNext())
    {
        var x509Data = keyInfoEnumerator.Current as KeyInfoX509Data;

        if (x509Data != null && x509Data.Certificates.Count > 0)
            foreach (var cert in x509Data.Certificates)
                if (signer.CheckSignature((X509Certificate2)cert, true))
                    return true;
    }

    return false;
}

Como o próprio nome sugere, a propriedade KeyInfo contém informações referentes à chave que foi utilizada para realizar a assinatura. E, utilizando um enumerador, identificamos se trata-se de um certificado, e ao iterar pela coleção, invocamos o método CheckSignature, e através de um segundo overload, passamos o certificado que queremos que ele considere para realizer a validação. Apesar de ter o mesmo resultado, aqui é possível extrair informações do certificado utilizado, caso seja necessário.

Importante: um erro bastante comum é que alguns sistemas incluem novas informações no XML depois que ele foi assinado. Ao fazer isso, a assinatura digital é corrompida, e ao submetermos o XML para o código que escrevemos acima, a assinatura será dada como inválida. Entre essas informações, é comum ver namespaces como os que são mostrados abaixo (em negrito), e segundo a Nota Técnica 2010/009 da SEFAZ, eles não devem ser incluídos, nem antes, e muito menos depois, da assinatura.

<?xml version="1.0" encoding="UTF-8"?>
<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe"
         xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         versao="2.00">
  <NFe xmlns="http://www.portalfiscal.inf.br/nfe">
    <infNFe Id="NFe..." versao="2.00">

Mantendo objetos durante a requisição

Quando a requisição chega em um determinado serviço ela tem como alvo executar uma determinada tarefa e devolver um resultado. Antes da requisição chegar ao seu destino, ela passa por algumas etapas, e depois que a tarefa é de fato executada, outras etapas também são executadas antes de devolver a resposta para o cliente.

A manutenção de estado de objetos muitas vezes se faz necessária, pois ele servirá como uma espécie de contexto que viverá do início ao fim da requisição. Um exemplo comum disso é quando temos um objeto que gerencia uma transação (Unit of Work), e com ele seria possível envolver tudo o que acontece na requisição em um mesmo contexto transacional, pois se no fim do processamento alguma falha ocorrer, será possível reverter tudo o que fora realizado até ali.

Como sabemos, a ação dentro do controller de fato é a tarefa que queremos executar, mas o que ocorre antes e depois (geralmente são atividades de cross-cutting) também pode ser envolvido para contextualizar todo o processo. Este artigo exemplifica em como implementar e gerenciar isso no ASP.NET Web API. Para o exemplo, vamos ter um recurso que interceptará vários estágios da requisição, e logará todos os passos por onde ela passou. A interface IRequestTracking servirá como base para qualquer tipo de rastreador de requisições. A classe que está a seguir (MemoryRequestTracking) é uma implementação dela, que basicamente armazena os passos em uma coleção e persiste no Trace.

[InheritedExport]
public interface IRequestTracking
{
    Guid RequestId { get; }

    IEnumerable<string> Steps { get; }

    void AddStep(string message);

    void Flush();
}

public class MemoryRequestTracking : IRequestTracking
{
    private readonly IList<string> steps;

    public MemoryRequestTracking()
    {
        this.RequestId = Guid.NewGuid();
        this.steps = new List<string>();
    }

    public void AddStep(string message)
    {
        this.steps.Add(
            string.Format("{0:dd/MM/yyyy HH:mm:ss} - Id: {1} - {2}",
                DateTime.Now, this.RequestId, message));
    }

    public Guid RequestId { get; private set; }

    public IEnumerable<string> Steps { get { return this.steps; } }

    public void Flush()
    {
        foreach (var step in this.Steps)
            Trace.WriteLine(step);
    }
}

O ASP.NET Web API já traz nativamente uma espécie de repositório de dependências, que durante o runtime, a medida em que recursos vão sendo solicitados, esse repositório é consultado para resolver a(s) dependência(s), retornando uma implementação concreta do recurso solicitado. Felizmente podemos customizar esse repositório e utilizar algum container de injeção de dependências (DI) de sua preferência para auxiliar no gerenciamento e na criação das instâncias.

A customização resume em se criar uma classe que implemente a interface IDependencyResolver, e através dos métodos autoexplicativos, retornamos os recursos solicitados. É no interior desta classe que, eventualmente, podemos utilizar um container de DI, e para este exemplo estou utilizando o MEF (Managed Extensibility Framework).

public class MefResolver : IDependencyResolver
{
    private readonly CompositionContainer container;

    public MefResolver()
        : this(new CompositionContainer(
            new AssemblyCatalog(Assembly.GetExecutingAssembly()))) { }

    public MefResolver(CompositionContainer container) { this.container = container; }

    public IDependencyScope BeginScope() { return new MefResolver(); }

    public object GetService(Type serviceType)
    {
        return GetServices(serviceType).FirstOrDefault();
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        var services = this.container.GetExports(serviceType, null, null);

        return !services.Any() ? Enumerable.Empty<object>() : services.Select(s => s.Value);
    }

    public void Dispose() { this.container.Dispose(); }
}

Basicamente a classe acima recebe em seu construtor o container que servirá como "base de dados" das dependências e resolverá as solicitações procurando por implementações dentro do próprio assembly que está em execução. Só que a classe por si só não funcionará, pois precisamos acoplá-la à execução, e para isso, devemos configurar definer a instância dela na propriedade DependencyResolver exposta pela classe HttpConfiguration, assim como é mostrado abaixo:

public static void Register(HttpConfiguration config)
{
    config.DependencyResolver = new MefResolver();

    //outras configurações
}

Agora que toda a configuração já está realizada, precisamos começar a adicionar os passos nos locais que desejarmos interceptar. Como falei anteriormente, vamos querer acessar o rastreador em várias etapas diferentes, e felizmente, o ASP.NET Web API e seu container de DI são capazes de criar e disponibilizar a instância quando precisarmos, e isso quer dizer que poderemos acessar esses recursos não só no interior do controller, mas também em filtros, handlers, formatters, etc.

O gestor de dependência que temos (MefResolver) é construído no ínicio da requisição e é adicionado às propriedades da classe HttpRequestMessage, que nada mais é do que um dicionário de dados e mantém uma relação de objetos que são utilizados durante a requisição. Como o gestor de dependência é criado para cada requisição, então os objetos que são criados a partir dele também serão mantidos.

O primeiro lugar que vamos acessar é dentro de um filtro customizado. Note que a classe base ActionFilterAttribute fornece métodos para interceptar o antes e depois da ação executada.

public class RequestTrackingFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        ((IRequestTracking)actionContext
            .Request
            .GetDependencyScope()
            .GetService(typeof(IRequestTracking)))
            .AddStep("OnActionExecuting");

        base.OnActionExecuting(actionContext);
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        ((IRequestTracking)actionExecutedContext
            .Request
            .GetDependencyScope()
            .GetService(typeof(IRequestTracking)))
            .AddStep("OnActionExecuted");

        base.OnActionExecuted(actionExecutedContext);
    }
}

Graças à um método de extensão chamado GetDependencyScope atribuído a classe HttpRequestMessage, é fácil chegarmos à esse gestor e solicitar o recurso que queremos. Para expressar o recurso que queremos, basta informar a interface IRequestTracking, que ele devolverá a instância criada ou criará uma nova caso ela ainda não exista. Depois basta fazer a conversão explícita (casting) e acessar os seus membros.

Já dentro do controller, a forma de chegar até este recurso é bem semelhante. Note que no código abaixo temos o rastreador declarado como um campo na classe, e como ele está decorado com o atributo ImportAttribute (do MEF), o container resolve a dependência e injeta a instância nesta classe, neste campo, assim que ela for construída. Com isso, basta utilizar o mesmo no interior da ação/controller.

[Export]
public class TesteController : ApiController
{
    [Import]
    private IRequestTracking tracking;

    [HttpGet]
    [RequestTrackingFilter]
    public string Ping(string valor)
    {
        tracking.AddStep(string.Format("TesteController.Ping({0})", valor));

        return valor + " ping";
    }
}

É importante notar que o atributo criado anteriormente é decorado no méotdo Ping, e é a presença dele que faz com o que o runtime do ASP.NET o execute.

Além destas opções, podemos também acessar e utilizar este rastreador, por exemplo, dentro de um message handler. Além de utilizar para também adicionar mais um passo ao rastreador, é também o momento de invocar o método Flush para que o conteúdo armazenado até o momento seja definitivamente adicionado no arquivo de trace, previamente configurado. Como podemos notar no código abaixo, fazemos todo este trabalho depois que a requisição foi processada por todos os componentes (de mais baixo nível), incluindo a ação que está dentro do controller. O flush é feito momentos antes de retornar a resposta para o cliente.

public class FlushingRequestTrackingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return await base.SendAsync(request, cancellationToken).ContinueWith(r =>
        {
            var tracking =
                ((IRequestTracking)request
                    .GetDependencyScope()
                    .GetService(typeof(IRequestTracking)));

            tracking.AddStep("FRTH.SendAsync");
            tracking.Flush();

            return r.Result;
        });
    }
}

E, ao rodar e visualizarmos o arquivo de tracing, teremos:

13/11/2014 15:44:34 - Id: 724b2a97-3bcf-428b-a65e-5ec582bb1f70 - OnActionExecuting
13/11/2014 15:44:34 - Id: 724b2a97-3bcf-428b-a65e-5ec582bb1f70 - TesteController.Ping(teste)
13/11/2014 15:44:34 - Id: 724b2a97-3bcf-428b-a65e-5ec582bb1f70 - OnActionExecuted
13/11/2014 15:44:34 - Id: 724b2a97-3bcf-428b-a65e-5ec582bb1f70 - FRTH.SendAsync

Importante: Apesar de tentador, é necessário tomar cuidado ao acessar o gestor de dependências através da propriedade fornecida pela GlobalConfiguration.Configuration.DependencyResolver. O problema é que esta propriedade sempre retornará uma nova instância do gestor (no nosso caso, do MefResolver) e não teremos os objetos sendo mantidos por toda a requisição.

Cancelando Requisições do HttpClient

Como sabemos, a classe HttpClient é uma espécie de proxy para o consumo de serviços REST a partir de aplicações .NET. Durante a sua criação é possível realizar diversas configurações, e as requisições que saem e chegam, compartilham estas configurações, evitando que se faça isso a cada nova requisição/resposta que é enviada/processada.

Entre os diversos métodos que esta classe fornece, o mais completo, ou melhor, "de mais baixo nível", é o SendAsync, que onde podemos (e devemos) configurar completamente a mensagem, especificando desde a URL até o método HTTP que será utilizado. Como facilitador, existem alguns métodos que foram criados sobre o método SendAsync, e foram nomeados utilizandos os verbos do HTTP (GET (GetAsync), POST (PostAsync), etc.).

A classe HttpClient fornece um método chamado CancelPendingRequests, que como o próprio nome sugere, solicitará o cancelamento de todas as requisições que estão sendo realizadas pela respectiva instância do HttpClient. Mas é provável que queremos ter um controle individual das requisições e conseguirmos cancelar especificamente uma delas.

Felizmente todos os métodos que iniciam uma nova requisição possuem um parâmetro onde é permitido controlar e, principalmente, cancelar a execução da mesma. Eles fazem uso de uma classe chamada CancellationTokenSource, que faz parte do .NET Framework, e possibilita ao chamador informar ao runtime que deseja cancelar a requisição que está em andamento.

Para exemplificar o seu uso, vamos utilizar uma aplicação WPF para iniciará a requisição, e enquanto ela estiver sendo executada, vamos dar a possibilidade ao usuário dele conseguir cancelar a mesma. Note no código abaixo que criamos em nível da classe um membro chamado status, qual será o responsável por gerenciar um eventual cancelamento. No clique do botão Requisitar, estamos recorrendo ao método GetAsync (chamando-o de forma assíncrona) e passamos o CancellationTokenSource. É importante notar que temos que envolver este código em um bloco try/catch para interceptar a exceção do tipo TaskCanceledException, que será disparada quando o cancelamento for efetivamente realizado.

public partial class MainWindow : Window
{
    private CancellationTokenSource status;
    private HttpClient proxy = new HttpClient();

    private async void Requisitar_Click(object sender, RoutedEventArgs e)
    {
        Reinicializar();

        try
        {
            var resposta = await proxy.GetAsync(Url.Text, status.Token);

            if (resposta.IsSuccessStatusCode)
                Conteudo.Text = await resposta.Content.ReadAsStringAsync();
        }
        catch (TaskCanceledException)
        {
            Conteudo.Foreground = Brushes.Red;
            Conteudo.Text = "*** A REQUISIÇÃO FOI CANCELADA ***";
        }
    }

    private void Reinicializar()
    {
        Conteudo.Text = null;
        Conteudo.Foreground = Brushes.Black;

        status = new CancellationTokenSource();
    }

    private void Cancelar_Click(object sender, RoutedEventArgs e)
    {
        status.Cancel();
    }
}

Agora o cancelamento pode ser solicitado ao clicar no botão Cancelar, e através do método Cancel da classe CancellationTokenSource, o código para o qual passamos este token (que foi o GetAsync) identifica a solicitação e cancela a execução, disparando a exceção que falamos acima. As imagens abaixo ilustram tanto a requisição sendo executada com sucesso quanto um cancelamento sendo solicitado e acatado, abortando o processamento.