Asp.Net Core WebApi學習筆記(四)-- Middleware

來源:互聯網
上載者:User

標籤:

Asp.Net Core WebApi學習筆記(四)-- Middleware

本文記錄了Asp.Net管道模型和Asp.Net Core的Middleware模型的對比,並在上一篇的基礎上增加Middleware功能支援。

在示範Middleware功能之前,先要瞭解一下Asp.Net管道模型發生了什麼樣的變化。

第一部分:管道模型1. Asp.Net管道

在之前的Asp.Net裡,主要的管道模型流程如所示:

請求進入Asp.Net背景工作處理序後,由進程建立HttpWorkRequest對象,封裝此次請求有關的所有資訊,然後進入HttpRuntime類進行進一步處理。HttpRuntime通過請求資訊建立HttpContext內容物件,此對象將貫穿整個管道,直到響應結束。同時建立或從應用程式集區裡初始化一個HttpApplication對象,由此對象開始處理之前註冊的多個HttpModule。之後調用HandlerFactory建立Handler處理常式,最終處理此次請求內容,產生響應返回。

下面用一個簡單的Asp.Net程式來驗證這個流程。

使用VS2015建立一個空的Asp.Net項目,根據嚮導添加HttpModule.cs、HttpHandler.cs、Global.asax檔案

 HttpModule.cs HttpHandler.cs

配置Web.Config。以下是在IIS7環境下的配置內容。

 1 <?xml version="1.0" encoding="utf-8"?> 2 <!-- 3   有關如何配置 ASP.NET 應用程式的詳細資料,請訪問 4   http://go.microsoft.com/fwlink/?LinkId=169433 5   --> 6 <configuration> 7   <system.web> 8     <compilation debug="true" targetFramework="4.5"/> 9     <httpRuntime targetFramework="4.5"/>10   </system.web>11   <system.webServer>12     <validation validateIntegratedModeConfiguration="false"/>13     <handlers>14       <add name="handler" verb="GET" path="index.handler" type="WebApplicationTest.HttpHandler,WebApplicationTest"/>15     </handlers>16     <modules>17       <add name="module1" type="WebApplicationTest.HttpModule1,WebApplicationTest"/>18       <add name="module2" type="WebApplicationTest.HttpModule2,WebApplicationTest"/>19       <add name="module3" type="WebApplicationTest.HttpModule3,WebApplicationTest"/>20     </modules>21   </system.webServer>22 </configuration>

啟動調試,訪問地址 http://localhost:5383/index.handler ,可以看到頁面內容。

之前版本的Asp.Net MVC正是通過 UrlRoutingModule.cs 類和 MvcHandler.cs 類進行擴充從而實現了MVC架構。

2、Asp.Net Core管道

而在Asp.Net Core裡面,管道模型流程發生了很大的變化:

IHttpModule和IHttpHandler不複存在,取而代之的是一個個中介軟體(Middleware)。

Server將接收到的請求直接向後傳遞,依次經過每一個中介軟體進行處理,然後由最後一個中介軟體處理並產生響應內容後回傳,再反向依次經過每個中介軟體,直到由Server發送出去。

中介軟體就像一層一層的“濾網”,過濾所有的請求和相應。這一設計非常適用於“要求-回應”這樣的情境——訊息從管道頭流入最後反向流出。

接下來將示範在Asp.Net Core裡如何?中介軟體功能。

 

第二部分、Middleware

其實,在這個系列的第一篇裡面,已經展示了管道的一個簡單用法。這裡再詳細講解一下如何?自訂管道。

Middleware支援Run、Use和Map三種方法進行註冊,下面將展示每一種方法的使用方式。

一、Run方法

所有需要實現的自訂管道都要在 Startup.cs 的 Configure 方法裡添加註冊。

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.Run(async context =>12             {13                 await context.Response.WriteAsync("Hello World!");14             });15 16             // 添加MVC中介軟體17             //app.UseMvc();18         }

啟動調試,訪問地址 http://localhost:5000/ ,頁面顯示Hello World!字樣。

再次添加一個Run方法

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.Run(async context =>12             {13                 await context.Response.WriteAsync("Hello World!");14             });15 16             app.Run(async context =>17             {18                 await context.Response.WriteAsync("Hello World too!");19             });20 21             // 添加MVC中介軟體22             //app.UseMvc();23         }

