Web Commands

Quando falamos de CQRS, temos as consultas e os comandos. Esses comandos podem ser consumidos diretamente por aplicações, mas como expor publicamente para os clientes? Uma opção comum nos dias de hoje é utilizar o protocolo HTTP, facilitando assim o consumo pelas mais diversas linguagens e tecnologias. E quais as opções no .NET para fazermos esta exposição?

O ASP.NET é um candidato para isso, já que em sua versão core está modularizado e flexível, e assim podemos otimizar o pipeline de execução com somente aquilo que seja de fato necessário para executar os comandos. A finalidade do vídeo abaixo é exibir uma forma alternativa para incorporar os comandos do domínio no ASP.NET, utilizando o mínimo de recursos possível do hosting. Link para o código do exemplo.

Anúncios

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");
}

HTTPS no Kestrel

Sempre quando pensamos em hospedar uma aplicação desenvolvida em ASP.NET logo nos vem a mente o IIS. Apesar de uma infinidade de recursos que ele possui, o novo ASP.NET traz nativamente uma forma de hospedar as aplicações em um processo fora do IIS (self-hosting), chamado de Kestrel, qual foi construído com a finalidade de ser multi-plataforma e vem ganhando bastante evidência com alguns testes que demonstram grandes resultados em termos de performance.

Aos poucos a Microsoft vem adicionado nele algumas funcionalidades para que ele incorpore tudo que é necessário para utilizarmos em ambientes de produção. A finalidade deste artigo é demonstrar como configurar a aplicação para suportar o uso do protocolo HTTPS e, consequentemente, proteger os recursos que serão expostos pela aplicação.

Como sabemos, independentemente da tecnologia ou servidor que utilizamos para as nossas aplicações, para o uso do HTTPS se faz necessário a compra de um certificado de alguma entidade publicamente conhecida, para que possamos instalar em nosso servidor e configurar a nossa aplicação para utiliza-lo para estabelecer um túnel seguro de comunicação entre o cliente e o servidor e vice-versa. Como o foco é demonstrar a configuração, vou utilizar um certificado de testes emitido por uma ferramenta, sem valor para uso em ambiente de produção.

O primeiro passa em nossa aplicação é indicar ao runtime que o hosting a ser utilizado é o Kestrel, e para isso, recorremos ao método UseKestrel. Em um dos seus overloads é possível acessar algumas configurações e entre elas a possibilidade de indicar que queremos que o HTTPS seja utilizado, que é expressado através do método de estensão UseHttps. Para fazer uso deste método é necessário a instalação de um pacote NUGET chamado Microsoft.AspNetCore.Server.Kestrel.Https.

Uma vez que o certificado está criado e armazenado em um arquivo PFX (que inclui a sua chave privada) e o pacote acima mencionado esteja instalado na aplicação, utilizamos o código abaixo para indicar o local onde o arquivo PFX referente ao certificado está localizado no disco; note também que o “12345” é a senha para que o ASP.NET possa acessar o certificado.

