Перенос обработчиков 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

Порядок обработки входящих запросов модулей определяется следующим образом:

  1. События серии, вызванные ASP.NET, например BeginRequest и AuthenticateRequest. Полный список см. в разделе System.Web.HttpApplication. Каждый модуль может создать обработчик для одного или нескольких событий.

  2. Для того же события порядок, в котором они настроены в Web.config.

В дополнение к модулям можно добавить обработчики для событий жизненного цикла в Global.asax.cs файл. Эти обработчики выполняются после обработчиков в настроенных модулях.

От обработчиков и модулей до ПО промежуточного слоя

По промежуточному слоям проще, чем модули HTTP и обработчики:

  • Модули, обработчики, Global.asax.csweb.config (за исключением конфигурации IIS) и жизненный цикл приложения исчезли

  • Роли обоих модулей и обработчиков были переданы по промежуточному слоям

  • ПО промежуточного слоя настраиваются с помощью кода, а не в web.config

  • Ветвление конвейера позволяет отправлять запросы в определенное ПО промежуточного слоя, основанное не только на URL-адресе, но и на заголовках запросов, строках запроса и т. д.
  • Ветвление конвейера позволяет отправлять запросы в определенное ПО промежуточного слоя, основанное не только на URL-адресе, но и на заголовках запросов, строках запроса и т. д.

По промежуточному слоям очень похожи на модули:

ПО промежуточного слоя и модули обрабатываются в другом порядке:

По промежуточному по промежуточному слоям авторизации запрашивает пользователя, который не авторизован. Запрос на страницу индекса разрешен и обрабатывается ПО промежуточного слоя MVC. Запрос отчета о продажах разрешен и обрабатывается пользовательским ПО промежуточного слоя отчета.

Обратите внимание, как на приведенном выше изображении по промежуточному слоям проверки подлинности оторван запрос.

Перенос кода модуля в ПО промежуточного слоя

Существующий модуль 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 принимает следующие параметры:

  1. Лямбда-строка, которая принимает HttpContext и возвращает true , если запрос должен пойти вниз ветвь. Это означает, что запросы можно ветвить не только на основе их расширения, но и заголовков запросов, параметров строки запроса и т. д.

  2. Лямбда-код, который принимает IApplicationBuilder и добавляет все ПО промежуточного слоя для ветви. Это означает, что вы можете добавить дополнительное ПО промежуточного слоя в ветвь перед ПО промежуточного слоя обработчика.

По промежуточному слоям, добавленным в конвейер до вызова ветви во всех запросах; ветвь не будет влиять на них.

Загрузка параметров ПО промежуточного слоя с помощью шаблона параметров

Некоторые модули и обработчики имеют параметры конфигурации, хранящиеся в web.config. Однако в ASP.NET Core новая модель конфигурации используется вместо web.config.

Новая система конфигурации предоставляет следующие варианты решения:

  1. Создайте класс для хранения параметров по промежуточного слоя, например:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Хранение значений параметров

    Система конфигурации позволяет хранить значения параметров в любом месте. Однако большинство сайтов используются appsettings.json, поэтому мы рассмотрим этот подход:

    {
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    

    MyMiddlewareOptionsSectionsSection здесь — имя раздела. Он не должен совпадать с именем класса параметров.

  3. Связывание значений параметров с классом параметров

    Шаблон параметров использует платформу внедрения зависимостей ASP.NET Core для связывания типа параметров (например MyMiddlewareOptions, с MyMiddlewareOptions объектом с фактическими параметрами).

    Startup Обновите класс:

    1. Если вы используете 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();
      }
      
    2. Настройте службу параметров:

      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();
      }
      
    3. Свяжите параметры с классом параметров:

      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();
      }
      
  4. Вставьте параметры в конструктор ПО промежуточного слоя. Это аналогично внедрению параметров в контроллер.

    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 классе и передать их непосредственно каждому экземпляру по промежуточного слоя.

  1. Добавление второго ключа в appsettings.json

    Чтобы добавить второй набор параметров в appsettings.json файл, используйте новый ключ для уникальной идентификации:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. Извлеките значения параметров и передайте их в ПО промежуточного слоя. Метод 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?}");
        });
    }
    
  3. Включите параметр параметров по промежуточного слоя. Укажите перегрузку 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); 
}

Дополнительные ресурсы