Interface IApplicationLifetime

Desde o ASP clássico nós temos um arquivo chamado Global.asa. Com o ASP.NET, este arquivo foi renomeado para Global.asax. A finalidade deste arquivo é fornecer uma forma de interceptar eventos que ocorrem no nível da aplicação. Entre estes eventos, nós temos um que é disparado indicando que a aplicação foi inicializada, outro que algum erro não tratado aconteceu, que uma nova requisição chegou, que a aplicação está sendo encerrada, etc.

Este arquivo já não existe mais no ASP.NET Core, porém a necessidade ainda existe em alguns tipos de aplicação. Para interceptar a inicialização da aplicação e o término dela, podemos recorrer à interface IApplicationLifetime. Esta interface fornece três propriedades que indica a inicialização da aplicação (ApplicationStarted), que ela está sendo encerrada (ApplicationStopping) e depois de encerrada (ApplicationStopped). Essas propriedades são do tipo CancellationToken, e através de um delegate do tipo Action, passamos para o método Register o código customizado que queremos executar quando estes eventos acontecerem.

Para exemplificar o uso e utilidade desta interface, considere o exemplo abaixo onde temos uma classe estática que gerencia o agendamento e execução de tarefas pelo Quartz.NET. Além do método Schedule, temos os métodos Start e Stop, que como já podemos perceber, queremos invocar cada um deles no início e no fim da aplicação.

public class TaskManager
{
    public static void Start()
    {
        Scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
        Scheduler.Start();
    }

    private static IScheduler Scheduler { get; set; }

    public async static Task Schedule<TJob>(IDictionary data) where TJob : IJob
    {
        await Scheduler.ScheduleJob(
            JobBuilder
                .Create<TJob>()
                .SetJobData(new JobDataMap(data))
                .Build(),
            TriggerBuilder
                .Create()
                .WithSimpleSchedule(x => x.WithRepeatCount(0))
                .Build());
    }

    public static void Shutdown()
    {
        Scheduler?.Shutdown(true);
    }
}

O método Start deverá ser chamado quando a aplicação estiver sendo inicializada. Já no momento, do encerramento, recorremos ao método Shutdown do Schedule, passando o parâmetro true, que indica que antes de encerrar o gerenciador de tarefas, é necessário aguardar o término de alguma que, eventualmente, ainda esteja em execução. A interface IApplicationLifetime, assim como diversas outras do ASP.NET Core, o próprio runtime cria a instância de uma implementação padrão e passa para o método Configure da nossa classe Startup. Com isso, basta associarmos os métodos aos respectivos eventos, conforme é possível visualizar abaixo:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    ILoggerFactory log, 
    IApplicationLifetime lifetime)
{
    lifetime.ApplicationStarted.Register(() => TaskManager.Start());
    lifetime.ApplicationStopped.Register(() => TaskManager.Shutdown());

    //outras configurações
}

Por fim, a aplicação (os controllers) interagem com a classe TaskManager a partir do método Schedule<TJob>, indicando qual a tarefa que precisa ser executada e parâmetros contextuais que queremos passar para a implementação da mesma.

[HttpPost]
public async Task<IActionResult> AgendarExecucao()
{
    await TaskManager
        .Schedule<GeracaoDeDados>(
            Request.Form.ToDictionary(x => x.Key, x => x.Value.ToString()));

    return View("ExecucaoAgendada");
}

Segregando Requisições no ASP.NET

No ASP.NET Core temos o conceito de middlewares, que nada mais são que plugins que podemos acoplar à execução para que eles sejam executados a medida em que uma nova requisição chega para o serviço ou para o site. Além de vários que já foram criados pela Microsoft, a criação destes middlewares é extremamente simples, e é a principal forma que temos de interceptar o momento antes e/ou depois das requisições.

Durante a inicialização da aplicação, devemos realizar as configurações necessárias para que ela funcione, e é neste momento que montamos o pipeline por onde cada uma delas deverá passar, e isso quer dizer que faremos uso constante de middlewares, mesmo que não se esteja explicitamente claro no código que vamos escrever. A execução ocorre de forma linear, onde a requisição é entregue para o primeiro middleware e este é responsável por fazer alguma atividade e passar adiante a requisição (leia-se para o middleware interno). Abaixo temos um exemplo de um simples middleware; note que temos algumas convenções que precisam ser respeitadas, tais como: receber no construtor o próximo middleware, ter o método chamado Invoke que será disparado pelo ASP.NET no momento adequado. É dentro deste método que você define quando quer invocar o próximo middleware.

public class LoggingMiddleware
{
    private readonly RequestDelegate next;

    public LoggingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        //Ação Antes
        await next(context);
        //Ação Depois
    }
}

