Перенос обработчиков HTTP-данных и модулей HTTP в ПО промежуточного слоя ASP.NET Core
В этой статье показано, как перенести существующие ASP.NET модули HTTP и обработчики из system.webserver в ASP.NET по промежуточному поверх Core.
Модули и обработчики изменены
Прежде чем переходить к по промежуточному поверх ASP.NET Core, давайте сначала рассмотрим, как работают модули и обработчики HTTP:
Обработчики:
Классы, реализующие IHttpHandler
Используется для обработки запросов с заданным именем или расширением файла, например .report
Настроено в web.config
Модули:
Классы, реализующие IHttpModule
Вызывается для каждого запроса
Возможность короткого замыкания (остановить дальнейшую обработку запроса)
Возможность добавлять в ответ HTTP или создавать собственные
Настроено в web.config
Порядок обработки входящих запросов модулей определяется следующим образом:
События серии, вызванные ASP.NET, например BeginRequest и AuthenticateRequest. Полный список см. в разделе System.Web.HttpApplication. Каждый модуль может создать обработчик для одного или нескольких событий.
Для того же события порядок, в котором они настроены в Web.config.
В дополнение к модулям можно добавить обработчики для событий жизненного цикла в Global.asax.cs
файл. Эти обработчики выполняются после обработчиков в настроенных модулях.
От обработчиков и модулей до ПО промежуточного слоя
По промежуточному слоям проще, чем модули HTTP и обработчики:
Модули, обработчики,
Global.asax.cs
web.config (за исключением конфигурации IIS) и жизненный цикл приложения исчезлиРоли обоих модулей и обработчиков были переданы по промежуточному слоям
ПО промежуточного слоя настраиваются с помощью кода, а не в web.config
- Ветвление конвейера позволяет отправлять запросы в определенное ПО промежуточного слоя, основанное не только на URL-адресе, но и на заголовках запросов, строках запроса и т. д.
- Ветвление конвейера позволяет отправлять запросы в определенное ПО промежуточного слоя, основанное не только на URL-адресе, но и на заголовках запросов, строках запроса и т. д.
По промежуточному слоям очень похожи на модули:
Вызывается в принципе для каждого запроса
Возможность короткого замыкания запроса, не передавая запрос следующему ПО промежуточного слоя
Возможность создания собственного HTTP-ответа
ПО промежуточного слоя и модули обрабатываются в другом порядке:
Порядок по промежуточного слоя основан на том порядке, в котором они вставляются в конвейер запросов, а порядок модулей в основном основан на System.Web.HttpApplication событиях.
Порядок по промежуточного слоя для ответов является обратным от этого для запросов, а порядок модулей совпадает с порядком запросов и ответов.
См. статью "Создание конвейера ПО промежуточного слоя" с помощью IApplicationBuilder
Обратите внимание, как на приведенном выше изображении по промежуточному слоям проверки подлинности оторван запрос.
Перенос кода модуля в ПО промежуточного слоя
Существующий модуль HTTP будет выглядеть примерно так:
// 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.
}
}
}
Как показано на странице ПО промежуточного слоя, по промежуточному поверх ASP.NET Core — это класс, который предоставляет Invoke
метод, принимаюющий HttpContext
и возвращающий объектTask
. Новый ПО промежуточного слоя будет выглядеть следующим образом:
// 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>();
}
}
}
Предыдущий шаблон ПО промежуточного слоя был взят из раздела по написанию ПО промежуточного слоя.
Вспомогательный класс MyMiddlewareExtensions упрощает настройку по промежуточного слоя в классе Startup
. Метод UseMyMiddleware
добавляет класс ПО промежуточного слоя в конвейер запроса. Службы, необходимые по промежуточному слоям, внедряются в конструктор ПО промежуточного слоя.
Модуль может завершить запрос, например, если пользователь не авторизован:
// 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;
}
}
ПО промежуточного слоя обрабатывает это, не вызывая Invoke
следующее ПО промежуточного слоя в конвейере. Помните, что это не полностью завершает запрос, так как предыдущие по промежуточному слоям по-прежнему будут вызываться, когда ответ делает свой путь обратно через конвейер.
// 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.
}
При переносе функциональных возможностей модуля в новое ПО промежуточного слоя вы можете обнаружить, что код не компилируется, так как HttpContext
класс значительно изменился в ASP.NET Core. Далее вы узнаете, как перейти на новый ASP.NET Core HttpContext.
Перенос вставки модуля в конвейер запросов
Модули HTTP обычно добавляются в конвейер запросов с помощью 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>
Преобразуйте это, добавив новое ПО промежуточного слоя в конвейер запросов в классе 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?}");
});
}
Точное место в конвейере, в котором вы вставляете новое ПО промежуточного слоя, зависит от события, которое он обрабатывает как модуль (BeginRequest
, EndRequest
и т. д.) и его порядок в списке модулей в web.config.
Как упоминалось ранее, жизненный цикл приложения в ASP.NET Core и порядок обработки ответов по промежуточному слоям отличается от порядка, используемого модулями. Это может сделать решение по заказу более сложным.
Если упорядочивание становится проблемой, модуль можно разделить на несколько компонентов ПО промежуточного слоя, которые можно упорядочить независимо.
Перенос кода обработчика в ПО промежуточного слоя
Обработчик HTTP выглядит примерно так:
// 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";
}
}
}
В проекте ASP.NET Core вы будете переводить это по промежуточному слоя, аналогичному этому:
// 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>();
}
}
}
Это ПО промежуточного слоя очень похоже на ПО промежуточного слоя, соответствующее модулям. Единственное реальное различие заключается в том, что здесь нет вызова _next.Invoke(context)
. Это имеет смысл, так как обработчик находится в конце конвейера запроса, поэтому вызов следующего ПО промежуточного слоя не будет.
Перенос вставки обработчика в конвейер запросов
Настройка обработчика HTTP выполняется в Web.config и выглядит примерно так:
<?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>
Это можно преобразовать, добавив новое ПО промежуточного слоя обработчика в конвейер запросов в Startup
классе, аналогично по промежуточному слоям, преобразованным из модулей. Проблема с этим подходом заключается в том, что все запросы будут отправляться в новое ПО промежуточного слоя обработчика. Однако для доступа к по промежуточному слоям требуется только запросы с заданным расширением. Это даст вам те же функциональные возможности, что и с обработчиком HTTP.
Одним из решений является ветвление конвейера запросов с заданным расширением с помощью MapWhen
метода расширения. Это выполняется в том же Configure
методе, в котором добавляется другое ПО промежуточного слоя:
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
принимает следующие параметры:
Лямбда-строка, которая принимает
HttpContext
и возвращаетtrue
, если запрос должен пойти вниз ветвь. Это означает, что запросы можно ветвить не только на основе их расширения, но и заголовков запросов, параметров строки запроса и т. д.Лямбда-код, который принимает
IApplicationBuilder
и добавляет все ПО промежуточного слоя для ветви. Это означает, что вы можете добавить дополнительное ПО промежуточного слоя в ветвь перед ПО промежуточного слоя обработчика.
По промежуточному слоям, добавленным в конвейер до вызова ветви во всех запросах; ветвь не будет влиять на них.
Загрузка параметров ПО промежуточного слоя с помощью шаблона параметров
Некоторые модули и обработчики имеют параметры конфигурации, хранящиеся в web.config. Однако в ASP.NET Core новая модель конфигурации используется вместо web.config.
Новая система конфигурации предоставляет следующие варианты решения:
Непосредственно вставьте параметры в ПО промежуточного слоя, как показано в следующем разделе.
Используйте шаблон параметров:
Создайте класс для хранения параметров по промежуточного слоя, например:
public class MyMiddlewareOptions { public string Param1 { get; set; } public string Param2 { get; set; } }
Хранение значений параметров
Система конфигурации позволяет хранить значения параметров в любом месте. Однако большинство сайтов используются
appsettings.json
, поэтому мы рассмотрим этот подход:{ "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
MyMiddlewareOptionsSectionsSection здесь — имя раздела. Он не должен совпадать с именем класса параметров.
Связывание значений параметров с классом параметров
Шаблон параметров использует платформу внедрения зависимостей ASP.NET Core для связывания типа параметров (например
MyMiddlewareOptions
, сMyMiddlewareOptions
объектом с фактическими параметрами).Startup
Обновите класс:Если вы используете
appsettings.json
, добавьте его в построитель конфигураций в конструктореStartup
: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(); }
Настройте службу параметров:
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(); }
Свяжите параметры с классом параметров:
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(); }
Вставьте параметры в конструктор ПО промежуточного слоя. Это аналогично внедрению параметров в контроллер.
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 } }
Метод расширения UseMiddleware, который добавляет по промежуточному слоям
IApplicationBuilder
к внедрению зависимостей.Это не ограничивается объектами
IOptions
. Любой другой объект, который требуется для по промежуточного слоя, можно внедрить таким образом.
Загрузка параметров ПО промежуточного слоя с помощью прямого внедрения
Шаблон параметров имеет преимущество, которое создает свободное связывание между значениями параметров и их потребителями. После того как вы связали класс параметров с фактическими значениями параметров, любой другой класс может получить доступ к параметрам через платформу внедрения зависимостей. Нет необходимости передавать значения параметров.
Это разбивается, хотя, если вы хотите использовать одно по промежуточное по промежуточное по промежуточному слоя дважды, с разными параметрами. Например, ПО промежуточного слоя авторизации, используемого в разных ветвях, разрешающих различные роли. Невозможно связать два разных объекта параметров с одним классом параметров.
Решение состоит в том, чтобы получить объекты параметров с фактическими значениями параметров в Startup
классе и передать их непосредственно каждому экземпляру по промежуточного слоя.
Добавление второго ключа в
appsettings.json
Чтобы добавить второй набор параметров в
appsettings.json
файл, используйте новый ключ для уникальной идентификации:{ "MyMiddlewareOptionsSection2": { "Param1": "Param1Value2", "Param2": "Param2Value2" }, "MyMiddlewareOptionsSection": { "Param1": "Param1Value", "Param2": "Param2Value" } }
Извлеките значения параметров и передайте их в ПО промежуточного слоя. Метод
Use...
расширения (который добавляет ПО промежуточного слоя в конвейер) является логическим местом для передачи значений параметров: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?}"); }); }
Включите параметр параметров по промежуточного слоя. Укажите перегрузку
Use...
метода расширения (который принимает параметр параметров и передает егоUseMiddleware
в ). ПриUseMiddleware
вызове с параметрами он передает параметры конструктору ПО промежуточного слоя при создании экземпляра объекта ПО промежуточного слоя.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)); } }
Обратите внимание, как это упаковывает объект options в
OptionsWrapper
объект. Это реализуетIOptions
, как ожидалось конструктором по промежуточного слоя.
Переход на новый HttpContext
Вы видели ранее, что Invoke
метод в по промежуточном слоя принимает параметр типа HttpContext
:
public async Task Invoke(HttpContext context)
HttpContext
значительно изменилось в ASP.NET Core. В этом разделе показано, как перевести наиболее часто используемые свойства System.Web.HttpContext в новые Microsoft.AspNetCore.Http.HttpContext
.
HttpContext
HttpContext.Items преобразуется в:
IDictionary<object, object> items = httpContext.Items;
Уникальный идентификатор запроса (без аналога System.Web.HttpContext)
Предоставляет уникальный идентификатор для каждого запроса. Очень полезно включить в журналы.
string requestId = httpContext.TraceIdentifier;
HttpContext.Request
HttpContext.Request.HttpMethod преобразуется в:
string httpMethod = httpContext.Request.Method;
HttpContext.Request.QueryString преобразуется в:
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 и HttpContext.Request.RawUrl преобразуют в:
// using Microsoft.AspNetCore.Http.Extensions;
var url = httpContext.Request.GetDisplayUrl();
HttpContext.Request.IsSecureConnection преобразуется в:
var isSecureConnection = httpContext.Request.IsHttps;
HttpContext.Request.UserHostAddress преобразуется в:
var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();
HttpContext.Request.Cookies преобразуется в:
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 преобразуется в:
var routeValue = httpContext.GetRouteValue("key");
HttpContext.Request.Headers преобразуется в:
// 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 преобразуется в:
string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();
HttpContext.Request.UrlReferrerer преобразуется в:
string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();
HttpContext.Request.ContentType преобразуется в:
// 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 преобразуется в:
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"];
}
Предупреждение
Чтение значений формы только в том случае, если подтип контента — x-www-form-urlencoded или form-data.
HttpContext.Request.InputStream преобразуется в:
string inputBody;
using (var reader = new System.IO.StreamReader(
httpContext.Request.Body, System.Text.Encoding.UTF8))
{
inputBody = reader.ReadToEnd();
}
Предупреждение
Используйте этот код только в ПО промежуточного слоя типа обработчика в конце конвейера.
Вы можете прочитать необработанный текст, как показано выше только один раз на запрос. ПО промежуточного слоя, пытающееся прочитать текст после первого чтения, считывает пустой текст.
Это не относится к чтению формы, как показано ранее, так как это делается из буфера.
HttpContext.Response
HttpContext.Response.Status и HttpContext.Response.StatusDescription преобразуют :
// using Microsoft.AspNetCore.Http;
httpContext.Response.StatusCode = StatusCodes.Status200OK;
HttpContext.Response.ContentEncoding и HttpContext.Response.ContentType преобразуются в:
// 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 также преобразуется в следующее:
httpContext.Response.ContentType = "text/html";
HttpContext.Response.Output преобразуется в следующее:
string responseContent = GetResponseContent();
await httpContext.Response.WriteAsync(responseContent);
HttpContext.Response.TransmitFile
Обслуживание файла рассматривается в разделе "Функции запроса" в ASP.NET Core.
HttpContext.Response.Headers
Отправка заголовков ответов усложняется тем, что если вы устанавливаете их после того, как все было записано в текст ответа, они не будут отправлены.
Решение состоит в том, чтобы задать метод обратного вызова, который будет вызываться прямо перед записью в ответ. Это лучше всего сделать в начале метода в ПО промежуточного Invoke
слоя. Это метод обратного вызова, который задает заголовки ответа.
Следующий код задает метод SetHeaders
обратного вызова:
public async Task Invoke(HttpContext httpContext)
{
// ...
httpContext.Response.OnStarting(SetHeaders, state: httpContext);
Метод обратного SetHeaders
вызова будет выглядеть следующим образом:
// 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
Файлы cookie отправляются в браузер в заголовке ответа Set.Cookie В результате отправка файлов cookie требует того же обратного вызова, что и для отправки заголовков ответов:
public async Task Invoke(HttpContext httpContext)
{
// ...
httpContext.Response.OnStarting(SetCookies, state: httpContext);
httpContext.Response.OnStarting(SetHeaders, state: httpContext);
Метод SetCookies
обратного вызова будет выглядеть следующим образом:
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);
}
Дополнительные ресурсы
ASP.NET Core