public class Program
{
    public static void Main(string[] args)
    {
        new WebHostBuilder()
            .UseKestrel(opts => opts.UseHttps(@”C:tempAppTesteMeuWebSite.pfx”, “12345”))
            .UseUrls(“http://localhost:5000/&#8221;, “https://localhost:5001/&#8221;)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .Build()
            .Run();
    }
}

Ao rodar, a aplicação estará disponível para acesso tanto através do HTTP quanto HTTPS, porém em portas diferentes.

Utilizando o HSTS

Quando falamos em proteger a comunicação entre a aplicação web e os clientes que a consomem, logo pensamos em HTTPS. Este protocolo tem a finalidade de criar um túnel entre as partes, garantindo com que as informações trafeguem entre os dois pontos mantendo a confidencialidade (ninguém além dos dois consegue visualizar) e a integridade (ninguém consegue alterar).

Quando habilitamos este protocolo na nossa aplicação e também no hosting que hospeda a mesma, não é 100% garantido que a aplicação será acessada através de HTTPS, colocando em risco a segurança, pois alguém no meio do caminho pode interceptar a requisição e explorar todo o seu conteúdo, e o pior, começar a direcionar o usuário para sites maliciosos.

Para um teste simples, considere o site do Banco Itaú. Muitas vezes as pessoas não se atentam em acessar o site digitando o https:// no navegador. Se monitorarmos a requisição para o endereço http://www.itau.com.br, nós veremos que o navegador recebe como resposta o código 301 (Moved Permanently), obrigando o navegador a encaminhar o cliente para o endereço seguro, que é o https://www.itau.com.br.

Ao receber este tipo de resposta e houver alguém interceptando a comunicação, ele pode substituir o endereço do redirecionamento e, consequentemente, nos mandar para um site malicioso, que simula a aparência do banco, e rouba todos os nossos dados. Para evitar isso, um novo recurso chamado HTTP Strict Transport Security (ou apenas HSTS) foi criado e permite à uma aplicação web indicar que ela somente pode ser acessada através do protocolo HTTPS, e em conjunto com o navegador, assegurarão que independentemente do que se tente fazer, a requisição sempre será feita (e as vezes até modificada para realizar) através do protocolo seguro. Para o servidor indicar ao navegador a obrigatoriedade da utilização do HTTPS, ele deve incluir no cabeçalho da resposta o item (chave) Strict-Transport-Security, indicando o tempo (em segundos) em que o cabeçalho será mantido do lado do cliente.

A especificação também contempla um parâmetro neste cabeçalho chamado includeSubDomains, que como o próprio nome indica, subdomínios deste site deverão ter a garantia de que também serão acessíveis somente através do HTTPS.

No ASP.NET 5, que é independente de hosting, podemos incluir este cabeçalho através de um middleware. É importante dizer que segundo a especificação, este cabeçalho só deve ser enviado ao cliente quando ele já estiver dentro de uma conexão protegida. E esta validação pode ser feita através da propriedade IsHttps que é exposta pelo objeto que representa a requisição.

public class HstsMiddleware
{
    private readonly RequestDelegate next;
    private readonly TimeSpan maxAge;

    public HstsMiddleware(RequestDelegate next, TimeSpan maxAge)
    {
        this.next = next;
        this.maxAge = maxAge;
    }