O problema aqui é que podemos hospedar na mesma aplicação dois conteúdos que demandam diferentes rotinas e características. É comum encontrarmos aplicações que expõe uma API web, e na mesma aplicação, fornece o acesso à um pequeno site com a documentação, tutoriais, central de suporte para a mesma. Do lado da API, é comum termos log de acesso, telemetria, controle de transações, etc., enquanto do “lado do UI” da aplicação, necessitamos de outros recursos, como a exposição de conteúdos estáticos e geração de HTML.

O ASP.NET possibilita uma forma de segregar as requisições (branch), roteando cada uma delas de acordo com um critério e, que se atendido, podemos customizar um pipeline de middlewares exclusivo para aquele cenário. Isso evita ter um único pipeline com todos os passos e ter que dar by-pass de acordo com a requisição que estamos processando naquele momento exige. Para isso, podemos recorrer ao método Map da interface IApplicationBuilder. Em sua versão mais simplista, ele aceita uma string que permite especificarmos a URL que, se atendida, deverá ser executada utilizando a configuração que especificamos no segundo parâmetro deste mesmo método. Há uma variação deste método chamado MapWhen, que nos permite inspecionar outros itens da requisição (headers, por exemplo) para tomar a decisão se deve ou não ser processada por aquele pipeline.

No exemplo abaixo, se a requisição conter /commands na URL, então o middleware de logging será disparado, na sequência o de transações, e por fim, a ação do controller será executada. Já quando a requisição for para /help, estamos permitindo o acesso à arquivos estáticos (imagens, PDFs, etc.) e também o uso do MVC para renderizar HTML com o conteúdo da documentação da API:

public void Configure(IApplicationBuilder app)
{
    app.Map("/commands",
        config =>
            config
                .UseMiddleware<LoggingMiddleware>()
                .UseMiddleware<TransactionMiddleware>()
                .UseMvc());

    app.Map("/help", 
        config => 
            config
                .UseMvc()
                .UseStaticFiles());
}

Se desejar ter acesso ao código completo deste artigo, consulte este endereço.

Middlewares para Comandos

Quando optamos por separar a leitura da escrita em nossa aplicação, é comum adotarmos o padrão CQRS. Entre os vários conceitos que temos, um deles é a criação de comandos, que nada mais são do que classes que expressam a intenção do usuário em alterar, criar ou excluir alguma informação. O comando é definido através de uma classe, que traz informações (dados) sobre a ação a ser executada. Ele é passado para o que chamamos de command handler, que é o tratador deste comando, ou seja, ele coordena todas as ações sobre aquela entidade. E, da mesma forma, também é implementada através de uma classe (apesar de haver algumas variações). Em geral, a relação um tratador para cada comando.

O código abaixo exibe um exemplo simples disso. A classe Command não traz qualquer informação adicional, é apenas uma convenção; já a interface IHandler<T> define a estrutura que todo tratador de comando deverá ter, ou seja, o método Handle. É no interior deste método que recorremos ao repositório para adicionar/remover a entidade, manipular, etc.

public class CadastrarUsuario : Command
{
    public string Nome { get; set; }
}

public class CadastrarUsuarioHandler : IHandler<CadastrarUsuario>
{
    public CadastrarUsuarioHandler(IRepositorioDeUsuarios repositorio) { }

    public void Handle(CadastrarUsuario message)
    {
        Console.WriteLine("Cadastrando o usuário " + message.Nome);
    }
}

Para manter a simplicidade, este código está apenas escrevendo o nome do usuário na tela. Em um ambiente real, é provável termos que incrementar isso. Para citar alguns exemplos, é comum ter a necessidade de realizar logs, mensurar o tempo gasto na operação, proteger as ações através de uma transação, etc. A partir do momento que começarmos a incorporar essas atividades em nosso tratador, ele começará a ficar poluído, e o pior, começa a ter mais de uma responsabilidade além daquela para qual ele foi criado. Note o mesmo tratador que temos acima, contendo estes incrementos:

public class CadastrarUsuarioHandler : IHandler<CadastrarUsuario>
{
    public CadastrarUsuarioHandler(
        ILogger logger, 
        ITimer timer, 
        IUnitOfWork transaction, 
        IRepositorioDeUsuarios repositorio) { }

    public void Handle(CadastrarUsuario message)
    {
        this.timer.Start();
        this.logger.Info("Início");

        using (this.transaction)
        {
            this.repositorio.Salvar(message.Nome);
            Console.WriteLine("Cadastrando o usuário " + message.Nome);
        }

        this.logger.Info("Fim");
        this.timer.Stop();
    }
}

Mesmo que estamos recebendo as implementações do mundo exterior, a classe CadastrarUsuarioHandler está manipulando mais coisas do que deveria. Quando criarmos um segundo comando, teremos que repetir todos estes códigos, ficando sujeito a erros e com pouca reusabilidade.

