HTTP ハンドラーとモジュールを ASP.NET Core ミドルウェアに移行するMigrate HTTP handlers and modules to ASP.NET Core middleware

この記事では、既存の ASP.NET HTTP モジュールとハンドラーを system.webserver から ASP.NET Core ミドルウェアに移行する方法について説明します。This article shows how to migrate existing ASP.NET HTTP modules and handlers from system.webserver to ASP.NET Core middleware.

再検討されたモジュールとハンドラーModules and handlers revisited

ミドルウェアの ASP.NET Core に進む前に、まず HTTP モジュールとハンドラーがどのように機能するかについて説明します。Before proceeding to ASP.NET Core middleware, let's first recap how HTTP modules and handlers work:

モジュールハンドラー

ハンドラーは次のとおりです。Handlers are:

  • IHttpHandlerを実装するクラスClasses that implement IHttpHandler

  • 指定されたファイル名または拡張子を持つ要求を処理するために使用され ます。レポートなどです。Used to handle requests with a given file name or extension, such as .report

  • Web.config構成済みConfigured in Web.config

モジュールは次のとおりです。Modules are:

  • IHttpModuleを実装するクラスClasses that implement IHttpModule

  • すべての要求に対して呼び出されますInvoked for every request

  • ショートサーキットを可能にする (要求の後続の処理を停止する)Able to short-circuit (stop further processing of a request)

  • HTTP 応答に追加することも、独自の応答を作成することもできます。Able to add to the HTTP response, or create their own

  • Web.config構成済みConfigured in Web.config

受信要求を処理するモジュールの順序は、次のように決定されます。The order in which modules process incoming requests is determined by:

  1. は、 /previous-versions/ms227673(v=vs.140) ASP.NET: BeginRequestAuthenticateRequestなどによって発生するシリーズイベントです。各モジュールは、1つまたは複数のイベントのハンドラーを作成できます。The /previous-versions/ms227673(v=vs.140), which is a series events fired by ASP.NET: BeginRequest, AuthenticateRequest, etc. Each module can create a handler for one or more events.

  2. 同じイベントの場合、 Web.config で構成されている順序。For the same event, the order in which they're configured in Web.config.

モジュールに加えて、ライフサイクルイベントのハンドラーを Global.asax.cs ファイルに追加することができます。In addition to modules, you can add handlers for the life cycle events to your Global.asax.cs file. これらのハンドラーは、構成されているモジュールのハンドラーの後に実行されます。These handlers run after the handlers in the configured modules.

ハンドラーとモジュールからミドルウェアへFrom handlers and modules to middleware

ミドルウェアは、HTTP モジュールとハンドラーよりも簡単です。Middleware are simpler than HTTP modules and handlers:

  • モジュール、ハンドラー、 Global.asax.csWeb.config (IIS 構成を除く) とアプリケーションのライフサイクルが失われるModules, handlers, Global.asax.cs , Web.config (except for IIS configuration) and the application life cycle are gone

  • ミドルウェアによってモジュールとハンドラーの両方のロールが取得されています。The roles of both modules and handlers have been taken over by middleware

  • ミドルウェアは、ではなくコードを使用して構成 Web.configMiddleware are configured using code rather than in Web.config

  • パイプライン分岐 を使用すると、URL だけでなく、要求ヘッダー、クエリ文字列などに基づいて、特定のミドルウェアに要求を送信できます。Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.
  • パイプライン分岐 を使用すると、URL だけでなく、要求ヘッダー、クエリ文字列などに基づいて、特定のミドルウェアに要求を送信できます。Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.

ミドルウェアはモジュールとよく似ています。Middleware are very similar to modules:

ミドルウェアとモジュールは、別の順序で処理されます。Middleware and modules are processed in a different order:

ミドルウェア

上の図のように、認証ミドルウェアは要求のショートサーキットを行います。Note how in the image above, the authentication middleware short-circuited the request.

ミドルウェアへのモジュールコードの移行Migrating module code to middleware

既存の HTTP モジュールは次のようになります。An existing HTTP module will look similar to this:

// 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 TaskAs shown in the Middleware page, an ASP.NET Core middleware is a class that exposes an Invoke method taking an HttpContext and returning a Task. 新しいミドルウェアは次のようになります。Your new middleware will look like this:

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

