ASP.NET 5ミドルウェア、またはHTTPモジュールはどこに行きましたか?

ASP.NET 5の新しいバージョンはほぼゼロから書き直されており、以前のバージョンと比較して大幅な変更が加えられています。 最大の変更点の1つは、処理パイプライン(HTTPパイプライン)です。 この記事では、これらの変更が以前にHttpモジュールとして提示されたコンポーネントの設計と実装にどのように影響するかについて説明します。



HTTPモジュールの動作は、以前はASP.NET 5までのクエリフィルターの動作に似ていました。これは、クエリパイプラインに埋め込むことができ、たとえばアプリケーションのイベントに応答するために実行するタスクを記述する機能です。 モジュールは、認証、グローバルエラー処理、およびログ記録に使用されます。 また、スペースや圧縮を削除するなど、サーバーの応答をインターセプトおよび変更するためにもよく使用されます。 System.Webアセンブリで定義されているIHttpModuleインターフェイスを実装します。これは、新しいASP.NETの一部ではありません。



基本的に、コード内のHttpModuleの登録は、Global.asaxのイベントハンドラーとして追加されるか、 web.configの登録用にアセンブリが作成されます。



ミドルウェアとは何ですか?



「ミドルウェア」の定義は大きく異なりますが、ASP.NET 5のコンテキストでは、おそらく最も正確な定義がOwin仕様で与えられています。



サーバーと特定の目的のために要求と応答を検査、ルーティング、または変更するアプリケーションとの間のパイプラインを形成するエンドツーエンドのコンポーネント。



オリジナル:

サーバーとアプリケーションの間のパイプラインを形成するコンポーネントを通過して、特定の目的のために要求および応答メッセージを検査、ルーティング、または変更します



従来のHTTPモジュールまたはハンドラー(handler-a)の説明に非常に似ています。



ASP.NET 5アプリケーションのクエリパイプラインへのアクセスは、アプリケーション自体へのエントリポイントでもあるStartupクラス(Startup.csファイル)で提供されます 。 Startupクラスには、 IApplicationBuilderをパラメーターとして受け取る Configureメソッドが含まれています。 IApplicationBuilderインターフェイス( appパラメーター)は、さまざまなコンポーネントをリクエストパイプラインに接続するための多くの拡張メソッドを提供します。 これとHTTPパイプラインの以前の実装との主な違いは、以前のアプローチがイベントに基づいていたということであり、現在では複合モデルに置き換えられています(コンポーネントは次々に追加されます)。 また、新しいASP.NETでは、これらのコンポーネントが追加される順序が重要です。 基本的なMVCアプリケーションテンプレートには既にいくつかのコンポーネントがあり、コメントからその目的を理解できます。

// Add static files to the request pipeline. app.UseStaticFiles(); // Add cookie-based authentication to the request pipeline. app.UseIdentity(); // Add MVC to the request pipeline. app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); // Uncomment the following line to add a route for porting Web API 2 controllers. // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}"); });
      
      







ご覧のとおり、Identity、MVC、および静的ファイルを追加するメソッドは、IApplicationBuilderの拡張メソッドです。 この記事で説明する新しい中間ハンドラーを追加するときは、パイプラインに追加する拡張メソッドも作成します。



ミドルウェアの例