O melhor a fazermos aqui é remover todo este código de cross-cutting, deixando o tratador do comando responsável apenas por criar e armazenar o usuário na base de dados através do repositório. Todas essas outras – importantes – atividades podem ser delegadas para classes que circundam todos os tratadores da aplicação, não ficando restrito a apenas um, e a partir daí, tendo um ponto central para isso, fica fácil a manutenção e eventual ajuste na interface dos componentes, já que não precisamos sair refatorando em diversos lugares da aplicação.

Uma das técnicas para isso, é criar um pipeline de funcionalidades, onde o item mais baixo de ações é a execução do tratador do comando. Até ele chegar no tratador propriamente dito, ele passaria pelas funcionalidades de log, timer e transação, deixando o ambiente todo preparado para a execução da atividade principal. A ideia é criar middlewares, próximo ao que temos no ASP.NET Core, para que possamos acoplar ou desacoplar alguma funcionalidade quando necessário. Além da extensibilidade, temos uma separação bem definida do que cada middleware deve fazer, sem um afetar no trabalho doutro. A imagem abaixo ilustra o exemplo:

commandhandlermiddleware1

O primeiro passo é a criação da interface que irá definir a estrutura de um middleware. Basicamente ele fornecerá um método chamado Execute, que além da instância do comando a ser executado, ela também recebe o método a ser executado na sequência. Opcionalmente, você pode optar por receber o delegate da próxima ação no construtor do middleware.

public interface IMiddleware
{
    void Execute(Command command, Action<Command> next);
}

A partir daí podemos ir criando os middlewares necessários para a nossa aplicação. Abaixo temos o exemplo do TimerMiddleware, que utiliza um Stopwatch para mensurar o tempo gasto na execução do comando. É importante notar que podemos ter códigos sendo executado antes e depois da próxima ação.

public class TimerMiddleware : IMiddleware
{
    public void Execute(Command command, Action<Command> next)
    {
        var timer = Stopwatch.StartNew();
        Console.WriteLine("[INFO] - Início - {0:HH:mm:ss}", DateTime.Now);

        try
        {
            next(command);
        }
        finally
        {
            timer.Stop();
        }

        Console.WriteLine("[INFO] - Fim - {0:HH:mm:ss} - Tempo Decorrido: {1}", 
            DateTime.Now, timer.Elapsed);
    }
}

Só que este middleware por si só não funciona. Temos agora que criar uma estrutura que consiga receber os middlewares e, consequentemente, montar uma cadeia de chamadas na ordem desejada para execução. A ideia é receber todos os middlewares necessários através do construtor do pipeline, e com isso, já criar uma sequência de execução, para que ela já esteja pronta quando chegar para o pipeline um novo comando. É importante notar aqui que neste cenário, o command bus (mencionado em arquitetura CQRS) é implementado como um middleware.

public class Pipeline
{
    private Action<Command> middlewareChain;

    public Pipeline(params IMiddleware[] middlewares)
    {
        this.middlewareChain = BuildChain(middlewares.Reverse().ToArray());
    }

    private static Action<Command> BuildChain(IMiddleware[] middlewares)
    {
        Action<Command> chain = command => { };

        foreach (var middleware in middlewares)
        {
            var temp = chain;

            chain = command => middleware.Execute(command, temp);
        }

        return chain;
    }

    public virtual void Handle<T>(T command) where T : Command
    {
        this.middlewareChain(command);
    }
}

Se quiser postergar a criação da cadeia de chamada para a primeira execução, podemos declarar o campo middlewareChain como Lazy<T>. E, por fim, basta instanciarmos o pipeline de acordo com a nossa estrutura de middlewares necessária para a aplicação, e a partir daí, passar os comandos que estão sendo requisitados pelos usuários. Novamente, note que o middleware CommandHandlerMiddleware é o último da lista:

var pipeline = 
    new Pipeline(
        new TimerMiddleware(), 
        new LoggingMiddleware(), 
        new TransactionMiddleware(), 
        new CommandHandlerMiddleware());

pipeline.Handle(new CadastrarUsuario() { Nome = "Israel" });

O código na íntegra está disponível neste link.

Request Features no ASP.NET