啟動調試,再次訪問發現頁面上只有Hello World!字樣。

原因是:Run的這種用法表示註冊的此中介軟體為管道內的最後一個中介軟體,由它處理完請求後直接返回。

二、Use方法 
 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.Use(async (context, next) =>12             {13                 await context.Response.WriteAsync("Hello World!");14             });15 16             // 添加MVC中介軟體17             //app.UseMvc();18         }

啟動調試,訪問頁面同樣顯示Hello World!字樣。我們發現使用Use方法替代Run方法,一樣可以實現同樣的功能。

再次添加一個Use方法,將原來的Use方法內容稍作調整,嘗試實現頁面顯示兩個Hello World!字樣。

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.Use(async (context, next) =>12             {13                 await context.Response.WriteAsync("Hello World!");14                 await next();15             });16 17             app.Use(async (context, next) =>18             {19                 await context.Response.WriteAsync("Hello World too!");20             });21 22             // 添加MVC中介軟體23             //app.UseMvc();24         }

啟動調試,訪問頁面

將兩個Use方法換個順序,稍微調整一下內容,再次啟動調試,訪問頁面,發現字樣輸出順序也發生了變化。

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); HelloworldMiddleware.cs  9 10             // 添加自訂中介軟體11             app.Use(async (context, next) =>12             {13                 await context.Response.WriteAsync("Hello World too!");14                 await next();15             });16 17             app.Use(async (context, next) =>18             {19                 await context.Response.WriteAsync("Hello World!");20             });21 22             // 添加MVC中介軟體23             //app.UseMvc();24         }

從上面的例子可以發現,通過Use方法註冊的中介軟體,如果不調用next方法,效果等同於Run方法。當調用next方法後,此中介軟體處理完後將請求傳遞下去,由後續的中介軟體繼續處理。

當註冊中介軟體順序不一樣時,處理的順序也不一樣,這一點很重要,當註冊的自訂中介軟體數量較多時,需要考慮哪些中介軟體先處理請求,哪些中介軟體後處理請求。

另外,我們可以將中介軟體單獨寫成獨立的類,通過UseMiddleware方法同樣可以完成註冊。下面將通過獨立的中介軟體類重寫上面的示範功能。

建立兩個中介軟體類: HelloworldMiddleware.cs 、 HelloworldTooMiddleware.cs  

 HelloworldMiddleware.cs HelloworldTooMiddleware.cs

修改 Startup.cs 的Configure方法內容

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.UseMiddleware<HelloworldMiddleware>();12             app.UseMiddleware<HelloworldTooMiddleware>();13 14             // 添加MVC中介軟體15             //app.UseMvc();16         }

啟動調試,訪問頁面,可以看到同樣的效果。

三、Map方法

Map方法主要通過請求路徑和其他自訂條件過濾來指定註冊的中介軟體,看起來更像一個路由。

修改 Startup.cs 的Configure方法內容,增加靜態方法MapTest

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.Map("/test", MapTest);12 13             // 添加MVC中介軟體14             //app.UseMvc();15         }16 17         private static void MapTest(IApplicationBuilder app){18             app.Run(async context => {19                 await context.Response.WriteAsync("Url is " + context.Request.PathBase.ToString());20             });21         }

啟動調試,訪問路徑 http://localhost:5000/test ,頁面顯示如下內容

但是訪問其他路徑時,頁面沒有內容顯示。從這個可以看到,Map方法通過類似路由的機制,將特定的Url地址請求引導到固定的方法裡,由特定的中介軟體處理。

另外,Map方法還可以實現多級Url“路由”,其實就是Map方法的嵌套使用

 1             // 添加自訂中介軟體 2             app.Map("/level1", lv1App => { 3                 app.Map("/level1.1", lv11App => { 4                     // /level1/level1.1 5  6                 }); 7                  8                 app.Map("/level1.2", lv12App => { 9                     // /level1/level1.210 11                 });12             });

也可以通過MapWhen方法使用自訂條件進行“路由”

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.MapWhen(context =>12             {13                 return context.Request.Query.ContainsKey("a");14             }, MapTest);15 16             // 添加MVC中介軟體17             //app.UseMvc();18         }19 20         private static void MapTest(IApplicationBuilder app)21         {22             app.Run(async context =>23             {24                 await context.Response.WriteAsync($"Url is {context.Request.Path.ToString()}{context.Request.QueryString.Value}");25             });26 27         }