    public async Task Invoke(HttpContext context)
    {
        if (maxAge > TimeSpan.Zero && context.Request.IsHttps)
            context.Response.Headers.Append(
                “Strict-Transport-Security”, $”max-age={maxAge.TotalSeconds}”);

        await next(context);
    }
}

public static class HstsMiddlewareExtensions
{
    public static IApplicationBuilder UseHsts(this IApplicationBuilder builder, TimeSpan maxAge) => 
        builder.UseMiddleware<HstsMiddleware>(maxAge);
}

Alternativamente você também pode recorrer ao IIS para realizar a configuração do HSTS, e permitir com que os recursos que são disponibilizados por ele também sejam protegidos por HTTPS. A solução foi proposta pelo Doug Wilson e consiste em criar uma regra para sobrescrever o protocolo HTTP para HTTPS utilizando o arquivo de configuração da aplicação ou do servidor.

<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name=”HTTP to HTTPS redirect” stopProcessing=”true”>
                    <match url=”(.*)” />
                    <conditions>
                        <add input=”{HTTPS}” pattern=”off” ignoreCase=”true” />
                    </conditions>
                    <action type=”Redirect” url=”https://{HTTP_HOST}/{R:1}”
                        redirectType=”Permanent” />
                </rule>
            </rules>
            <outboundRules>
                <rule name=”Add Strict-Transport-Security when HTTPS” enabled=”true”>
                    <match serverVariable=”RESPONSE_Strict_Transport_Security”
                        pattern=”.*” />
                    <conditions>
                        <add input=”{HTTPS}” pattern=”on” ignoreCase=”true” />
                    </conditions>
                    <action type=”Rewrite” value=”max-age=31536000″ />
                </rule>
            </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>

Preload List

Apesar de garantir que todos os recursos da aplicação serão acessados através do HTTPS, o primeiro problema ainda persiste, ou seja, a requisição inicial para o site do banco pode continuar sendo feita através de HTTP, e a possibilidade de interceptação continua. Para evitar isso, o Google mantém um site (HSTS Preload List) em que podemos adicionar URLs que emitem este cabeçalho, e obviamente, possuem um certificado válido. Isso permitirá ao navegador antes de fazer a requisição para o site avaliar se ele está contido na lista, e se estiver, já transformará a primeira requisição em HTTPS.

Depois da primeira requisição feita, o navegador já é capaz de detectar a necessidade de acessar a aplicação através de HTTPS, e ao acessa-lo via HTTP, o próprio navegador faz um redirecionamento interno (307 – Internal Redirect) e já faz a requisição com HTTPS. É possível ver esse comportamento na imagem abaixo.

Integração do IIS com o DNX.exe

Como sabemos, o ASP.NET 5 é desenvolvido para ser independente de hosting, ou seja, podemos hospedar as aplicações no IIS, via self-hosting (WebListener) e com o, novo hosting, Kestrel, que é multi-plataforma. Em uma nova atualização que a Microsoft liberou do ASP.NET 5 (beta8), foi realizada uma mudança na forma como as aplicações ASP.NET 5 rodam no IIS.

Até então, a Microsoft utilizava um componente chamado “Helios” que servia como ponte entre a estrutura existente do .NET dentro IIS com este novo modelo do ASP.NET 5. Esse componente tinha uma série de responsabilidades, entre elas, fazer a configuração e gestão do runtime (DNX).

A partir da beta8, a Microsoft passou a utilizar o HttpPlataformHandler, que é um módulo nativo (código não gerenciado) que quando instalado no IIS, permite que as requisições HTTP que chegam para ele sejam encaminhadas para um outro processo. E, no caso do ASP.NET 5, esse processo é o dnx.exe (ambiente de execução do ASP.NET 5). Isso garantirá à Microsoft e ao time do projeto, o controle do hosting e a possibilidade de integrá-lo facilmente em ambientes Windows que já utilizam o IIS, tirando o proveito das funcionalidades que ele já disponibiliza. Depois de instalado, já é possível visualizarmos este módulo no IIS:

Quando criamos um novo projeto no Visual Studio, podemos perceber que dentro da pasta wwwroot haverá um arquivo web.config. Vale lembrar que o ASP.NET não utiliza mais este tipo de arquivo para centralizar as configurações; web.config é também o arquivo de configuração do IIS. Dentro deste arquivo temos a configuração do HttpPlataformHandler, que é onde informamos o processo para qual a requisição será encaminhada. Note que o arquivo referencia variáveis de ambiente, mas quando o deployment é feito, ele subistitui pelo endereço até o respectivo local.

<?xml version=”1.0″ encoding=”utf-8″?>
<configuration>
<system.webServer>
<handlers>
<add name=”httpPlatformHandler”
path=”*”
verb=”*”
modules=”httpPlatformHandler”
resourceType=”Unspecified”/>
</handlers>
<httpPlatform processPath=”%DNX_PATH%”
arguments=”%DNX_ARGS%”
stdoutLogEnabled=”false”
startupTimeLimit=”3600″/>
</system.webServer>
</configuration>

É importante dizer que o comando web continua apontando para o hosting Kestrel. Por padrão, existe um pacote já adicionado chamado Microsoft.AspNet.IISPlatformHandler. Ele é responsável por fornecer um middleware que captura os headers customizados que o IIS passa para o DNX e inclui na coleção de headers da requisição que foi criada dentro deste ambiente.

{
“dependencies”: {
“Microsoft.AspNet.IISPlatformHandler”: “1.0.0-beta8”
},

  “commands”: {
“web”: “Microsoft.AspNet.Server.Kestrel”
}
}

Esse middleware é adicionado através do método UseIISPlatformHandler, e faz a cópia de alguns cabeçalhos (X-Forwarded-Proto, X-Original-Proto, etc.) utilizados para acompanha-la durante o repasse entre os ambientes. Ele também já faz a mescla da identidade (ClaimsPrincipal) repassada do IIS com a identidade da aplicação, caso o usuário já esteja autenticado deste lado.

Quando a aplicação é instalada no IIS, como falando acima, o web.config sobre algumas mudanças e ajusta os caminhos para o endereço de onde está o executável do DNX:

<httpPlatform processPath=”..approotweb.cmd” … />

Finalmente, ao rodar a aplicação, é possível percebermos que o dnx.exe está abaixo do w3wp.exe, que é o processo que o IIS utiliza para rodar cada pool de aplicação. As funcionalidades fornecidas pelo IIS continuam disponíveis, como reciclagem do processo, inatividade, etc. O interessante também é que o pool não precisa mais rodar sobre código gerenciado, e ele também não será mais o responsável por fazer o carregando do runtime (CLR). Isso passa a ser responsabilidade do processo que está abaixo dele. Nem mesmo as funcionalidades do ASP.NET fornecidas pelo Windows precisam ser mais instaladas; a aplicação já é toda “auto-contida” (código, pacotes e runtime).

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();
}