O ASP.NET 5 está sendo construído para ser completamente independente do hosting em que ele está sendo executado, permitindo assim, que este tipo de aplicação seja também levado para outras plataformas (Linux e Mac). Atualmente o ASP.NET 5 pode ser hospedado no IIS (incluindo a versão Express), via self-host (WebListener) ou através do Kestrel (que é um servidor multi-plataforma baseado no libuv.

Mesmo que seja independente do hosting, o ASP.NET fornece um conjunto de tipos que permite interagir, de forma genérica e abstrata, com as funcionalidades que são expostas pelo HTTP, e que na maioria das vezes, são criadas e abastecidas pelo receptor da requisição, ou seja, o próprio hosting. Esses tipos são chamados de request features, e estão incluídas no pacote chamado Microsoft.AspNet.Http.Features. Entre os tipos (funcionalidades) disponíveis temos as interfaces IHttpRequestFeatureIHttpResponseFeature, que representam o básico para o funcionamento de qualquer servidor web, que é receber a requisição, processar e devolver o resultado. Um outro exemplo é a interface ITlsConnectionFeature, que pode ser utilizada para interagir com os certificados enviados pelo cliente. Para uma relação completa, consulte a documentação.

Cada um dos servidores disponíveis já implementam as interfaces definidas (desde que eles suportem), e nos permite acessar essas funcionalidades durante a execução da aplicação, incluindo a possibilidade de acesso através de qualquer middleware. A classe HttpContext expõe uma propriedade chamada Features, que nada mais é que uma coleção das funcionalidades que são incluídas (e, novamente, suportadas) pelo hosting onde a aplicação está rodando. Essa coleção fornece um método genérico chamado Get<TFeature>, onde podemos definir a funcionalidade que queremos acessar. Abaixo um trecho do código de exemplo:

public Task Invoke(HttpContext httpContext)
{
    var clientCertificate =
        httpContext.Features.Get<ITlsConnectionFeature>()?.ClientCertificate;

    if (clientCertificate != null)
    {
        //…
    }

    return _next(httpContext);
}

Além da possibilidade de extrair alguma funcionalidade, é possível também incluir funcionalidade durante a execução, e como já era de se esperar, basta utilizar o método, também genérico, Set<TFeature>. E o mais interessante é que não estamos restritos a utilizar uma das interfaces expostas pelo ASP.NET; podemos criar novas funcionalidades e incorporá-las na execução.

Considere o exemplo abaixo, onde criamos uma funcionalidade para simular a proteção da requisição por uma transação. Basta utilizarmos uma interface para descrever a funcionalidade e criar uma classe que a implemente.

public interface ITransactionFeature
{
    string TransactionId { get; }
    void Commit();
    void Abort();
}

public class TransactionFeatureContext : ITransactionFeature
{
    public string TransactionId { get; } = Guid.NewGuid().ToString();

    public void Commit()
    {
        //…
    }

    public void Abort()
    {
       //…
    }
}

A classe TransactionFeatureContext faz toda a gestão da transação, desde a sua criação (identificando-a) e expõe métodos para conclusão com sucesso (Commit) ou falha (Abort). Repare que tanto a interface quanto a classe não referenciam nenhum tipo do ASP.NET; estamos livres para determinarmos toda a estrutura da nossa funcionalidade. Só que estas classes precisam ser incorporadas na execução, e para isso, vamos criar um middelware que incluirá esta funcionalidade na coleção de Features do contexto da requisição.

public class TransactionMiddleware
{
    private readonly RequestDelegate _next;

    public TransactionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var transactionContext = new TransactionFeatureContext();
        httpContext.Features.Set<ITransactionFeature>(transactionContext);

        try
        {
            await _next(httpContext);
            transactionContext.Commit();
        }
        catch
        {
            transactionContext.Abort();
            throw;
        }
    }
}

O middleware é criado é os middlewares internos que serão invocados a partir dele estarão sendo gerenciados pela transação. Qualquer exceção não tratada que ocorra, a transação será automaticamente abortada. Por fim, depois do middleware criado, incluímos ele na aplicação através do método UseMiddleware.

Vale lembrar que a ordem em que se adiciona os middlewares são importantes. Como a classe TransactionMiddleware adiciona a feature TransactionFeature que criamos, os middlewares que vem a seguir podem acessar a funcionalidade através do método Get<TFeature>, conforme falado acima. Opcionalmente podemos criar na interface métodos para que os middlewares da sequência possam votar no sucesso ou falha e este, por sua vez, avalia os votos antes de chamar o método Commit.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
    app.UseTransactionMiddleware();
    app.UseClientCertificateLogMiddleware();
    app.UseMvc();
}

Convenções no ASP.NET

Durante a construção e evolução do ASP.NET MVC e Web API a Microsoft foi assumindo algumas regras, que durante a execução, os frameworks alteram e geram informações de acordo com o código escrito por nós. Isso é conhecido como convention-over-configuration, e tem como foco diminuir algumas configurações explícitas por parte do desenvolvedor, focando em detalhes mais importantes para atender o negócio e o framework se responsabiliza por realizar as configurações necessárias para o funcionamento daquele código respeitando as tais convenções.

