Sdílet prostřednictvím


Migrace HttpContextu z ASP.NET Frameworku na ASP.NET Core

HttpContext je základní komponenta webových aplikací, která poskytuje přístup k informacím o požadavku a odpovědi HTTP. Při migraci z architektury ASP.NET na ASP.NET Core představuje HttpContext jedinečné výzvy, protože obě architektury mají různá rozhraní API a přístupy.

Proč je migrace HttpContext složitá

ASP.NET Framework a ASP.NET Core mají v podstatě různé implementace HttpContext:

Tyto rozdíly znamenají, že kód HttpContext nemůžete jednoduše přesunout z architektury do jádra beze změn.

Přehled strategií migrace

Během migrace máte dva hlavní přístupy pro zpracování httpContextu:

  1. Úplné přepsání – Přepište veškerý kód HttpContext tak, aby používal nativní implementaci HttpContext ASP.NET Core.
  2. System.Web Adapters – Použití adaptérů k minimalizaci změn kódu při přírůstkové migraci

U většiny aplikací poskytuje migrace na nativní httpContext ASP.NET Core nejlepší výkon a udržovatelnost. Při přírůstkové migraci mohou být větší aplikace nebo ty s rozsáhlým využitím HttpContextu přínosné díky použití adaptérů System.Web.

Volba přístupu k migraci

Máte dvě hlavní možnosti migrace HttpContext z ASP.NET Framework na ASP.NET Core. Vaše volba závisí na časové ose migrace, jestli potřebujete současně spouštět obě aplikace a kolik kódu jste ochotni přepsat.

Průvodce rychlým rozhodováním

Odpovězte na tyto otázky a zvolte svůj přístup:

  1. Provádíte úplný přepis nebo přírůstkovou migraci?

  2. Máte rozsáhlé využití HttpContext napříč sdílenými knihovnami?

Porovnání přístupů k migraci

Přístup Změny kódu Výkon Sdílené knihovny Kdy použít
Dokončení přepsání Vysoká – Přepsat veškerý kód HttpContext Nejlepší Vyžaduje aktualizace. Kompletní přepracování, aplikace důležité pro výkon
System.Web adapters Nízká – zachování existujících vzorů Dobré Funguje s existujícím kódem. Přírůstkové migrace, rozsáhlé využití HttpContext

Důležité rozdíly

Doba života httpContext

Adaptéry jsou podporovány pomocí HttpContext, který nelze používat po dobu životnosti požadavku. Když je HttpContext spuštěn na ASP.NET Core, nelze jej použít ani po dokončení požadavku, zatímco na ASP.NET Framework může někdy fungovat. V případech, kdy je použito za koncem požadavku, bude vyvoláno ObjectDisposedException.

Doporučení: Uložte potřebné hodnoty do POCO a uchovejte je.

Požadavky na aspekty dělení na vlákna

Výstraha

ASP.NET Core nezaručuje afinitu vláken pro požadavky. Pokud váš kód vyžaduje bezpečný přístup pomocí vláken k HttpContext, musíte zajistit správnou synchronizaci.

V ASP.NET Framework má požadavek spřažení vláken a Current je k dispozici pouze na tomto vlákně. ASP.NET Core tuto záruku nemá, takže Current bude k dispozici ve stejném asynchronním kontextu, ale nebudou provedeny žádné záruky týkající se vláken.

Doporučení: Při čtení a zápisu do HttpContext je nutné zajistit, abyste to udělali jednovláknově. Můžete vynutit, aby se požadavek nikdy nespustil souběžně v žádném asynchronním kontextu nastavením ISingleThreadedRequestMetadata. To bude mít vliv na výkon a mělo by se použít jenom v případě, že nemůžete refaktorovat využití, abyste zajistili nesouběhový přístup. K dispozici je implementace pro přidání kontrolerů s SingleThreadedRequestAttribute:

[SingleThreadedRequest]
public class SomeController : Controller
{
    ...
} 

Ukládání síťových požadavků do vyrovnávací paměti streamů