前のミドルウェアテンプレートは、 ミドルウェアの作成に関するセクションから取得されました。The preceding middleware template was taken from the section on writing middleware.

MyMiddlewareExtensions helper クラスを使用すると、クラスでミドルウェアを簡単に構成 Startup できます。The MyMiddlewareExtensions helper class makes it easier to configure your middleware in your Startup class. メソッドは、 UseMyMiddleware 要求パイプラインにミドルウェアクラスを追加します。The UseMyMiddleware method adds your middleware class to the request pipeline. ミドルウェアが必要とするサービスは、ミドルウェアのコンストラクターに挿入されます。Services required by the middleware get injected in the middleware's constructor.

ユーザーが承認されていないなど、モジュールが要求を終了する場合があります。Your module might terminate a request, for example if the user isn't authorized:

// 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 、パイプラインの次のミドルウェアでを呼び出さずにこの処理を行います。A middleware handles this by not calling Invoke on the next middleware in the pipeline. これは、要求を完全に終了しないことに注意してください。これは、応答がパイプラインを通過するときに前のミドルウェアが呼び出されるためです。Keep in mind that this doesn't fully terminate the request, because previous middlewares will still be invoked when the response makes its way back through the pipeline.

// 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 で大幅に変更されているため、コードがコンパイルされないことがあります。When you migrate your module's functionality to your new middleware, you may find that your code doesn't compile because the HttpContext class has significantly changed in ASP.NET Core. で、新しい ASP.NET Core HttpContext に移行する方法について説明します。Later on, you'll see how to migrate to the new ASP.NET Core HttpContext.

要求パイプラインへのモジュール挿入の移行Migrating module insertion into the request pipeline

HTTP モジュールは通常、 Web.config を使用して要求パイプラインに追加されます。HTTP modules are typically added to the request pipeline using 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 ます。Convert this by adding your new middleware to the request pipeline in your Startup class:

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 内のモジュールの一覧での順序によって異なります。The exact spot in the pipeline where you insert your new middleware depends on the event that it handled as a module (BeginRequest, EndRequest, etc.) and its order in your list of modules in Web.config.

前述のように、ASP.NET Core にはアプリケーションのライフサイクルがなく、ミドルウェアによって応答が処理される順序は、モジュールで使用される順序とは異なります。As previously stated, there's no application life cycle in ASP.NET Core and the order in which responses are processed by middleware differs from the order used by modules. これにより、順序を決定することが困難になる可能性があります。This could make your ordering decision more challenging.

順序付けが問題になった場合は、個別に並べ替えることのできる複数のミドルウェアコンポーネントにモジュールを分割できます。If ordering becomes a problem, you could split your module into multiple middleware components that can be ordered independently.

ミドルウェアへのハンドラーコードの移行Migrating handler code to middleware

HTTP ハンドラーは次のようになります。An HTTP handler looks something like this:

// 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 プロジェクトでは、これを次のようなミドルウェアに変換します。In your ASP.NET Core project, you would translate this to a middleware similar to this:

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

このミドルウェアは、モジュールに対応するミドルウェアと非常によく似ています。This middleware is very similar to the middleware corresponding to modules. 実際の違いは、ここではへの呼び出しがないことです _next.Invoke(context)The only real difference is that here there's no call to _next.Invoke(context). これは、ハンドラーが要求パイプラインの最後にあるため、次に呼び出すミドルウェアが存在しないため、意味があります。That makes sense, because the handler is at the end of the request pipeline, so there will be no next middleware to invoke.

要求パイプラインへのハンドラー挿入の移行Migrating handler insertion into the request pipeline

HTTP ハンドラーの構成は Web.config で行われ、次のようになります。Configuring an HTTP handler is done in Web.config and looks something like this:

<?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 ます。これは、モジュールから変換されたミドルウェアに似ています。You could convert this by adding your new handler middleware to the request pipeline in your Startup class, similar to middleware converted from modules. この方法の問題は、すべての要求を新しいハンドラーミドルウェアに送信することです。The problem with that approach is that it would send all requests to your new handler middleware. ただし、特定の拡張機能を持つ要求のみがミドルウェアに到着するようにします。However, you only want requests with a given extension to reach your middleware. これにより、HTTP ハンドラーと同じ機能が提供されます。That would give you the same functionality you had with your HTTP handler.