啟動調試,訪問路徑 http://localhost:5000/path?a=1&b=2 ,頁面顯示如下內容

只有當請求參數中含有a時,頁面才正常顯示內容。

四、其他內建的中介軟體

Asp.Net Core架構內建了幾個中介軟體

 

最後,用自訂中介軟體實現一個簡單的訪問日誌記錄功能,記錄每一次請求的內容和回應時間。

1. 添加日誌模型 VisitLog.cs 

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4  5 namespace WebApiFrame.Models 6 { 7     public class VisitLog 8     { 9         public string Url { get; set; }10 11         public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();12 13         public string Method { get; set; }14 15         public string RequestBody { get; set; }16 17         public DateTime ExcuteStartTime { get; set; }18 19         public DateTime ExcuteEndTime { get; set; }20 21         public override string ToString()22         {23             string headers = "[" + string.Join(",", this.Headers.Select(i => "{" + $"\"{i.Key}\":\"{i.Value}\"" + "}")) + "]";24             return $"Url: {this.Url},\r\nHeaders: {headers},\r\nMethod: {this.Method},\r\nRequestBody: {this.RequestBody},\r\nExcuteStartTime: {this.ExcuteStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")},\r\nExcuteStartTime: {this.ExcuteEndTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}";25         }26     }27 }

2. 添加訪問日誌記錄中介軟體 VisitLogMiddleware.cs ,同時添加UseVisitLogger擴充方法。

 1 using Microsoft.AspNetCore.Builder; 2 using Microsoft.AspNetCore.Http; 3 using Microsoft.Extensions.Logging; 4 using System; 5 using System.IO; 6 using System.Linq; 7 using System.Threading.Tasks; 8 using WebApiFrame.Models; 9 10 namespace WebApiFrame.Core.Middlewares11 {12     public class VisitLogMiddleware13     {14         private readonly RequestDelegate _next;15 16         private readonly ILogger logger;17 18         private VisitLog visitLog;19 20         public VisitLogMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)21         {22             _next = next;23             logger = loggerFactory.CreateLogger<VisitLogMiddleware>();24         }25 26         public async Task Invoke(HttpContext context)27         {28             visitLog = new VisitLog();29             HttpRequest request = context.Request;30             visitLog.Url = request.Path.ToString();31             visitLog.Headers = request.Headers.ToDictionary(k => k.Key, v => string.Join(";", v.Value.ToList()));32             visitLog.Method = request.Method;33             visitLog.ExcuteStartTime = DateTime.Now;34 35             using (StreamReader reader = new StreamReader(request.Body))36             {37                 visitLog.RequestBody = reader.ReadToEnd();38             }39 40             context.Response.OnCompleted(ResponseCompletedCallback, context);41             await _next(context);42         }43 44         private Task ResponseCompletedCallback(object obj)45         {46             visitLog.ExcuteEndTime = DateTime.Now;47             logger.LogInformation($"VisitLog: {visitLog.ToString()}");48             return Task.FromResult(0);49         }50     }51 52     public static class VisitLogMiddlewareExtensions53     {54         public static IApplicationBuilder UseVisitLogger(this IApplicationBuilder builder)55         {56             return builder.UseMiddleware<VisitLogMiddleware>();57         }58     }59 }

3. 在 Startup.cs 添加中介軟體支援

 1         public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 2         { 3             // 添加日誌支援 4             loggerFactory.AddConsole(); 5             loggerFactory.AddDebug(); 6              7             // 添加NLog日誌支援 8             loggerFactory.AddNLog(); 9 10             // 添加自訂中介軟體11             app.UseVisitLogger();12 13             app.Run(async context =>14             {15                 await context.Response.WriteAsync("Hello World!");16             });17 18 19             // 添加MVC中介軟體20             //app.UseMvc();21         }

4. 啟動調試,訪問地址 http://localhost:5000/ ,查看偵錯主控台日誌列印資訊。

另外,如果你比較細心會發現,在Configure方法裡有這樣一句代碼: app.UseMvc(); ,Asp.Net Core Mvc正是通過這個方法借用中介軟體來擴充實現了MVC架構。 

 分類: Asp.Net Core

Asp.Net Core WebApi學習筆記(四)-- Middleware

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.