次の方法で共有


ASP.NET Framework HttpContext を ASP.NET Core に移行する

HttpContext は Web アプリケーションの基本的なコンポーネントであり、HTTP 要求と応答情報へのアクセスを提供します。 ASP.NET Framework から ASP.NET Core に移行する場合、2 つのフレームワークには異なる API とアプローチがあるため、HttpContext には固有の課題があります。

HttpContext の移行が複雑な理由

ASP.NET Framework と ASP.NET Core には、基本的に異なる HttpContext 実装があります。

これらの違いは、変更なしで HttpContext コードを Framework から Core に単に移動できないことを意味します。

移行戦略の概要

移行中に HttpContext を処理するには、主に次の 2 つの方法があります。

  1. 完全な書き換え - すべての HttpContext コードを ASP.NET Core のネイティブ HttpContext 実装を使用するように書き換える
  2. System.Web アダプター - 増分移行中にアダプターを使用してコードの変更を最小限に抑える

ほとんどのアプリケーションでは、ASP.NET Core のネイティブ HttpContext に移行すると、最適なパフォーマンスと保守容易性が提供されます。 ただし、大規模なアプリケーションや、HttpContext を広範に使用するアプリケーションでは、増分移行中に System.Web アダプターを使用するとメリットが得られる場合があります。

移行方法を選ぶ

ASP.NET Framework から ASP.NET Core に HttpContext を移行するには、主に 2 つのオプションがあります。 選択は、移行のタイムライン、両方のアプリケーションを同時に実行する必要があるかどうか、および書き換えるコードの量によって異なります。

クイック デシジョン ガイド

次の質問に答えて、アプローチを選択します。

  1. 完全な書き換えまたは増分移行を行っていますか?

  2. 共有ライブラリ間で HttpContext を広範に使用していますか?

移行アプローチの比較

方法 コードの変更 [パフォーマンス] 共有ライブラリ 使用するタイミング
書き換えを完了する High - すべての HttpContext コードを書き換える 最高 更新プログラムが必要 完全な書き換え、パフォーマンスが重要なアプリ
System.Web アダプター 低 - 既存のパターンを保持する よし 既存のコードを使用する 増分移行、広範な HttpContext の使用

重要な違い

HttpContext の有効期間

アダプターは、要求の有効期間を過ぎても使用できない HttpContext によってサポートされます。 したがって、ASP.NET Core で実行する場合 HttpContext は要求を超えて使用することはできませんが、ASP.NET Framework では動作します。 ObjectDisposedException は、要求が終了した後に使用された場合にスローされます。

推奨事項: 必要な値を POCO に格納し、その値を保持します。

要求のスレッド処理に関する考慮事項

Warnung

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();

応答ストリームのバッファリング

Responseの一部の API では、OutputEnd()Clear()SuppressContentなど、出力ストリームをバッファー処理する必要があります。

推奨事項: 送信前に応答をバッファリングする必要がある Response の動作をサポートするには、エンドポイントは、 IBufferResponseStreamMetadataを実装するエンドポイント メタデータを使用して応答をオプトインする必要があります。

すべての MVC エンドポイントでこれを有効にするには、次のように使用できる拡張メソッドがあります。

app.MapDefaultControllerRoute()
    .BufferResponseStream();

ASP.NET Core HttpContext への書き換えを完了する

完全な移行を実行するときにこの方法を選択し、HttpContext 関連のコードを書き換えて、ASP.NET Core のネイティブ実装を使用できます。

ASP.NET Core の HttpContext は、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 の最も一般的に使用されるプロパティを、ASP.NET Core の同等の Microsoft.AspNetCore.Http.HttpContext に変換する方法について説明します。

HttpContext プロパティ

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.RawUrl 複数のプロパティを→ する

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

    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"];
    }
    

    警告: コンテンツ タイプが x-www-form-urlencoded または form-data の場合にのみフォーム値を読み取ります

  • HttpRequest.InputStreamHttpRequest.Body

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

    警告: パイプラインの最後のハンドラー ミドルウェアでのみ使用します。 本文は要求ごとに 1 回のみ読み取ることができます

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.TransmitFile要求機能を確認する

    ファイルの提供については、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);
    }
    

    応答が開始される前に、コールバック パターンを使用してヘッダーを設定する必要があります

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

    応答が開始される前に、コールバック パターンを使用して 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 アダプター

これにより、 System.Web アダプター を使用して移行が簡略化されます。

共有ライブラリ間で HttpContext を広範に使用する場合、またはコードの変更を最小限に抑える増分移行を実行する場合は、この方法を選択します。

System.Web アダプターには、ASP.NET Core アプリケーションで使い慣れたHttpContext API を使用できる互換性レイヤーが用意されています。 この方法は、次の場合に特に便利です。

  • あなたが共有しているライブラリはHttpContextを使用しています。
  • あなたは増分移行を実行している
  • 移行プロセス中にコードの変更を最小限に抑える必要がある

System.Web アダプターを使用する利点

  • 最小限のコード変更: 既存の System.Web.HttpContext 使用パターンを維持する
  • 共有ライブラリ: ライブラリは、ASP.NET Framework と ASP.NET Core の両方で動作します
  • 増分移行: 共有依存関係を中断せずにアプリケーションを 1 つずつ移行する
  • 移行の高速化: 複雑なアプリケーションの移行に必要な時間を短縮する

考慮事項

  • パフォーマンス: 適切ですが、アダプターではネイティブ ASP.NET Core API と比較してオーバーヘッドが発生します
  • 機能パリティ: アダプターを通じてすべての HttpContext 機能を使用できるわけではありません
  • 長期的な戦略: 最適なパフォーマンスを得るために、最終的にネイティブ ASP.NET Core API に移行することを検討する

System.Web アダプターの詳細については、 System.Web アダプターのドキュメントを参照してください

その他のリソース