中間ハンドラーの例では、2つのことを行います。要求の処理時間を測定し、この値を発信HTMLおよび応答ヘッダーに追加します。 これは3つの全体です。 いずれにせよ、この例では、「HTTPモジュールの書き方」などの多くの記事で説明されている2つの最も一般的なシナリオ-サーバー応答の変更とヘッダーの追加について説明します。 ハンドラーコード:

 using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using System.Diagnostics; using System.IO; using System.Threading.Tasks; namespace WebApplication1.MiddleWare { public class MyMiddleware { RequestDelegate _next; public MyMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { var sw = new Stopwatch(); sw.Start(); using (var memoryStream = new MemoryStream()) { var bodyStream = context.Response.Body; context.Response.Body = memoryStream; await _next(context); var isHtml = context.Response.ContentType?.ToLower().Contains("text/html"); if (context.Response.StatusCode == 200 && isHtml.GetValueOrDefault()) { { memoryStream.Seek(0, SeekOrigin.Begin); using (var streamReader = new StreamReader(memoryStream)) { var responseBody = await streamReader.ReadToEndAsync(); var newFooter = @"<footer><div id=""process"">Page processed in {0} milliseconds.</div>"; responseBody = responseBody.Replace("<footer>", string.Format(newFooter, sw.ElapsedMilliseconds)); context.Response.Headers.Add("X-ElapsedTime", new[] { sw.ElapsedMilliseconds.ToString() }); using (var amendedBody = new MemoryStream()) using (var streamWriter = new StreamWriter(amendedBody)) { streamWriter.Write(responseBody); amendedBody.Seek(0, SeekOrigin.Begin); await amendedBody.CopyToAsync(bodyStream); } } } } } } } }
      
      







RequestDelegate型のプライベートフィールドがあり、デリゲートが格納され、コンストラクタに渡されます。 RequestDelegate自体は、 HttpContext取得しTaskを返すメソッドをカプセル化します。

 public delegate Task RequestDelegate(HttpContext context);
      
      







HttpContextオブジェクト 、要求、応答などへのアクセスを提供するという点で以前のバージョンのASP.NETと同じですが、これは完全に異なる獣であり、はるかに簡単です。



ASP.NET 5では、ミドルウェアはFunc <RequestDelegate、RequestDelegate>のインスタンスです。パラメーターとしてRequestDelegateを受け入れ、 RequestDelegateを返すデリゲートです。 そして、前述のように、 RequestDelegateは関数を返す関数であり、これが処理パイプラインの構築原理です-中間層の各部分は、他の部分にリベットされ、チェーン内の次の部分に処理を転送する責任があります(必要な場合)。



Invokeメソッドは、規則に基づいて実行時に呼び出されます。 これは、ミドルウェア部分で処理が行われる場所であり、必要に応じてパイプライン内の次のコンポーネントに制御を与える場所です。 さらに制御を移す必要はないが、代わりに実行を停止する必要がある場合があります。たとえば、現在のユーザーに適切な権限がないと判断するのが認証コンポーネントの場合です。 await _next(context)を呼び出すことにより、制御が次のコンポーネントに渡されます 。 その前に配置したコードが実行されます。たとえば、この場合、ストップウォッチを作成して実行します。 さらに、データストリーム(ストリーム)として実装される応答にアクセスできます。 次に、次のコンポーネントが呼び出され、次に、次のコンポーネントが呼び出されます。 次に、制御はコンポーネントチェーンを介して戻され、 await _next(コンテキスト)が呼び出された後に追加されたコードが実行されます。 この例のコードブロックでは、応答の本文が変更され、ページのフッターに期限切れのHTMLが含まれるようになります。 そして、同じ値を持つヘッダーが追加されます。



コンベアへのくさび



次のステップは、コンポーネントを処理パイプラインに含めることです。 上記のように、これはextesionメソッドを使用して実行できます。たとえば、次のとおりです。

 namespace ASPNET5Test.MiddleWare { public static class BuilderExtensions { public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder app) { return app.UseMiddleware<MyMiddleware>(); } } }
      
      







これは、 Microsoft.AspNet.Builderにある標準のUseMiddlewareメソッドのラッパーの簡単な例です。

さて、最後のステップはStartupクラスでメソッドを呼び出すことです。

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { app.UseMyMiddleware(); // ...
      
      







最初にサイトを開始するとき、それは多くの時間を要します-ほぼ2秒、これはページの下部に見ることができます:





たとえば、Chromeの開発者ツールなどを使用すると、ヘッダーを確認できます。





おわりに



この記事では、従来のHTTPモジュールに代わるものを紹介し、そのようなハンドラーのインスタンスをアプリケーションで作成および実装する方法を示しました。 この記事はASP.NET 5 Beta 3に基づいており、ここに示されている概念は変更される可能性があります。



All Articles