1つの解決策は、拡張メソッドを使用して、特定の拡張機能を持つ要求に対してパイプラインを分岐することです MapWhenOne solution is to branch the pipeline for requests with a given extension, using the MapWhen extension method. これは、もう一方のミドルウェアを追加するのと同じ方法で行い Configure ます。You do this in the same Configure method where you add the other middleware:

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 次のパラメーターを取得します。MapWhen takes these parameters:

  1. を受け取るラムダ HttpContext true 。要求が分岐を下位に移動する必要がある場合は、を返します。A lambda that takes the HttpContext and returns true if the request should go down the branch. これは、拡張機能だけでなく、要求ヘッダー、クエリ文字列パラメーターなどにもブランチ要求を分岐できることを意味します。This means you can branch requests not just based on their extension, but also on request headers, query string parameters, etc.

  2. を受け取り、 IApplicationBuilder 分岐のすべてのミドルウェアを追加するラムダ。A lambda that takes an IApplicationBuilder and adds all the middleware for the branch. これは、ハンドラーミドルウェアの前に分岐にミドルウェアを追加できることを意味します。This means you can add additional middleware to the branch in front of your handler middleware.

ブランチがすべての要求で呼び出される前に、ミドルウェアがパイプラインに追加されました。ブランチには影響がありません。Middleware added to the pipeline before the branch will be invoked on all requests; the branch will have no impact on them.

オプションパターンを使用したミドルウェアオプションの読み込みLoading middleware options using the options pattern

一部のモジュールとハンドラーには、 Web.config に格納されている構成オプションがあります。ただし ASP.NET Core では、 Web.config の代わりに新しい構成モデルが使用されます。Some modules and handlers have configuration options that are stored in Web.config. However, in ASP.NET Core a new configuration model is used in place of Web.config.

新しい 構成システム には、これを解決するための次のオプションが用意されています。The new configuration system gives you these options to solve this:

  1. ミドルウェアオプションを保持するクラスを作成します。次に例を示します。Create a class to hold your middleware options, for example:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. オプションの値を格納するStore the option values

    構成システムでは、オプションの値を任意の場所に格納できます。The configuration system allows you to store option values anywhere you want. ただし、ほとんどのサイトではを使用しているので、 appsettings.json 次の方法を使用します。However, most sites use appsettings.json , so we'll take that approach:

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

    MyMiddlewareOptionsSection ここにはセクション名があります。MyMiddlewareOptionsSection here is a section name. オプションクラスの名前と同じである必要はありません。It doesn't have to be the same as the name of your options class.

  3. オプションの値を options クラスに関連付けるAssociate the option values with the options class

    オプションのパターンでは ASP.NET Core の依存関係挿入フレームワークを使用して、オプションの種類 (など MyMiddlewareOptions ) を MyMiddlewareOptions 実際のオプションを持つオブジェクトに関連付けます。The options pattern uses ASP.NET Core's dependency injection framework to associate the options type (such as MyMiddlewareOptions) with a MyMiddlewareOptions object that has the actual options.

    クラスを更新し Startup ます。Update your Startup class:

    1. を使用している場合は appsettings.json 、コンストラクターの構成ビルダーに追加し Startup ます。If you're using appsettings.json , add it to the configuration builder in the Startup constructor:

      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. オプションサービスを構成します。Configure the options service:

      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. オプションをオプションクラスに関連付けます。Associate your options with your options class:

      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. ミドルウェアコンストラクターにオプションを挿入します。Inject the options into your middleware constructor. これは、コントローラーにオプションを挿入するのと似ています。This is similar to injecting options into a controller.

    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 依存関係の挿入を行います。The UseMiddleware extension method that adds your middleware to the IApplicationBuilder takes care of dependency injection.

    これはオブジェクトに限定されません IOptionsThis isn't limited to IOptions objects. ミドルウェアが必要とするその他のオブジェクトは、この方法で挿入できます。Any other object that your middleware requires can be injected this way.

直接挿入によるミドルウェアオプションの読み込みLoading middleware options through direct injection