Apesar de as vezes não notarmos, isso está bastante presente em nosso dia a dia. Quando criamos um controller, dentro dele uma ação e esta, por sua vez, retornarmos uma view, o ASP.NET MVC assume a estrutura física de Views/NomeDoController/NomeDaAcao.cshtml para armazenar o arquivo e localiza-lo para exibir para o cliente. Outro exemplo é quando utilizamos o Entity Framework e queremos que a entidade Cliente seja persistida na base de dados; o ORM irá criar, automaticamente, a tabela com o mesmo nome daquela entidade. Já no ASP.NET Web API, se criarmos uma ação com o método chamado Get, ele estará automaticamente acessível através do verbo GET do HTTP, sem precisar decorar o mesmo com o atributo HttpGetAttribute.

Note que nos dois exemplos a Microsoft assumiu essas regras e as asseguram durante a execução, ficando, de certa forma, transparente para nós. Se não houvesse isso, teríamos que configurar as mesmas coisas em 100% dos casos; com essas convenções predefinidas, precisamos apenas nos preocupar quando queremos algo diferente do padrão.

O ASP.NET MVC e Web API expõem tipos para que possamos customizar o framework de acordo com nossas necessidades. Até então, isso poderia ser realizado customizando alguns serviços de baixo nível (controller factory, por exemplo), mas o problema desta técnica é que estaremos tocando em outras atividades e que qualquer descuido poderemos ter problemas ao executar e, consequentemente, não atingirmos o objetivo esperado.

O ASP.NET 5 simplifica bastante essa customização expondo uma interface chamada IApplicationModelConvention. Ao implementar ela e acopla-la a execução, o ASP.NET identificará todos os controllers, ações, rotas e filtros candidatos a serem expostos (de acordo com a convenção predefinida) e entregará para analisarmos (através da classe ApplicationModel), e com isso, podemos customizar da forma que precisamos. Tudo isso através de uma API de mais fácil utilização, sem a necessidade de conhecer detalhes dos bastidores para implementar convenções que estão em um nível mais superficial.

Para exemplificar o uso, considere o controller abaixo, que possui duas ações e que retornam uma string. Como podemos perceber, o código foi escrito utilizando o português. Por convenção, o ASP.NET Web API assumirá que o nome dos membros (classe e métodos) serão os mesmos que serão expostos e utilizados pelo sistema de roteamento para montar os links de acesso à API e suas ações.

public class UtilitariosController : Controller
{
    [HttpGet]
    public string ExecutarTarefa()
    {
        return “…” ;
    }

    [HttpGet]
    public string EnviarEmail()
    {
        return “…”;
    }
}

A ideia é deixar o desenvolvedor livre para codificar no idioma desejado, e através do recurso de convenção, vamos estipular que o framework irá traduzir de acordo com a escolha realizada durante a sua configuração. Na ausência do idioma, a nomenclatura utilizada no código será exposta.

O que fazemos abaixo é no método Apply (único, exposto pela interface IApplicationModelConvention) percorremos os controllers encontrados pelo ASP.NET, procuramos pela chave no dicionário (que em um ambiente real, poderíamos estar recorrendo à algum outro repositório ao invés de hard-code), e por fim substituímos o nome do controller e da ação pelo encontrado no dicionário, baseado na cultura escolhida.

public class LocalizedApiNamingModelConvention : IApplicationModelConvention
{
    private string culture;

    private IDictionary<string, Tuple<string, string>> mapping =
        new Dictionary<string, Tuple<string, string>>();

    public LocalizedApiNamingModelConvention(string culture)
    {
        this.culture = culture;

        mapping.Add(
            “en-US.Utilitarios.ExecutarTarefa”,
            Tuple.Create(“Utilities”, “RunTask”));

        mapping.Add(
            “en-US.Utilitarios.EnviarEmail”,
            Tuple.Create(“Utilities”, “SendEmail”));
    }

    public void Apply(ApplicationModel application)
    {
        foreach (var c in application.Controllers)
        {
            var controllerName = c.ControllerName;

            foreach (var a in c.Actions)
            {
                var key = BuildKey(controllerName, a.ActionName);

                if (this.mapping.ContainsKey(key))
                {
                    var info = this.mapping[key];

                    c.ControllerName = info.Item1;
                    a.ActionName = info.Item2;
                }
            }
        }
    }

    private string BuildKey(string controllerName, string actionName)
    {
        return $”{culture}.{controllerName}.{actionName}”;
    }
}

Depois da classe criada, basta incluirmos na coleção chamada Conventions, e fazemos isso através do método AddMvcCore, conforme é mostrado no código abaixo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore(o =>
        o.Conventions.Add(new LocalizedApiNamingModelConvention(“en-US”)));
}

