Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
HttpContext — это фундаментальный компонент веб-приложений, предоставляющий доступ к данным HTTP-запроса и ответа. При переходе с ASP.NET Framework на ASP.NET Core HttpContext представляет уникальные проблемы, так как две платформы имеют разные API и подходы.
Почему миграция HttpContext является сложной
ASP.NET Framework и ASP.NET Core имеют принципиально разные реализации HttpContext:
- ASP.NET Framework используется System.Web.HttpContext со встроенными свойствами и методами
- ASP.NET Core используется Microsoft.AspNetCore.Http.HttpContext с более модульным расширяемым дизайном
Эти различия означают, что вы не можете просто переместить код HttpContext из Платформы в Core без изменений.
Обзор стратегий миграции
Во время миграции существует два основных подхода к обработке HttpContext:
- Полная перезапись - перепишите весь код HttpContext для использования собственной реализации HttpContext в ASP.NET Core.
- Адаптеры System.Web — используйте адаптеры для минимизации изменений кода при переносе постепенно
Для большинства приложений миграция на собственный HttpContext ядра ASP.NET обеспечивает оптимальную производительность и удобство обслуживания. Однако более крупные приложения или те, у которых широкое использование HttpContext, могут воспользоваться адаптерами System.Web во время добавочной миграции.
Выбор способа миграции
У вас есть два основных варианта переноса HttpContext из ASP.NET Framework в ASP.NET Core. Выбор зависит от временной шкалы миграции, нужно ли одновременно запускать оба приложения и сколько кода вы хотите переписать.
Краткое руководство по принятию решений
Ответьте на эти вопросы, чтобы выбрать подход:
Выполняется ли полная перезапись или добавочная миграция?
- Полная перезапись в ASP.NET Core HttpContext
- Добавочная миграция → Продолжить вопрос 2
У вас есть обширное использование HttpContext в общих библиотеках?
- Да, множество общего кода → адаптеров System.Web
- Нет, изолированное использование HttpContext → Завершено перезапись в ASP.NET Core HttpContext
Сравнение подходов к миграции
| Подход | Изменения кода | Производительность | Общие библиотеки | Когда следует использовать |
|---|---|---|---|---|
| Полная перезапись | High — перезапись всего кода HttpContext | Лучший | Требуется обновление | Полные перезаписи и приложения, критически важные для производительности |
| Адаптеры System.Web | Низкая— сохранение существующих шаблонов | Хорошо | Работает с существующим кодом | Добавочные миграции, широкое использование HttpContext |
Важные различия
Время существования HttpContext
Адаптеры поддерживаются HttpContext, которые не могут использоваться после срока действия запроса. Таким образом, HttpContext при запуске на ASP.NET Core не может использоваться после выполнения запроса, в то время как на ASP.NET Framework он иногда работает. Будет выброшено ObjectDisposedException в случаях, когда оно используется после завершения выполнения запроса.
Рекомендация: Сохраните необходимые значения в POCO и храните их там.
Соображения по потокам в запросах
Предупреждение
ASP.NET Core не гарантирует сходство потоков для запросов. Если для кода требуется потокобезопасный доступ к HttpContext, необходимо обеспечить правильную синхронизацию.
В ASP.NET Framework запрос был привязан к потоку, и Current был бы доступен только на этом потоке. ASP.NET Core не имеет этой гарантии, поэтому Current будет доступен в том же асинхронном контексте, но никаких гарантий о потоках не даётся.
Рекомендация: при чтении и записи в HttpContext, необходимо убедиться, что вы делаете это в однопоточном режиме. Вы можете принудительно заставить запрос никогда не выполняться одновременно в любом асинхронном контексте, задав параметр ISingleThreadedRequestMetadata. Это будет иметь последствия для производительности и должно использоваться только если вы не можете изменить код, чтобы обеспечить несобместный доступ. Существует реализация, доступная для добавления в контроллеры с помощью SingleThreadedRequestAttribute:
[SingleThreadedRequest]
public class SomeController : Controller
{
...
}
Буферизация потоков запроса
По умолчанию входящие запросы не всегда доступны и не полностью доступны. Чтобы получить поведение, наблюдаемое в .NET Framework, можно выбрать предварительную буферизацию входного потока. Это полностью считывает входящий поток и буферизирует его в память или диск (в зависимости от параметров).
Рекомендация. Это можно включить, применяя метаданные конечной IPreBufferRequestStreamMetadata точки, реализующие интерфейс. Это доступно как атрибут PreBufferRequestStreamAttribute , который можно применить к контроллерам или методам.
Чтобы включить это для всех конечных точек MVC, существует метод расширения, который можно использовать следующим образом:
app.MapDefaultControllerRoute()
.PreBufferRequestStream();
Буферизация потока ответа
Для некоторых API Response требуется буферизация выходного потока, например Output, End(), Clear()и SuppressContent.
Рекомендация: Чтобы поддержать поведение для Response, которое требует буферизации ответа перед отправкой, конечные точки должны активировать эту функцию с помощью метаданных конечной точки, реализующих IBufferResponseStreamMetadata.
Чтобы включить это для всех конечных точек MVC, существует метод расширения, который можно использовать следующим образом:
app.MapDefaultControllerRoute()
.BufferResponseStream();
Завершение перезаписи в ASP.NET Core HttpContext
Выберите этот подход при выполнении полной миграции и когда вы можете переписать код, связанный с HttpContext, для использования собственной реализации ASP.NET Core.
ASP.NET HttpContext Core обеспечивает более модульный и расширяемый дизайн по сравнению с ASP.NET Framework. Этот подход обеспечивает лучшую производительность, но требует больше изменений кода во время миграции.
Обзор
HttpContext значительно изменился в ASP.NET Core. При миграции модулей HTTP или обработчиков на промежуточное программное обеспечение необходимо обновить код для работы с новым HttpContext API.
В промежуточном слое ASP.NET Core метод Invoke принимает параметр типа HttpContext:
public async Task Invoke(HttpContext context)
Это HttpContext отличается от версии ASP.NET Framework и требует различных подходов к доступу к информации о запросах и ответах.
Переводы свойств
В этом разделе показано, как преобразовать наиболее часто используемые свойства System.Web.HttpContext в эквивалент Microsoft.AspNetCore.Http.HttpContext в ASP.NET Core.
Свойства HttpContext
HttpContext.Items → HttpContext.Items
IDictionary<object, object> items = httpContext.Items;Нет эквивалента → HttpContext.TraceIdentifier
string requestId = httpContext.TraceIdentifier;Уникальный идентификатор запроса для журналирования
Свойства HttpRequest
HttpRequest.HttpMethod → HttpRequest.Method
string httpMethod = httpContext.Request.Method;HttpRequest.QueryString → HttpRequest.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();HttpRequest.Url / HttpRequest.RawUrl → нескольких свойств
// using Microsoft.AspNetCore.Http.Extensions; var url = httpContext.Request.GetDisplayUrl();Использование Request.Scheme, Host, PathBase, Path, QueryString
HttpRequest.IsSecureConnection → HttpRequest.IsHttps
var isSecureConnection = httpContext.Request.IsHttps;HttpRequest.UserHostAddress → ConnectionInfo.RemoteIpAddress
var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();HttpRequest.Cookies → HttpRequest.Cookies
IRequestCookieCollection cookies = httpContext.Request.Cookies; string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception) string knownCookieValue = cookies["cookie1name"]; // will be actual valueHttpRequest.RequestContext → RoutingHttpContextExtensions.GetRouteData
var routeValue = httpContext.GetRouteValue("key");HttpRequest.Headers → HttpRequest.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();HttpRequest.UserAgent → HttpRequest.Headers
string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();HttpRequest.UrlReferrer → HttpRequest.Headers
string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();HttpRequest.ContentType → HttpRequest.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;HttpRequest.Form → HttpRequest.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
HttpRequest.InputStream → HttpRequest.Body
string inputBody; using (var reader = new System.IO.StreamReader( httpContext.Request.Body, System.Text.Encoding.UTF8)) { inputBody = reader.ReadToEnd(); }Предупреждение: Используйте только в промежуточных слоях обработчика в конце конвейера. Объект может быть прочитан только один раз за запрос
Свойства HttpResponse
HttpResponse.Status / HttpResponse.StatusDescription → HttpResponse.StatusCode
// using Microsoft.AspNetCore.Http; httpContext.Response.StatusCode = StatusCodes.Status200OK;HttpResponse.ContentEncoding / HttpResponse.ContentType → HttpResponse.ContentType
// using Microsoft.Net.Http.Headers; var mediaType = new MediaTypeHeaderValue("application/json"); mediaType.Encoding = System.Text.Encoding.UTF8; httpContext.Response.ContentType = mediaType.ToString();HttpResponse.ContentType → HttpResponse.ContentType
httpContext.Response.ContentType = "text/html";HttpResponse.Output → HttpResponseWritingExtensions.WriteAsync
string responseContent = GetResponseContent(); await httpContext.Response.WriteAsync(responseContent);HttpResponse.TransmitFile → См. функции запроса
Обслуживание файлов рассматривается в функциях запроса в ASP.NET Core
HttpResponse.Headers → HttpResponse.OnStarting
// 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); }Необходимо использовать шаблон обратного вызова для задания заголовков перед запуском ответа
HttpResponse.Cookies → HttpResponse.OnStarting
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); }Необходимо использовать шаблон обратного вызова для задания файлов cookie перед запуском ответа
Настройка заголовков ответов:
public async Task Invoke(HttpContext httpContext) { // Set callback to execute before response starts httpContext.Response.OnStarting(SetHeaders, state: httpContext); // ... rest of middleware logic }Настройка файлов cookie ответа:
public async Task Invoke(HttpContext httpContext)
{
// Set callbacks to execute before response starts
httpContext.Response.OnStarting(SetCookies, state: httpContext);
httpContext.Response.OnStarting(SetHeaders, state: httpContext);
// ... rest of middleware logic
}
Адаптеры System.Web
Замечание
Это позволяет использовать системные веб-адаптеры для упрощения миграции.
Выберите этот подход при обширном использовании HttpContext в общих библиотеках или при добавочной миграции, в которой требуется свести к минимуму изменения кода.
Адаптеры System.Web предоставляют уровень совместимости, который позволяет использовать знакомые HttpContext API в приложениях ASP.NET Core. Этот подход особенно полезен, когда:
- У вас есть общие библиотеки, использующие HttpContext
- Вы выполняете добавочную миграцию
- Вы хотите свести к минимуму изменения кода во время процесса миграции
Преимущества использования адаптеров System.Web
-
Минимальные изменения кода. Сохранение существующих
System.Web.HttpContextшаблонов использования - Общие библиотеки: библиотеки могут работать как с ASP.NET Framework, так и с ASP.NET Core
- Добавочная миграция. Перенос приложений по частям без нарушения общих зависимостей
- Ускорение миграции. Сокращение времени, необходимого для миграции сложных приложений
Соображения
- Производительность: хотя адаптеры работают хорошо, они вводят некоторые издержки по сравнению с родными API ASP.NET Core
- Функциональное соответствие: Не все HttpContext возможности доступны через адаптеры
- Долгосрочная стратегия. Рассмотрите возможность миграции в собственные api-интерфейсы ASP.NET Core для оптимальной производительности.
Дополнительные сведения о адаптерах System.Web см. в документации по адаптерам System.Web.
Дополнительные ресурсы
ASP.NET Core