オプションのパターンには、オプションの値とコンシューマーの間に疎結合が作成されるという利点があります。The options pattern has the advantage that it creates loose coupling between options values and their consumers. オプションクラスを実際のオプション値に関連付けた後、他のクラスは、依存関係挿入フレームワークを通じてオプションにアクセスできます。Once you've associated an options class with the actual options values, any other class can get access to the options through the dependency injection framework. オプションの値を渡す必要はありません。There's no need to pass around options values.

これは、異なるオプションを使用して同じミドルウェアを2回使用する場合には、このようになります。This breaks down though if you want to use the same middleware twice, with different options. たとえば、異なるブランチで使用される承認ミドルウェアは、さまざまなロールを許可します。For example an authorization middleware used in different branches allowing different roles. 2つの異なる options オブジェクトを1つの options クラスに関連付けることはできません。You can't associate two different options objects with the one options class.

ソリューションでは、オプションオブジェクトをクラスの実際のオプション値で取得 Startup し、ミドルウェアの各インスタンスに直接渡すことができます。The solution is to get the options objects with the actual options values in your Startup class and pass those directly to each instance of your middleware.

  1. 2番目のキーをに追加します。 appsettings.jsonAdd a second key to appsettings.json

    2番目のオプションセットをファイルに追加するには appsettings.json 、新しいキーを使用して一意に識別します。To add a second set of options to the appsettings.json file, use a new key to uniquely identify it:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. オプションの値を取得し、ミドルウェアに渡します。Retrieve options values and pass them to middleware. Use...(ミドルウェアをパイプラインに追加する) 拡張メソッドは、オプション値を渡す論理的な場所です。The Use... extension method (which adds your middleware to the pipeline) is a logical place to pass in the option values:

    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. ミドルウェアがオプションパラメーターを受け取ることができるようにします。Enable middleware to take an options parameter. Use...拡張メソッド (options パラメーターを受け取り、に渡す) のオーバーロードを指定し UseMiddleware ます。Provide an overload of the Use... extension method (that takes the options parameter and passes it to UseMiddleware). パラメーターを使用して UseMiddleware を呼び出すと、ミドルウェアオブジェクトをインスタンス化するときに、ミドルウェアコンストラクターにパラメーターが渡されます。When UseMiddleware is called with parameters, it passes the parameters to your middleware constructor when it instantiates the middleware object.

    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 してください。Note how this wraps the options object in an OptionsWrapper object. これは IOptions 、ミドルウェアコンストラクターによって想定されているとおりにを実装します。This implements IOptions, as expected by the middleware constructor.

新しい HttpContext への移行Migrating to the new HttpContext

Invoke に、ミドルウェア内のメソッドが型のパラメーターを受け取ることを確認しました HttpContextYou saw earlier that the Invoke method in your middleware takes a parameter of type HttpContext:

public async Task Invoke(HttpContext context)

HttpContext は ASP.NET Core で大幅に変更されました。HttpContext has significantly changed in ASP.NET Core. このセクションでは、 system.web の最もよく使用されるプロパティを新しいに変換する方法について説明します Microsoft.AspNetCore.Http.HttpContextThis section shows how to translate the most commonly used properties of System.Web.HttpContext to the new Microsoft.AspNetCore.Http.HttpContext.

Httpcontext.currentHttpContext

HttpContext は次のように変換さ れます。HttpContext.Items translates to:

IDictionary<object, object> items = httpContext.Items;

一意の要求 ID (対応する System.web)Unique request ID (no System.Web.HttpContext counterpart)

要求ごとに一意の id を提供します。Gives you a unique id for each request. ログに含めるのに非常に便利です。Very useful to include in your logs.

string requestId = httpContext.TraceIdentifier;

HttpContext. 要求HttpContext.Request

HttpMethod は次のように変換されます。HttpContext.Request.HttpMethod translates to:

string httpMethod = httpContext.Request.Method;

Httpcontext.current は次のように変換されます。HttpContext.Request.QueryString translates to:

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. urlhttpcontext.current url はに変換されます。HttpContext.Request.Url and HttpContext.Request.RawUrl translate to:

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

HttpContext. IsSecureConnection は次のように変換されます。HttpContext.Request.IsSecureConnection translates to:

