Migrar manipuladores e módulos HTTP para middleware do ASP.NET Core
Este artigo mostra como você pode migrar os módulos e manipuladores ASP.NET HTTP existentes do system.webserver para ASP.NET Core Middleware.
Módulos e manipuladores revisitados
Antes de prosseguir para o Middleware ASP.NET Core, vamos primeiro recapitular como funcionam os módulos e manipuladores HTTP:
Os Manipuladores são:
Classes que implementam IHttpHandler
Usadas para manipular solicitações com um determinado nome de arquivo ou extensão, como .report
Configurado no Web.config
Os Módulos são:
Classes que implementam IHttpModule
Invocados para cada solicitação
Capaz de interromper rapidamente (parar o processamento adicional de uma solicitação)
Capazes de adicionar à resposta HTTP ou criar suas próprias respostas
Configurado no Web.config
A ordem em que os módulos processam as solicitações recebidas é determinada por:
Uma série de eventos disparados pelo ASP.NET, como BeginRequest e AuthenticateRequest. Para obter uma lista completa, consulte System.Web.HttpApplication. Cada módulo pode criar um manipulador para um ou mais eventos.
Para o mesmo evento, a ordem em que eles são configurados em Web.config.
Além dos módulos, você pode adicionar manipuladores para os eventos do ciclo de vida ao seu arquivo Global.asax.cs
. Esses manipuladores são executados após os manipuladores nos módulos configurados.
De manipuladores e módulos a Middleware
O Middleware é mais simples do que os módulos e manipuladores HTTP:
Módulos, manipuladores,
Global.asax.cs
, Web.config (exceto a configuração do IIS) e o ciclo de vida do aplicativo desapareceramAs funções dos módulos e dos manipuladores foram assumidas pelo Middleware
O Middleware é configurado usando código, e não no Web.config
- As ramificações do pipeline permitem que você envie solicitações a Middlewares específicos, com base não apenas na URL, mas também nos cabeçalhos da solicitação, cadeias de caracteres de consulta etc.
- As ramificações do pipeline permitem que você envie solicitações a Middlewares específicos, com base não apenas na URL, mas também nos cabeçalhos da solicitação, cadeias de caracteres de consulta etc.
Os Middlewares são muito semelhantes aos módulos:
Invocado, em princípio, para cada solicitação
Capaz de interromper rapidamente uma solicitação, não passando a solicitação para o próximo Middleware
Capaz de criar sua própria resposta HTTP
O Middleware e os módulos são processados em uma ordem diferente:
A ordem do Middleware é baseada na ordem em que são inseridos no pipeline de solicitações, enquanto a ordem dos módulos é baseada principalmente em eventos System.Web.HttpApplication.
A ordem do Middleware para respostas é o inverso da ordem para solicitações, enquanto a ordem dos módulos é a mesma para solicitações e respostas
Consulte Criar um pipeline de Middleware com o IApplicationBuilder
Observe como, na imagem acima, o Middleware de autenticação interrompeu rapidamente a solicitação.
Migrando o código do módulo para o Middleware
Um módulo HTTP existente terá uma aparência semelhante a esta:
// ASP.NET 4 module
using System;
using System.Web;
namespace MyApp.Modules
{
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
application.EndRequest += (new EventHandler(this.Application_EndRequest));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpContext context = ((HttpApplication)source).Context;
// Do something with context near the beginning of request processing.
}
private void Application_EndRequest(Object source, EventArgs e)
{
HttpContext context = ((HttpApplication)source).Context;
// Do something with context near the end of request processing.
}
}
}
Conforme mostrado na página Middleware, um Middleware ASP.NET Core é uma classe que expõe um método Invoke
que recebe um HttpContext
e retorna um Task
. Seu novo Middleware terá a seguinte aparência:
// ASP.NET Core middleware
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace MyApp.Middleware
{
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// Do something with context near the beginning of request processing.
await _next.Invoke(context);
// Clean up.
}
}
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
}
O modelo de Middleware anterior foi retirado da seção sobre gravação do Middleware.
A classe auxiliar MyMiddlewareExtensions facilita a configuração do Middleware na sua classe Startup
. O método UseMyMiddleware
adiciona sua classe de Middleware ao pipeline de solicitações. Os serviços exigidos pelo Middleware são injetados no construtor do Middleware.
Seu módulo pode encerrar uma solicitação, por exemplo, se o usuário não estiver autorizado:
// ASP.NET 4 module that may terminate the request
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpContext context = ((HttpApplication)source).Context;
// Do something with context near the beginning of request processing.
if (TerminateRequest())
{
context.Response.End();
return;
}
}
Um Middleware lida com isso não chamando Invoke
no próximo Middleware do pipeline. Lembre-se de que isso não encerra totalmente a solicitação, pois os Middlewares anteriores ainda serão chamados quando a resposta retornar pelo pipeline.
// ASP.NET Core middleware that may terminate the request
public async Task Invoke(HttpContext context)
{
// Do something with context near the beginning of request processing.
if (!TerminateRequest())
await _next.Invoke(context);
// Clean up.
}
Ao migrar a funcionalidade do seu módulo para o novo Middleware, você pode descobrir que seu código não está sendo compilado porque a classe HttpContext
foi alterada significativamente no ASP.NET Core. Mais adiante, você verá como migrar para o novo ASP.NET Core HttpContext.
Migrando a inserção do módulo no pipeline de solicitação
Normalmente, os módulos HTTP são adicionados ao pipeline de solicitação usando Web.config:
<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
<system.webServer>
<modules>
<add name="MyModule" type="MyApp.Modules.MyModule"/>
</modules>
</system.webServer>
</configuration>
Converta isso adicionando seu novo Middleware ao pipeline de solicitações na sua classe Startup
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMyMiddleware();
app.UseMyMiddlewareWithParams();
var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
app.UseMyMiddlewareWithParams(myMiddlewareOptions);
app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
app.UseMyTerminatingMiddleware();
// Create branch to the MyHandlerMiddleware.
// All requests ending in .report will follow this branch.
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".report"),
appBranch => {
// ... optionally add more middleware to this branch
appBranch.UseMyHandler();
});
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".context"),
appBranch => {
appBranch.UseHttpContextDemoMiddleware();
});
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
O ponto exato no pipeline no qual você insere seu novo Middleware depende do evento que ele lidou como um módulo (BeginRequest
, EndRequest
etc.) e da ordem dele na sua lista de módulos no Web.config.
Como dito anteriormente, não há ciclo de vida do aplicativo no ASP.NET Core e a ordem em que as respostas são processadas pelo Middleware difere da ordem usada pelos módulos. Isso pode tornar a sua decisão de ordenação mais desafiadora.
Se a ordenação se tornar um problema, você poderá dividir seu módulo em vários componentes de Middleware que podem ser ordenados de forma independente.
Migrando o código do manipulador para o Middleware
Um manipulador HTTP é mais ou menos assim:
// ASP.NET 4 handler
using System.Web;
namespace MyApp.HttpHandlers
{
public class MyHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
string response = GenerateResponse(context);
context.Response.ContentType = GetContentType();
context.Response.Output.Write(response);
}
// ...
private string GenerateResponse(HttpContext context)
{
string title = context.Request.QueryString["title"];
return string.Format("Title of the report: {0}", title);
}
private string GetContentType()
{
return "text/plain";
}
}
}
No seu projeto ASP.NET Core, você traduziria isso para um Middleware semelhante a este:
// ASP.NET Core middleware migrated from a handler
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace MyApp.Middleware
{
public class MyHandlerMiddleware
{
// Must have constructor with this signature, otherwise exception at run time
public MyHandlerMiddleware(RequestDelegate next)
{
// This is an HTTP Handler, so no need to store next
}
public async Task Invoke(HttpContext context)
{
string response = GenerateResponse(context);
context.Response.ContentType = GetContentType();
await context.Response.WriteAsync(response);
}
// ...
private string GenerateResponse(HttpContext context)
{
string title = context.Request.Query["title"];
return string.Format("Title of the report: {0}", title);
}
private string GetContentType()
{
return "text/plain";
}
}
public static class MyHandlerExtensions
{
public static IApplicationBuilder UseMyHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyHandlerMiddleware>();
}
}
}
Esse Middleware é muito semelhante ao Middleware correspondente aos módulos. A única diferença real é que aqui não há chamada para _next.Invoke(context)
. Isso faz sentido, porque o manipulador está no final do pipeline de solicitações, de modo que não haverá um próximo Middleware a ser invocado.
Migrando a inserção do manipulador no pipeline de solicitação
A configuração de um manipulador HTTP é feita no Web.config e tem a seguinte aparência:
<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
<system.webServer>
<handlers>
<add name="MyHandler" verb="*" path="*.report" type="MyApp.HttpHandlers.MyHandler" resourceType="Unspecified" preCondition="integratedMode"/>
</handlers>
</system.webServer>
</configuration>
Você poderia converter isso adicionando seu novo Middleware manipulador ao pipeline de solicitações em sua classe Startup
, semelhante ao Middleware convertido de módulos. O problema com essa abordagem é que você enviaria todas as solicitações ao seu novo Middleware manipulador. No entanto, você só quer que as solicitações com uma determinada extensão cheguem ao seu Middleware. Isso lhe daria a você mesma funcionalidade que tinha com o seu manipulador HTTP.
Uma solução é ramificar o pipeline para solicitações com uma determinada extensão, utilizando o método de extensão MapWhen
. Você faz isso no mesmo método Configure
no qual adiciona o outro Middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMyMiddleware();
app.UseMyMiddlewareWithParams();
var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
app.UseMyMiddlewareWithParams(myMiddlewareOptions);
app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
app.UseMyTerminatingMiddleware();
// Create branch to the MyHandlerMiddleware.
// All requests ending in .report will follow this branch.
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".report"),
appBranch => {
// ... optionally add more middleware to this branch
appBranch.UseMyHandler();
});
app.MapWhen(
context => context.Request.Path.ToString().EndsWith(".context"),
appBranch => {
appBranch.UseHttpContextDemoMiddleware();
});
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
MapWhen
recebe esses parâmetros:
Um lambda que recebe o
HttpContext
e retornatrue
se a solicitação deve descer pela ramificação. Isso significa que você pode ramificar solicitações não apenas com base na extensão, mas também em cabeçalhos de solicitação, parâmetros de cadeia de caracteres de consulta etc.Um lambda que recebe
IApplicationBuilder
e adiciona todo o Middleware para a ramificação. Isso significa que você pode adicionar outros Middlewares à ramificação na frente do Middleware do manipulador.
O Middleware adicionado ao pipeline antes da ramificação será chamado em todas as solicitações; a ramificação não terá impacto sobre eles.
Carregamento das opções de Middleware usando o padrão de opções
Alguns módulos e manipuladores têm as opções de configuração armazenadas no Web.config. No entanto, no ASP.NET Core, um novo modelo de configuração é usado no lugar do Web.config.
O novo sistema de configuração oferece a você essas opções para resolver isso:
Injetar diretamente as opções no Middleware, conforme mostrado na próxima seção.
Usar o padrão opções:
Crie uma classe para manter as opções do Middleware, por exemplo:
public class MyMiddlewareOptions { public string Param1 { get; set; } public string Param2 { get; set; } }
Armazenar os valores das opções
O sistema de configuração permite que você armazene os valores das opções onde quiser. No entanto, a maioria dos sites usa
appsettings.json
, portanto, adotaremos essa abordagem:{ "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
MyMiddlewareOptionsSection aqui está o nome da seção. Ele não precisa ser o mesmo que o nome da classe de opções.
Associar os valores da opção à classe de opções
O padrão de opções usa a estrutura de injeção de dependências do ASP.NET Core para associar o tipo de opções (como
MyMiddlewareOptions
) a um objetoMyMiddlewareOptions
que tem as opções reais.Atualize sua classe
Startup
:Se você estiver usando
appsettings.json
, adicione-a ao construtor de configurações no construtorStartup
:public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); }
Configure o serviço de opções:
public void ConfigureServices(IServiceCollection services) { // Setup options service services.AddOptions(); // Load options from section "MyMiddlewareOptionsSection" services.Configure<MyMiddlewareOptions>( Configuration.GetSection("MyMiddlewareOptionsSection")); // Add framework services. services.AddMvc(); }
Associe suas opções à sua classe de opções:
public void ConfigureServices(IServiceCollection services) { // Setup options service services.AddOptions(); // Load options from section "MyMiddlewareOptionsSection" services.Configure<MyMiddlewareOptions>( Configuration.GetSection("MyMiddlewareOptionsSection")); // Add framework services. services.AddMvc(); }
Injete as opções no seu construtor de Middleware. Isso é semelhante a injetar opções em um controlador.
public class MyMiddlewareWithParams { private readonly RequestDelegate _next; private readonly MyMiddlewareOptions _myMiddlewareOptions; public MyMiddlewareWithParams(RequestDelegate next, IOptions<MyMiddlewareOptions> optionsAccessor) { _next = next; _myMiddlewareOptions = optionsAccessor.Value; } public async Task Invoke(HttpContext context) { // Do something with context near the beginning of request processing // using configuration in _myMiddlewareOptions await _next.Invoke(context); // Do something with context near the end of request processing // using configuration in _myMiddlewareOptions } }
O método de extensão UseMiddleware que adiciona seu Middleware ao
IApplicationBuilder
cuida da injeção de dependências.Isso não se limita aos objetos
IOptions
. Qualquer outro objeto que seu Middleware exija pode ser injetado dessa maneira.
Carregando as opções do Middleware através da injeção direta
O padrão de opções tem a vantagem de criar um acoplamento flexível entre os valores das opções e seus consumidores. Depois que você associa uma classe de opções aos valores reais das opções, qualquer outra classe poderá obter acesso às opções através da estrutura de injeção de dependências. Não há necessidade de transmitir os valores das opções.
Entretanto, isso não funcionará se você quiser usar o mesmo Middleware duas vezes, com opções diferentes. Por exemplo, um Middleware de autorização usado em ramificações diferentes que permitem funções diferentes. Você não pode associar dois objetos de opções diferentes a uma única classe de opções.
A solução é obter os objetos de opções com os valores reais de opções em sua classe Startup
e passá-los diretamente para cada instância do seu Middleware.
Adicionar uma segunda chave a
appsettings.json
Para adicionar um segundo conjunto de opções ao arquivo
appsettings.json
, use uma nova chave para identificá-lo de forma exclusiva:{ "MyMiddlewareOptionsSection2": { "Param1": "Param1Value2", "Param2": "Param2Value2" }, "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
Recupere os valores das opções e passe-os para o Middleware. O método de extensão
Use...
(que adiciona seu Middleware ao pipeline) é um local lógico para você passar os valores das opções:public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseMyMiddleware(); app.UseMyMiddlewareWithParams(); var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>(); var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>(); app.UseMyMiddlewareWithParams(myMiddlewareOptions); app.UseMyMiddlewareWithParams(myMiddlewareOptions2); app.UseMyTerminatingMiddleware(); // Create branch to the MyHandlerMiddleware. // All requests ending in .report will follow this branch. app.MapWhen( context => context.Request.Path.ToString().EndsWith(".report"), appBranch => { // ... optionally add more middleware to this branch appBranch.UseMyHandler(); }); app.MapWhen( context => context.Request.Path.ToString().EndsWith(".context"), appBranch => { appBranch.UseHttpContextDemoMiddleware(); }); app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
Você pode habilitar o Middleware para receber um parâmetro de opções. Forneça uma sobrecarga do método de extensão
Use...
(que recebe o parâmetro de opções e o passa paraUseMiddleware
). QuandoUseMiddleware
é chamado com parâmetros, ele passa os parâmetros para o construtor do Middleware quando ele criar uma instância o objeto de Middleware.public static class MyMiddlewareWithParamsExtensions { public static IApplicationBuilder UseMyMiddlewareWithParams( this IApplicationBuilder builder) { return builder.UseMiddleware<MyMiddlewareWithParams>(); } public static IApplicationBuilder UseMyMiddlewareWithParams( this IApplicationBuilder builder, MyMiddlewareOptions myMiddlewareOptions) { return builder.UseMiddleware<MyMiddlewareWithParams>( new OptionsWrapper<MyMiddlewareOptions>(myMiddlewareOptions)); } }
Observe como isso encapsula o objeto de opções em um objeto
OptionsWrapper
. Isso implementaIOptions
, conforme esperado pelo construtor do Middleware.
Migrando para o novo HttpContext
Você viu anteriormente que o método Invoke
no seu Middleware recebe um parâmetro do tipo HttpContext
:
public async Task Invoke(HttpContext context)
HttpContext
foi alterado significativamente no ASP.NET Core. Esta seção mostra como traduzir as propriedades mais comumente usadas de System.Web.HttpContext para o novo Microsoft.AspNetCore.Http.HttpContext
.
HttpContext
HttpContext.Items é traduzido para:
IDictionary<object, object> items = httpContext.Items;
ID de solicitação exclusiva (sem equivalente em System.Web.HttpContext)
Fornece a você uma ID exclusiva para cada solicitação. Muito útil para você incluir nos seus logs.
string requestId = httpContext.TraceIdentifier;
HttpContext.Request
HttpContext.Request.HttpMethod é traduzido para:
string httpMethod = httpContext.Request.Method;
HttpContext.Request.QueryString é traduzido para:
IQueryCollection queryParameters = httpContext.Request.Query;
// If no query parameter "key" used, values will have 0 items
// If single value used for a key (...?key=v1), values will have 1 item ("v1")
// If key has multiple values (...?key=v1&key=v2), values will have 2 items ("v1" and "v2")
IList<string> values = queryParameters["key"];
// If no query parameter "key" used, value will be ""
// If single value used for a key (...?key=v1), value will be "v1"
// If key has multiple values (...?key=v1&key=v2), value will be "v1,v2"
string value = queryParameters["key"].ToString();
HttpContext.Request.Url e HttpContext.Request.RawUrl são traduzidos para:
// using Microsoft.AspNetCore.Http.Extensions;
var url = httpContext.Request.GetDisplayUrl();
HttpContext.Request.IsSecureConnection é traduzido para:
var isSecureConnection = httpContext.Request.IsHttps;
HttpContext.Request.UserHostAddress é traduzido para:
var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();
HttpContext.Request.Cookies é traduzido para:
IRequestCookieCollection cookies = httpContext.Request.Cookies;
string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception)
string knownCookieValue = cookies["cookie1name"]; // will be actual value
HttpContext.Request.RequestContext.RouteData é traduzido para:
var routeValue = httpContext.GetRouteValue("key");
HttpContext.Request.Headers é traduzido para:
// using Microsoft.AspNetCore.Http.Headers;
// using Microsoft.Net.Http.Headers;
IHeaderDictionary headersDictionary = httpContext.Request.Headers;
// GetTypedHeaders extension method provides strongly typed access to many headers
var requestHeaders = httpContext.Request.GetTypedHeaders();
CacheControlHeaderValue cacheControlHeaderValue = requestHeaders.CacheControl;
// For unknown header, unknownheaderValues has zero items and unknownheaderValue is ""
IList<string> unknownheaderValues = headersDictionary["unknownheader"];
string unknownheaderValue = headersDictionary["unknownheader"].ToString();
// For known header, knownheaderValues has 1 item and knownheaderValue is the value
IList<string> knownheaderValues = headersDictionary[HeaderNames.AcceptLanguage];
string knownheaderValue = headersDictionary[HeaderNames.AcceptLanguage].ToString();
HttpContext.Request.UserAgent é traduzido para:
string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();
HttpContext.Request.UrlReferrer é traduzido para:
string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();
HttpContext.Request.ContentType é traduzido para:
// using Microsoft.Net.Http.Headers;
MediaTypeHeaderValue mediaHeaderValue = requestHeaders.ContentType;
string contentType = mediaHeaderValue?.MediaType.ToString(); // ex. application/x-www-form-urlencoded
string contentMainType = mediaHeaderValue?.Type.ToString(); // ex. application
string contentSubType = mediaHeaderValue?.SubType.ToString(); // ex. x-www-form-urlencoded
System.Text.Encoding requestEncoding = mediaHeaderValue?.Encoding;
HttpContext.Request.Form é traduzido para:
if (httpContext.Request.HasFormContentType)
{
IFormCollection form;
form = httpContext.Request.Form; // sync
// Or
form = await httpContext.Request.ReadFormAsync(); // async
string firstName = form["firstname"];
string lastName = form["lastname"];
}
Aviso
A leitura dos valores do formulário é feita somente se o subtipo de conteúdo for x-www-form-urlencoded ou form-data.
HttpContext.Request.InputStream é traduzido para:
string inputBody;
using (var reader = new System.IO.StreamReader(
httpContext.Request.Body, System.Text.Encoding.UTF8))
{
inputBody = reader.ReadToEnd();
}
Aviso
Use esse código somente em um Middleware do tipo manipulador, no final de um pipeline.
Você pode fazer a leitura do corpo bruto, conforme mostrado acima, apenas uma vez por solicitação. O Middleware que tentar fazer a leitura do corpo após a primeira leitura lerá um corpo vazio.
Isso não se aplica à leitura de um formulário, conforme mostrado anteriormente, porque isso é feito a partir de um buffer.
HttpContext.Response
HttpContext.Response.Status e HttpContext.Response.StatusDescription são traduzidos para:
// using Microsoft.AspNetCore.Http;
httpContext.Response.StatusCode = StatusCodes.Status200OK;
HttpContext.Response.ContentEncoding e HttpContext.Response.ContentType são traduzidos para:
// using Microsoft.Net.Http.Headers;
var mediaType = new MediaTypeHeaderValue("application/json");
mediaType.Encoding = System.Text.Encoding.UTF8;
httpContext.Response.ContentType = mediaType.ToString();
HttpContext.Response.ContentType por si só também é traduzido para:
httpContext.Response.ContentType = "text/html";
HttpContext.Response.Output é traduzido para:
string responseContent = GetResponseContent();
await httpContext.Response.WriteAsync(responseContent);
HttpContext.Response.TransmitFile
O envio de um arquivo é discutido em Recursos de solicitação no ASP.NET Core.
HttpContext.Response.Headers
O envio de cabeçalhos de resposta é complicado pelo fato de que, se você os definir depois que algo tiver sido gravado no corpo da resposta, eles não serão enviados.
A solução é a definição de um método de retorno de chamada que será chamado logo antes do início da gravação na resposta. A melhor maneira de fazer isso é no início do método Invoke
no seu Middleware. É esse método de retorno de chamada que define os cabeçalhos da resposta.
O seguinte código define um método de retorno de chamada chamado SetHeaders
:
public async Task Invoke(HttpContext httpContext)
{
// ...
httpContext.Response.OnStarting(SetHeaders, state: httpContext);
O método de retorno de chamada SetHeaders
teria a seguinte aparência:
// using Microsoft.AspNet.Http.Headers;
// using Microsoft.Net.Http.Headers;
private Task SetHeaders(object context)
{
var httpContext = (HttpContext)context;
// Set header with single value
httpContext.Response.Headers["ResponseHeaderName"] = "headerValue";
// Set header with multiple values
string[] responseHeaderValues = new string[] { "headerValue1", "headerValue1" };
httpContext.Response.Headers["ResponseHeaderName"] = responseHeaderValues;
// Translating ASP.NET 4's HttpContext.Response.RedirectLocation
httpContext.Response.Headers[HeaderNames.Location] = "http://www.example.com";
// Or
httpContext.Response.Redirect("http://www.example.com");
// GetTypedHeaders extension method provides strongly typed access to many headers
var responseHeaders = httpContext.Response.GetTypedHeaders();
// Translating ASP.NET 4's HttpContext.Response.CacheControl
responseHeaders.CacheControl = new CacheControlHeaderValue
{
MaxAge = new System.TimeSpan(365, 0, 0, 0)
// Many more properties available
};
// If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
return Task.FromResult(0);
}
HttpContext.Response.Cookies
Cookies viajam para o navegador em um cabeçalho de resposta Set-Cookie. Como resultado, o envio de cookies requer a mesma chamada de retorno usada para o envio de cabeçalhos de resposta:
public async Task Invoke(HttpContext httpContext)
{
// ...
httpContext.Response.OnStarting(SetCookies, state: httpContext);
httpContext.Response.OnStarting(SetHeaders, state: httpContext);
O método de retorno de chamada SetCookies
seria parecido com o seguinte:
private Task SetCookies(object context)
{
var httpContext = (HttpContext)context;
IResponseCookies responseCookies = httpContext.Response.Cookies;
responseCookies.Append("cookie1name", "cookie1value");
responseCookies.Append("cookie2name", "cookie2value",
new CookieOptions { Expires = System.DateTime.Now.AddDays(5), HttpOnly = true });
// If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
return Task.FromResult(0);
}