Repare que criamos a classe e no construtor passamos a cultura en-US e ao rodar a aplicação, os links acessíveis serão /Utilities/RunTask e /Utilities/SendEmail. Os links em português retornarão o erro 404 do HTTP (Not Found).

Registrando Recursos para Eliminação

Apesar do ASP.NET Web API possui um mecanismo que nos permite estender e acoplar algum gerenciador de dependências, temos a possibilidade de registrar um determinado objeto para que ele seja descartado quando a requisição for completamente atendida.

É muito comum em aplicações Web criar um determinado objeto e querer mante-lo por toda a requisição, e em vários momentos e locais por onde a requisição trafega, podemos recuperar o referido objeto e reutiliza-lo para fazer alguma atividade complementar. Bancos de dados, loggers, entre outros, são exemplos de recursos que são úteis durante a vida da requisição dentro do pipeline onde ela está sendo processada.

A classe HttpRequestMessage possui uma propriedade chamada Properties (IDictionary<string, object>) que permite catalogarmos diversas informações inerentes à requisição, bem como recursos que podem ser (re)utilizados durante todas as etapas da execução. Uma vez adicionados, eles podem ser extraídos em qualquer momento no futuro que venha a precisar deles, mesmo que seja em outras camadas.

A dificuldade não é adicionar, mas saber o momento certo de descarta-lo. Simplesmente adicionar o recurso no dicionário não é suficiente para o ASP.NET Web API fazer a eliminação correta do mesmo. A Microsoft disponibilizou um método de extensão chamado RegisterForDispose, exposto pela classe HttpRequestMessageExtensions. Devemos utilizar este método para indicar explicitamente ao ASP.NET Web API que aquele objeto deve ser descartado no final da requisição. E, como era de se esperar, este método recebe objetos que implementam obrigatoriamente a interface IDisposable.

public class LoggerHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var logger = new TextLogger(@”C:TempLog.txt”);
        request.RegisterForDispose(logger);
        request.Properties.Add(“TextLogger”, logger);

        return base.SendAsync(request, cancellationToken).ContinueWith(tr =>
        {
            var response = tr.Result;

            logger.Write(“Request: ” + request.ToString()).ContinueWith(_ =>
                logger.Write(“Response: ” + response.ToString())).Wait();

            return response;
        }, cancellationToken);
    }
}

Curiosamente o método RegisterForDispose também recorre a propriedade Properties da classe HttpRequestMessage para acomodar os objetos que serão descartados quando a mesma também for removida. Caso encontre um momento mais oportuno para fazer o descarte, podemos recorrer ao método (também de extensão) chamado DisposeRequestResources para antecipar essa rotina, ou ainda, se preferir inspecionar os objetos marcados, pode-se utilizar o método GetResourcesForDisposal para isso.

Outro uso para o que vimos aqui é quando você opta por interceptar a criação do controller (através da implementação da interface IHttpControllerActivator), onde o mesmo pode necessitar de recursos em seu construtor e que sejam necessários serem marcados para descarte no término da requisição.

Conversão de Datas em Ações da API

Há momentos em que precisamos parametrizar as ações de controllers com outros tipos de dados que vão além de inteiros e strings, e sem ainda falar de tipos de complexos, o tipo DateTime pode ser uma necessidade muito comum em alguns casos. O problema é que este tipo de dado possui diversas formatações e sua validade varia de acordo com a cultura contra qual ele está sendo validado.

Sendo assim, podemos nos deparar com alguns problemas, onde apesar de ser uma data válida se considerarmos o formato brasileiro, ela pode ser considerada inválida pela API que utiliza o formato americano para tratar e vice versa. Mesmo que a cultura da aplicação esteja definida para a região correta, os componentes internos do ASP.NET Web API não são capazes de entender o formato da data que o cliente deve passar. Se considerarmos a data 20 de dezembro de 2015, 20/12/2015 é um formato válido para nós mas inválido para os Estados Unidos; já a data 12/20/2015 é o contrário.

Para especificar o formato exato para realizar a conversão, podemos criar um model binder específico para a data, e no interior do mesmo fazer a formatação desejada para todas as datas que são recebidas em parâmetros dos serviços. Abaixo está o exemplo de como poderíamos codificar este formatador:

public class CustomDateTimeModelBinder : IModelBinder
{
    private readonly string format;

    public CustomDateTimeModelBinder(string format)
    {
        this.format = format;
    }

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var date = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;

        if (string.IsNullOrWhiteSpace(date))
            return false;

        bindingContext
            .ModelState
            .SetModelValue(
                bindingContext.ModelName,
                bindingContext.ValueProvider.GetValue(bindingContext.ModelName));

        DateTime result = DateTime.MinValue;