var isSecureConnection = httpContext.Request.IsHttps;

HttpContext. UserHostAddress は次のように変換されます。HttpContext.Request.UserHostAddress translates to:

var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();

HttpContext. 要求。 Cookies は次のように変換します。HttpContext.Request.Cookies translates to:

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

Httpcontext.current は次のように変換されます。HttpContext.Request.RequestContext.RouteData translates to:

var routeValue = httpContext.GetRouteValue("key");

HttpContext。ヘッダー は次のように変換されます。HttpContext.Request.Headers translates to:

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

UserAgent は次のように変換されます。HttpContext.Request.UserAgent translates to:

string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();

Httpcontext.current は次のように変換されます。HttpContext.Request.UrlReferrer translates to:

string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();

HttpContext。 ContentType は次のように変換されます。HttpContext.Request.ContentType translates to:

// 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。フォーム は次のように変換されます。HttpContext.Request.Form translates to:

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

警告

コンテンツサブタイプが url エンコード または フォームデータ の場合にのみ、フォーム値を読み取ります。Read form values only if the content sub type is x-www-form-urlencoded or form-data.

InputStream は次のように変換されます。HttpContext.Request.InputStream translates to:

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

警告

このコードは、パイプラインの最後のハンドラー型ミドルウェアでのみ使用してください。Use this code only in a handler type middleware, at the end of a pipeline.

前に示したように、未加工の本文は、要求ごとに1回だけ読み取ることができます。You can read the raw body as shown above only once per request. 最初の読み取り後に、ミドルウェアが本文を読み取ろうとすると、空の本文が読み取られます。Middleware trying to read the body after the first read will read an empty body.

これは、前に示したように、フォームの読み取りには適用されません。これは、バッファーから実行されるためです。This doesn't apply to reading a form as shown earlier, because that's done from a buffer.

HttpContextHttpContext.Response

Httpcontext.current は、次のように変換されます。 statusdescription :HttpContext.Response.Status and HttpContext.Response.StatusDescription translate to:

// using Microsoft.AspNetCore.Http;
httpContext.Response.StatusCode = StatusCodes.Status200OK;

Httpcontext.current は、次のように 変換します。HttpContext.Response.ContentEncoding and HttpContext.Response.ContentType translate to:

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

また、その独自の ContentType にも、次のように変換されます。HttpContext.Response.ContentType on its own also translates to:

httpContext.Response.ContentType = "text/html";

出力 は次のように変換されます。HttpContext.Response.Output translates to:

string responseContent = GetResponseContent();
await httpContext.Response.WriteAsync(responseContent);

TransmitFileHttpContext.Response.TransmitFile

ファイルの提供については、「」で説明 ASP.NET Core での要求機能 しています。Serving up a file is discussed in ASP.NET Core での要求機能.

HttpContext. 応答ヘッダーHttpContext.Response.Headers

応答ヘッダーの送信は、応答本文に何かが書き込まれた後に設定した場合、送信されないという事実により複雑になります。Sending response headers is complicated by the fact that if you set them after anything has been written to the response body, they will not be sent.

解決策として、応答の書き込みが開始される前に、右に呼び出されるコールバックメソッドを設定します。The solution is to set a callback method that will be called right before writing to the response starts. これは、ミドルウェアのメソッドの開始時に実行することをお勧め Invoke します。This is best done at the start of the Invoke method in your middleware. 応答ヘッダーを設定するコールバックメソッドです。It's this callback method that sets your response headers.

次のコードは、というコールバックメソッドを設定し SetHeaders ます。The following code sets a callback method called SetHeaders:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

SetHeadersコールバックメソッドは次のようになります。The SetHeaders callback method would look like this:

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

CookieHttpContext。2$sHttpContext.Response.Cookies

Cookieは、 セット Cookie 応答ヘッダー内のブラウザーに移動します。Cookies travel to the browser in a Set-Cookie response header. そのため、を送信するには、 cookie 応答ヘッダーの送信に使用するのと同じコールバックが必要です。As a result, sending cookies requires the same callback as used for sending response headers:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetCookies, state: httpContext);
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

SetCookiesコールバックメソッドは次のようになります。The SetCookies callback method would look like the following:

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

その他のリソースAdditional resources