Ve výchozím nastavení není příchozí požadavek vždy vyhledatelný ani plně dostupný. Pokud chcete dosáhnout chování viditelného v rozhraní .NET Framework, můžete se přihlásit k předběžnému načtení vstupního datového proudu. Příchozí datový proud se tímto plně načte a uloží do vyrovnávací paměti nebo na disk (v závislosti na nastavení).

Doporučení: Můžete to povolit použitím metadat koncového bodu, která implementují rozhraní. To je k dispozici jako atribut PreBufferRequestStreamAttribute , který lze použít pro kontrolery nebo metody.

Pokud to chcete povolit pro všechny koncové body MVC, existuje metoda rozšíření, která se dá použít následujícím způsobem:

app.MapDefaultControllerRoute()
    .PreBufferRequestStream();

Ukládání streamů odpovědí do vyrovnávací paměti

Některá rozhraní API Response vyžadují uložení výstupního datového proudu do vyrovnávací paměti, například Output, End(), Clear()a SuppressContent.

Doporučení: Aby bylo možné podporovat chování Response, které vyžaduje uložení odpovědi do vyrovnávací paměti před odesláním, musí koncové body aktivovat tuto možnost pomocí implementace metadat koncového bodu IBufferResponseStreamMetadata.

Pokud to chcete povolit pro všechny koncové body MVC, existuje metoda rozšíření, která se dá použít následujícím způsobem:

app.MapDefaultControllerRoute()
    .BufferResponseStream();

Dokončení přepsání na ASP.NET Core HttpContext

Tento přístup zvolte, když provádíte úplnou migraci a můžete přepsat kód související s HttpContext tak, aby používal nativní implementaci ASP.NET Core.

ASP.NET Core HttpContext poskytuje modulární a rozšiřitelný návrh v porovnání s ASP.NET Framework. Tento přístup nabízí nejlepší výkon, ale během migrace vyžaduje více změn kódu.

Přehled

HttpContext byla výrazně změněna v ASP.NET Core. Při migraci modulů HTTP nebo obslužných rutin do middlewaru budete muset aktualizovat kód, aby fungoval s novým HttpContext rozhraním API.

V middlewaru Invoke ASP.NET Core používá metoda parametr typu HttpContext:

public async Task Invoke(HttpContext context)

To HttpContext se liší od verze ASP.NET Framework a vyžaduje různé přístupy k přístupu k informacím o žádostech a odpovědích.

Překlady vlastností

Tato část ukazuje, jak přeložit nejčastěji používané vlastnosti System.Web.HttpContext na ekvivalent Microsoft.AspNetCore.Http.HttpContext v ASP.NET Core.

Vlastnosti HttpContext

Vlastnosti HttpRequest

  • HttpRequest.HttpMethodHttpRequest.Method

    string httpMethod = httpContext.Request.Method;
    
  • HttpRequest.QueryStringHttpRequest.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.RawUrlvíce vlastností

    // using Microsoft.AspNetCore.Http.Extensions;
    var url = httpContext.Request.GetDisplayUrl();
    

    Použijte Request.Scheme, Host, PathBase, Path, QueryString

  • HttpRequest.IsSecureConnectionHttpRequest.IsHttps

    var isSecureConnection = httpContext.Request.IsHttps;
    
  • HttpRequest.UserHostAddressConnectionInfo.RemoteIpAddress

    var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();
    
  • HttpRequest.CookiesHttpRequest.Cookies

    IRequestCookieCollection cookies = httpContext.Request.Cookies;
    string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception)
    string knownCookieValue = cookies["cookie1name"];     // will be actual value
    
  • HttpRequest.RequestContextRoutingHttpContextExtensions.GetRouteData

    var routeValue = httpContext.GetRouteValue("key");
    
  • HttpRequest.HeadersHttpRequest.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.UserAgentHttpRequest.Headers

    string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();
    
  • HttpRequest.UrlReferrerHttpRequest.Headers

    string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();
    
  • HttpRequest.ContentTypeHttpRequest.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.FormHttpRequest.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"];
    }
    

    Upozornění: Čtení hodnot formuláře pouze v případě, že je typ obsahu x-www-form-urlencoded nebo form-data

  • HttpRequest.InputStreamHttpRequest.Body

    string inputBody;
    using (var reader = new System.IO.StreamReader(
        httpContext.Request.Body, System.Text.Encoding.UTF8))
    {
        inputBody = reader.ReadToEnd();
    }
    

    Upozornění: Používejte pouze v obslužném middlewaru na konci řetězce zpracování. Text se dá na žádost přečíst jen jednou.