        if (DateTime.TryParseExact(date, format, Thread.CurrentThread.CurrentCulture, DateTimeStyles.None, out result))
        {
            bindingContext.Model = result;
            return true;
        }

        bindingContext.ModelState.AddModelError(
            bindingContext.ModelName,
            string.Format(“”{0}” is invalid.”, bindingContext.ModelName));

        return false;
    }
}

Por fim, basta acoplá-lo à execução e indicar ao ASP.NET Web API que ele trata e valida o tipo DateTime. Para isso, basta utilizar a configuração do serviço para inclui-lo e as requisições que mandarem a data no formato especificado em seu construtor serão corretamente convertidas em DateTime respeitando o dia, mês e ano.

config.BindParameter(typeof(DateTime), new CustomDateTimeModelBinder(“dd/MM/yyyy”));

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.

Validade do Cookie no CookieAuthenticationMiddleware

Falei em um outro artigo sobre um componente que temos no OWIN que substitui o Forms Authentication (que é o CookieAuthenticationMiddleware). Como vimos naquele artigo, uma vez que o usuário se identificou como válido para aplicação, este componente é o responsável por emitir o cookie e embutir na resposta; em futuras requisições, o navegador será responsável por encaminhar este cookie, e este mesmo componente fará a validação para saber se o mesmo ainda continua válido.

Por válido me refiro aqui a duas verificações: integridade, que é a garantia que o mesmo não foi alterado entre a ida e a volta e o tempo de expiração. Ambos podem ser configurados através da classe CookieAuthenticationOptions. Só que podemos ter outras coisas que deveriam invalidar o acesso do usuário, como por exemplo, mudanças em seu perfil e/ou a remoção de uma permissão que ele possui(a), e as vezes, por questões de segurança, não podemos esperar até que o cookie expire para renovar as credenciais do usuário.

Apesar deste componente não fazer isso nativamente, ele possui um ponto de estensibilidade que nos permite acoplar um código customizado e fazer esta validação (de forma centralizada). A classe  CookieAuthenticationOptions expõe uma propriedade chamada Provider, que recebe a instância de uma classe do tipo CookieAuthenticationProvider, que por sua vez, possui várias propriedades, que via delegates, conseguimos interceptar e customizar diversos pontos da execução deste componente.

Entre eles, temos a propriedade OnValidateIdentity, que como o nome sugere, nos permite avaliar se a identidade é, ou melhor, continua válida. No exemplo abaixo estamos recorrendo ao gestor de usuários (repositório) e verificando se o mesmo continua sendo válido. Caso não seja, invocamos o método RejectIdentity (que está disponível através do contexto da execução) e obriga o usuário novamente a se identificar para a aplicação, redirecionando-o para a tela de login.

public void Configuration(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions()
    {
        AuthenticationType = “AppTeste”,
        LoginPath = new PathString(“/Seguranca/Login”),
        Provider = new CookieAuthenticationProvider()
        {
            OnValidateIdentity = async ctx =>
            {
                if (!(await GestorDeUsuarios.ValidarCadastroDoUsuario(ctx.Identity.Name)))
                    ctx.RejectIdentity();
            }
        }
    });
}

Se quiser tornar a experiência mais amigável para o mesmo, ao invés de utilizar o método RejectIdentity podemos recorrer ao método ReplaceIdentity, gerando uma nova identidade já refletindo as mudanças que foram realizadas na base de dados. O método ValidarCadastroDoUsuario pode fazer a verificação de diversas formas, e entre delas, podemos recorrer à um eventual timestamp que a tabela de usuário possa ter, o que permitirá identificar de forma fácil se alguma informação (coluna) foi alterada..

Versionamento de APIs

Sempre que precisamos desenvolver um serviço para que ele seja consumido (interna ou externamente), a maior dificildade é sempre decidir o que e como expor. A tecnologia em pouco tempo é possível que se tenha conhecimento suficiente para extrair o seu potencial, mas o maior desafio é saber o que expor, e isso muitas vezes está diretamente ligado ao conhecimento que se tem do negócio.

E como se não fosse suficiente, os problemas não param por aí. Depois que o serviço (ou API) esteja no ar, o desafio é outro, seja, a manutenção do mesmo, no que diz respeito a segurança, performance e evolução. A partir do momento em que a API está sendo consumida por, no mínimo, um cliente, uma preocupação passa a ser necessária ao fazer qualquer alteração em sua interface pública, pois dependendo do que é alterado, podemos deixar alguns clientes inoperantes, problema que não tínhamos quando colocamos pela primeira vez a API no ar.

O versionamento da API é importante para caracterizar a evolução da mesma, mas é útil também para que o cliente saiba o que e como está consumindo, e quando uma nova versão entrar no ar, é desejável que se mantenha compatibilidade com os clientes que já fazem uso das versões anteriores, e os novos clientes, já podem usufruir da nova versão sem qualquer restrição.

