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.

Anúncios

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″}]