Vlastnosti HttpResponse

  • HttpResponse.Status / HttpResponse.StatusDescriptionHttpResponse.StatusCode

    // using Microsoft.AspNetCore.Http;
    httpContext.Response.StatusCode = StatusCodes.Status200OK;
    
  • HttpResponse.ContentEncoding / HttpResponse.ContentTypeHttpResponse.ContentType

    // using Microsoft.Net.Http.Headers;
    var mediaType = new MediaTypeHeaderValue("application/json");
    mediaType.Encoding = System.Text.Encoding.UTF8;
    httpContext.Response.ContentType = mediaType.ToString();
    
  • HttpResponse.ContentTypeHttpResponse.ContentType

    httpContext.Response.ContentType = "text/html";
    
  • HttpResponse.OutputHttpResponseWritingExtensions.WriteAsync

    string responseContent = GetResponseContent();
    await httpContext.Response.WriteAsync(responseContent);
    
  • HttpResponse.TransmitFileZobrazit funkce žádostí

    Obsluha souborů je popsána v tématu Funkce žádosti v ASP.NET Core

  • HttpResponse.HeadersHttpResponse.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);
    }
    

    Před zahájením odpovědi je nutné nastavit hlavičky pomocí vzoru zpětného volání.

  • HttpResponse.CookiesHttpResponse.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); 
    }
    

    Před zahájením odpovědi je nutné použít vzor zpětného volání k nastavení souborů cookie.

  • Nastavení hlaviček odpovědi:

    public async Task Invoke(HttpContext httpContext)
    {
        // Set callback to execute before response starts
        httpContext.Response.OnStarting(SetHeaders, state: httpContext);
        // ... rest of middleware logic
    }
    
  • Nastavení souborů cookie odpovědi:

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 adaptéry

Poznámka:

Díky tomu se používají adaptéry System.Web Adapters ke zjednodušení migrace.

Tento přístup zvolte, pokud máte rozsáhlé využití HttpContext ve sdílených knihovnách nebo při provádění přírůstkové migrace, ve které chcete minimalizovat změny kódu.

Adaptéry System.Web poskytují vrstvu kompatibility, která umožňuje používat známá HttpContext rozhraní API v aplikacích ASP.NET Core. Tento přístup je užitečný zejména v těchto případech:

  • Máte sdílené knihovny, které používají HttpContext
  • Provádíte přírůstkovou migraci
  • Chcete minimalizovat změny kódu během procesu migrace.

Výhody používání adaptérů System.Web

  • Minimální změny kódu: Zachování stávajících System.Web.HttpContext vzorů použití
  • Sdílené knihovny: Knihovny můžou pracovat s rozhraním ASP.NET Framework a ASP.NET Core
  • Přírůstková migrace: Migrace aplikací kusem bez přerušení sdílených závislostí
  • Rychlejší migrace: Zkrácení doby potřebné k migraci složitých aplikací

Úvahy

  • Výkon: I když je to dobré, adaptéry představují v porovnání s nativními rozhraními API pro ASP.NET Core určitou režii.
  • Parita funkcí: Ne všechny HttpContext funkce jsou dostupné prostřednictvím adaptérů.
  • Dlouhodobá strategie: Zvažte možnost nakonec migrovat na nativní rozhraní API ASP.NET Core pro zajištění nejlepšího výkonu.

Další informace o adaptérech System.Web najdete v dokumentaci k adaptérům System.Web.

Dodatečné zdroje