Quando falamos de API REST, podemos fazer uso de uma das três opções abaixo para identificar a versão, a saber:

A primeira opção, que é a utilização da coleção de headers, acaba sendo uma opção bastante interessante, já que não altera a URI e permite manter separado qualquer detalhe de versionamento; já a segunda opção, é bem mais problemática, pois se o cliente salvar localmente o endereço e mais tarde quiser acessá-lo novamente, o servidor ainda terá que responder à esta solicitação, ou seja, sabe-se lá por quanto tempo ainda será necessário manter os dois endereços e, consequentemente, as duas APIs rodando. E por fim, a terceira opção, apesar de menos elegante que a primeira, permite facilmente expressar qual versão da API deseja acessar, sem a manipulação de headers (que pode complicar para alguns clientes) e sem agregar à URI alguma informação que possa prejudicar futuramente.

O ASP.NET Web API permite que você customize a seleção do controller através de um ponto de estensibilidade, sem misturar infraestrutura com regra de negócio. Para isso, podemos recorrer à requisição extraindo as informações (headers, querystrings, etc.) que são necessárias para tomar a decisão de qual controller acessar. Para nosso exemplo, suponhamos que temos um controller que retorna documentos (versão 1.0) e mais tarde, criamos uma nova versão que retorna os mesmos documentos, só que agora incluindo a assinatura de quem o assinou (versão 2.0). A imagem abaixo ilustra os tipos que foram utilizados.

Para que seja possível influenciar na escolha do controller, o primeiro passo para é implementar a interface IHttpControllerSelector, e dentro desta classe escrever a regra necessária para tomar esta decisão. No exemplo abaixo tentamos extrair o header com o nome “Versao”; se encontrado a versão 1.0 ou se nada for encontrado, então retornamos o controller DocumentosController (que é a versão 1.0). Se a versão solicitada pelo cliente for a 2.0, então retornamos a classe DocumentosAssinadosController.

public class SeletorDeControllerDeDocumento : IHttpControllerSelector
{
private readonly Dictionary<string, HttpControllerDescriptor> controllersConhecidos;
private const string HeaderDeVersao = “Versao”;
private const string VersaoPadrao = “1.0”;

    public SeletorDeControllerDeDocumento(HttpConfiguration config)
{
this.controllersConhecidos = new Dictionary<string, HttpControllerDescriptor>()
{
{ “1.0”, new HttpControllerDescriptor(config, “DocumentosController”,
typeof(DocumentosController)) },
{ “2.0”, new HttpControllerDescriptor(config, “DocumentosAssinadosController”,
typeof(DocumentosAssinadosController)) }
};
}

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
return this.controllersConhecidos;
}

    public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IEnumerable<string> valores = null;

        if (request.Headers.TryGetValues(HeaderDeVersao, out valores))
foreach (var item in valores)
if (controllersConhecidos.ContainsKey(item))
return controllersConhecidos[item];

        return controllersConhecidos[VersaoPadrao];
}
}

Só que esta classe por si só não funciona, ou seja, precisamos acoplá-la à execução, substituindo a implementação padrão que vem com o ASP.NET Web API. Para isso, basta ir até o arquivo Global.asax e fazer o seguinte ajuste:

config.Services.Replace(typeof(IHttpControllerSelector),
new SeletorDeControllerDeDocumento(config));

Depois da implementação e da configuração da API, basta executarmos e através de algum cliente (vamos utilizar o Fiddler para os testes), iremos notar a diferença na requisição e, principalmente, na resposta. Como vamos notar, competirá ao cliente expressar qual a versão que ele deseja, e se omitir (pois isso deve ser a configuração padrão dos clientes iniciais), então a versão 1.0 será retornada.

[ Requisição Omitindo a Versão ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156

[ Resposta na Versão 1.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 247

[{“Descricao”:”Documento1″,”DataDaAssinatura”:”2014-04-23″},{“Descricao”:”Documento2″,”DataDaAssinatura”:”2014-04-25″},{“Descricao”:”Documento3″,”DataDaAssinatura”:”2014-04-26″}]

[ Requisição na Versão 2.0 ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156
Versao: 2.0

[ Resposta na Versão 2.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 307

[{“Assinatura”:”AQID”,”Descricao”:”Documento1″,”DataDaAssinatura”:”2014-04-23″},{“Assinatura”:”BAUG”,”Descricao”:”Documento2″,”DataDaAssinatura”:”2014-04-25″},{“Assinatura”:”BwgJ”,”Descricao”:”Documento3″,”DataDaAssinatura”:”2014-04-26″}]