OwinHost.exe

A arquitetura do OWIN está tornando o ambiente de execução de aplicações Web (ASP.NET) muito mais simples e de fácil estensibilidade, sem contar no alívio de performance que ele traz para estes tipos de aplicações, pois está aos poucos tentando diminuir a dependência do assembly System.Web.dll.

Uma das características da arquitetura OWIN é a independência do host onde a aplicação roda. Hoje, quando criamos um projeto no Visual Studio, por padrão, ao pressionar F5, o IIS Express inicia para executar a aplicação para que possam realizar os testes e, principalmente, depurar quando for necessário. O Microsoft OWIN já fornece a implementação de host para rodar a aplicação sobre o pipeline do ASP.NET e uma implementação para self-hosting.

Mas como ele foi criado com a estensibilidade sendo um de seus pilares, a sua API é estensível o suficiente para que se crie novos tipos de hosts. E foi isso que a Microsoft fez para testes de aplicações que são “puramente” OWIN, ou seja, criou uma nova opção para rodar aplicações OWIN no Visual Studio (ou fora dele) que é o OwinHost.exe, e vamos ver sua utilização no decorer deste artigo.

Mesmo utilizando a template Empty do ASP.NET Web Application, o projeto já nasce com a System.Web.dll referenciada e uma porção de outros assemblies que também estão relacionados a ela. Se simplesmente rodarmos a aplicação, o IIS Express entra em ação para hospedar e gerenciar a execução da mesma.

Para os testes, exclui todos estes assemblies e referenciei dois pacotes (via Nuget): Owin e Microsoft.Owin. Depois disso, criei o código abaixo que recebe a requisição e retorna uma simples resposta. Se tentar rodar esta aplicação com esta configuração, não será possível executa-la pois o IIS Express não saberá como lidar com esse tipo de código.

[assembly: OwinStartup(typeof(WebApplication1.Startup))]

namespace WebApplication1
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Run(ctx => ctx.Response.WriteAsync(“Bem-vindo!”));
        }
    }
}

Se fizermos questão de utilizar o IIS Express (ou IIS), então será necessário a adição de mais um pacote, que é o Microsft.Owin.Host.SystemWeb, que como falei acima, faz com que o OWIN rode sobre o pipeline do ASP.NET. Mas isso não é o que queremos fazer. Ao invés de utilizarmos o IIS, vamos recorrer ao OwinHost, e é este também o nome do pacote disponível via Nuget.

Quando adicionamos este pacote ao projeto, passa a aparecer um novo tipo de host disponível nas configurações do projeto, assim como é mostrado na imagem abaixo. Quando o OwinHost é escolhido entre as opções, algumas configurações específicas são apresentadas, que basicamente determinam a URL e porta onde a aplicação rodará.

Ao rodar a aplicação, o OwinHost.exe se inicia (via console) e o navegador é aberto para que seja possível navegar pela aplicação. E, como podemos notar, isso abrirá as portas para que novos tipos de hosts sejam criados, e independente de qual escolhermos, o Visual Studio evolui para tentar continuar dando a mesma experiência (leia-se facilidade) quando desenvolvemos com ele.