最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面

來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:34:42
文檔

ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面

ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面:前言 StatusCodePagesMiddleware中間件與ExceptionHandlerMiddleware中間件比較類似,它們都是在后續(xù)請(qǐng)求處理過(guò)程中出錯(cuò)的情況下利用一個(gè)錯(cuò)誤處理器來(lái)完成最終的請(qǐng)求處理與響應(yīng)的任務(wù)。它們之間的差異在于對(duì)錯(cuò)誤的界定上,對(duì)于ExceptionHa
推薦度:
導(dǎo)讀ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面:前言 StatusCodePagesMiddleware中間件與ExceptionHandlerMiddleware中間件比較類似,它們都是在后續(xù)請(qǐng)求處理過(guò)程中出錯(cuò)的情況下利用一個(gè)錯(cuò)誤處理器來(lái)完成最終的請(qǐng)求處理與響應(yīng)的任務(wù)。它們之間的差異在于對(duì)錯(cuò)誤的界定上,對(duì)于ExceptionHa

前言

StatusCodePagesMiddleware中間件與ExceptionHandlerMiddleware中間件比較類似,它們都是在后續(xù)請(qǐng)求處理過(guò)程中“出錯(cuò)”的情況下利用一個(gè)錯(cuò)誤處理器來(lái)完成最終的請(qǐng)求處理與響應(yīng)的任務(wù)。它們之間的差異在于對(duì)“錯(cuò)誤”的界定上,對(duì)于ExceptionHandlerMiddleware中間件來(lái)說(shuō),它所謂的錯(cuò)誤就是拋出異常,但是對(duì)于StatusCodePagesMiddleware中間件來(lái)說(shuō),則將介于400~599之間的響應(yīng)狀態(tài)碼視為錯(cuò)誤。如下面的代碼片段所示,StatusCodePagesMiddleware中間件也采用“標(biāo)準(zhǔn)”的定義方式,針對(duì)它的配置選項(xiàng)通過(guò)一個(gè)對(duì)應(yīng)的對(duì)象以O(shè)ptions模式的形式提供給它。

 public class StatusCodePagesMiddleware
 {
 public StatusCodePagesMiddleware(RequestDelegate next, IOptions<StatusCodePagesOptions> options);
 public Task Invoke(HttpContext context);
 }

除了針對(duì)錯(cuò)誤的界定,StatusCodePagesMiddleware和ExceptionHandlerMiddleware這兩個(gè)中間件對(duì)于錯(cuò)誤處理器的表達(dá)也不相同。我們知道ExceptionHandlerMiddleware中間件使用的錯(cuò)誤處理器實(shí)際上就是一個(gè)類型為RequestDelegate的委托對(duì)象,但是錯(cuò)誤處理器之于StatusCodePagesMiddleware中間件來(lái)說(shuō)則是一個(gè)類型為Func<StatusCodeContext, Task>的委托對(duì)象。如下面的代碼片段所示,為StatusCodePagesMiddleware中間件提供配置選項(xiàng)的StatusCodePagesOptions對(duì)象的唯一目的就是提供這個(gè)作為錯(cuò)誤處理器的委托對(duì)象。

 public class StatusCodePagesOptions
 {
 public Func<StatusCodeContext, Task> HandleAsync { get; set; }
 }

我們知道一個(gè)RequestDelegate對(duì)象相當(dāng)于一個(gè)類型為Func<HttpContext, Task>類型的委托對(duì)象,而一個(gè)StatusCodeContext對(duì)象實(shí)際上也是對(duì)一個(gè)HttpContext對(duì)象的封裝,所以StatusCodePagesMiddleware中間件和ExceptionHandlerMiddleware中間件所使采用的錯(cuò)誤處理器并沒(méi)有本質(zhì)上的不同。如下面的代碼片段所示,除了從StatusCodeContext對(duì)象中獲取代表當(dāng)前請(qǐng)求上下文的HttpContext對(duì)象之外,我們還可以通過(guò)其Next屬性得到一個(gè)RequestDelegate對(duì)象,它代表由后續(xù)中間件組成的請(qǐng)求處理管道。至于另一個(gè)屬性O(shè)ptions,很明顯它返回我們?cè)趧?chuàng)建StatusCodePagesMiddleware中間件所指定的StatusCodePagesOptions對(duì)象。

 public class StatusCodeContext
 { 
 public HttpContext HttpContext { get; }
 public RequestDelegate Next { get; }
 public StatusCodePagesOptions Options { get; }
 
 public StatusCodeContext(HttpContext context, StatusCodePagesOptions options, RequestDelegate next);
 }

一、針對(duì)響應(yīng)狀態(tài)碼的錯(cuò)誤處理

由于采用了針對(duì)響應(yīng)狀態(tài)碼的錯(cuò)誤處理策略,所以實(shí)現(xiàn)在StatusCodePagesMiddleware中間件中的所有錯(cuò)誤處理操作只會(huì)發(fā)生在當(dāng)前響應(yīng)狀態(tài)碼在400~599之間的情況,如下所示的代碼片段體現(xiàn)了這一點(diǎn)。從下面給出的代碼片段可以看出,StatusCodePagesMiddleware中間件在決定是否執(zhí)行錯(cuò)誤處理操作時(shí)除了會(huì)查看當(dāng)前響應(yīng)狀態(tài)碼之外,還會(huì)查看響應(yīng)內(nèi)容以及媒體類型,如果已經(jīng)包含了響應(yīng)內(nèi)容或者設(shè)置了媒體類型,該中間件將不會(huì)執(zhí)行任何操作。

 public class StatusCodePagesMiddleware
 {
 private RequestDelegate _next;
 private StatusCodePagesOptions _options;
 
 public StatusCodePagesMiddleware(RequestDelegate next, 
 IOptions<StatusCodePagesOptions> options)
 {
 _next = next;
 _options = options.Value;
 }
 public async Task Invoke(HttpContext context)
 { 
 await _next(context);
 var response = context.Response;
 if ((response.StatusCode >= 400 && response.StatusCode <= 599) &&!response.ContentLength.HasValue && string.IsNullOrEmpty(response.ContentType))
 {
 await _options.HandleAsync(new StatusCodeContext(context, _options, _next));
 }
 }
 }

StatusCodePagesMiddleware中間件針對(duì)錯(cuò)誤的處理非常簡(jiǎn)單,它只需要從StatusCodePagesOptions對(duì)象中提取出作為錯(cuò)誤處理器的這個(gè)Func<StatusCodeContext, Task>對(duì)象,然后創(chuàng)建一個(gè)StatusCodeContext對(duì)象作為輸入?yún)?shù)調(diào)用這個(gè)委托對(duì)象即可。

二、阻止異常處理

如果當(dāng)前響應(yīng)已經(jīng)被寫入了內(nèi)容,或者響應(yīng)的媒體類型已經(jīng)被預(yù)先設(shè)置,那么StatusCodePagesMiddleware中間件將不會(huì)再執(zhí)行任何的錯(cuò)誤處理操作。這種情況實(shí)際上代表由后續(xù)中間件構(gòu)成的管道可能需要自行控制當(dāng)前的響應(yīng),所以StatusCodePagesMiddleware中間件不應(yīng)該再做任何的干預(yù)。從這個(gè)意義上來(lái)講,StatusCodePagesMiddleware中間件僅僅是作為一種后備的錯(cuò)誤處理機(jī)制而已。

更進(jìn)一步來(lái)將,如果后續(xù)的某個(gè)中間件返回了一個(gè)狀態(tài)碼在400~599之間的響應(yīng),并且這個(gè)響應(yīng)只有報(bào)頭集合沒(méi)有主體(媒體類型自然也不會(huì)設(shè)置),那么按照我們?cè)谏厦娼o出的錯(cuò)誤處理邏輯,StatusCodePagesMiddleware中間件還是會(huì)按照自己的策略來(lái)處理并響應(yīng)請(qǐng)求。為了解決這種情況下,我們必須賦予后續(xù)中間件一個(gè)能夠阻止StatusCodePagesMiddleware中間件進(jìn)行錯(cuò)誤處理的能力。

阻止StatusCodePagesMiddleware中間件進(jìn)行錯(cuò)誤處理的機(jī)制是借助于一個(gè)名為StatusCodePagesFeature的特性來(lái)實(shí)現(xiàn)的。StatusCodePagesFeature對(duì)應(yīng)如下這個(gè)IStatusCodePagesFeature接口,它具有唯一的布爾類型的屬性成員Enabled。默認(rèn)使用的StatusCodePagesFeature類型實(shí)現(xiàn)了這個(gè)接口,默認(rèn)情況下這個(gè)開(kāi)關(guān)是開(kāi)啟的。

 public interface IStatusCodePagesFeature
 {
 bool Enabled { get; set; }
 }
 
 public class StatusCodePagesFeature : IStatusCodePagesFeature
 {
 public bool Enabled { get; set; } = true ;
 }

StatusCodePagesMiddleware中間件在將請(qǐng)求交付給后續(xù)管道之前,它會(huì)創(chuàng)建一個(gè)StatusCodePagesFeature特性對(duì)象并將其添加到當(dāng)前HttpContext之中。當(dāng)最終決定是否執(zhí)行錯(cuò)誤處理操作的時(shí)候,它還會(huì)通過(guò)這個(gè)特性檢驗(yàn)是否某個(gè)后續(xù)的中間件不希望自己“畫蛇添足”地進(jìn)行不必要的錯(cuò)誤處理,如下的代碼片段很好的體現(xiàn)了這一點(diǎn)。

 public class StatusCodePagesMiddleware
 {
 …
 public async Task Invoke(HttpContext context)
 {
 StatusCodePagesFeature feature = new StatusCodePagesFeature();
 context.Features.Set<IStatusCodePagesFeature>(feature);
 
 await _next(context);
 var response = context.Response;
 if ((response.StatusCode >= 400 && response.StatusCode <= 599) && !response.ContentLength.HasValue &&string.IsNullOrEmpty(response.ContentType) &&
 feature.Enabled)
 {
 await _options.HandleAsync(new StatusCodeContext(context, _options, _next));
 }
 }
 }

我們通過(guò)一個(gè)簡(jiǎn)單的實(shí)例來(lái)演示如果利用這個(gè)StatusCodePagesFeature特性來(lái)屏蔽StatusCodePagesMiddleware中間件。在下面這個(gè)應(yīng)用中,我們將針對(duì)請(qǐng)求的處理定義在Invoke方法中,該方法會(huì)返回一個(gè)狀態(tài)碼為“401 Unauthorized”的響應(yīng)。我們通過(guò)隨機(jī)數(shù)讓這個(gè)方法會(huì)在50%的情況下利用StatusCodePagesFeature特性來(lái)阻止StatusCodePagesMiddleware中間件自身對(duì)錯(cuò)誤的處理。我們通過(guò)調(diào)用擴(kuò)展方法UseStatusCodePages注冊(cè)的StatusCodePagesMiddleware中間件會(huì)直接響應(yīng)一個(gè)內(nèi)容為“Error occurred!”的字符串。

 public class Program
 {
 public static void Main()
 {
 new WebHostBuilder()
 .UseKestrel()
 .Configure(app => app
 .UseStatusCodePages(async context => await context.HttpContext.Response.WriteAsync("Error occurred!"))
 .Run(Invoke))
 .Build()
 .Run();
 }
 
 private static Random _random = new Random();
 private static Task Invoke(HttpContext context)
 {
 context.Response.StatusCode = 401;
 
 if (_random.Next() % 2 == 0)
 {
 context.Features.Get<IStatusCodePagesFeature>().Enabled = false;
 }
 return Task.CompletedTask;
 }
 }

對(duì)于針對(duì)該應(yīng)用的請(qǐng)求來(lái)說(shuō),我們會(huì)得到如下兩種不同的響應(yīng)。沒(méi)有主體內(nèi)容響應(yīng)是通過(guò)Invoke方法產(chǎn)生的,這種情況下發(fā)生在StatusCodePagesMiddleware中間件通過(guò)StatusCodePagesFeature特性被屏蔽的時(shí)候。具有主體內(nèi)容的響應(yīng)則來(lái)源于StatusCodePagesMiddleware中間件。

 HTTP/1.1 401 Unauthorized
 Date: Sun, 18 Dec 2016 01:59:37 GMT
 Server: Kestrel
 Content-Length: 15
 
 Error occurred!
 
 
 HTTP/1.1 401 Unauthorized
 Date: Sun, 18 Dec 2016 01:59:38 GMT
 Content-Length: 0
 Server: Kestrel

三、注冊(cè)StatusCodePagesMiddleware中間件

我們?cè)诖蟛糠智闆r下都會(huì)調(diào)用ApplicationBuilder相應(yīng)的擴(kuò)展方法來(lái)注冊(cè)StatusCodePagesMiddleware中間件。對(duì)于StatusCodePagesMiddleware中間件的注冊(cè)來(lái)說(shuō),除了我們已經(jīng)很熟悉的UseStatusCodePages方之外,還具有額外一些擴(kuò)展方法供我們選擇。

UseStatusCodePages

我們可以調(diào)用如下三個(gè)UseStatusCodePages方法重載來(lái)注冊(cè)StatusCodePagesMiddleware中間件。不論我們調(diào)用那個(gè)重載,系統(tǒng)最終都會(huì)根據(jù)提供的StatusCodePagesOptions對(duì)象調(diào)用構(gòu)造函數(shù)來(lái)創(chuàng)建這個(gè)中間件對(duì)象,而且這個(gè)StatusCodePagesOptions必須具有一個(gè)作為錯(cuò)誤處理器的Func<StatusCodeContext, Task>對(duì)象。如果沒(méi)有指定任何參數(shù),StatusCodePagesOptions對(duì)象需要以O(shè)ptions模式的形式注冊(cè)為服務(wù)。

 public static class StatusCodePagesExtensions
 { 
 public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app)
 { 
 return app.UseMiddleware<StatusCodePagesMiddleware>();
 }
 
 public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, StatusCodePagesOptions options)
 {
 return app.UseMiddleware<StatusCodePagesMiddleware>(Options.Create(options));
 } 
 
 public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, Func<StatusCodeContext, Task> handler)
 { 
 return app.UseStatusCodePages(new StatusCodePagesOptions
 {
 HandleAsync = handler
 });
 }
 }

由于StatusCodePagesMiddleware中間件最終的目的還是將定制的錯(cuò)誤信息響應(yīng)給客戶端,所以我們可以在注冊(cè)該中間件的時(shí)候直接指定響應(yīng)的內(nèi)容和媒體類型,這樣的注冊(cè)方式可以通過(guò)調(diào)用如下這個(gè)UseStatusCodePages方法來(lái)完成。從如下所示的代碼片段我們不難看出,我們通過(guò)bodyFormat方法指定的實(shí)際上是一個(gè)模板,它可以包含一個(gè)表示響應(yīng)狀態(tài)的占位符(“{0}”)。

 public static class StatusCodePagesExtensions
 { 
 public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, string contentType, string bodyFormat)
 {
 return app.UseStatusCodePages(context =>
 {
 var body = string.Format(CultureInfo.InvariantCulture, bodyFormat, context.HttpContext.Response.StatusCode);
 context.HttpContext.Response.ContentType = contentType;
 return context.HttpContext.Response.WriteAsync(body);
 });
 }
 }

UseStatusCodePagesWithRedirects

如果我們調(diào)用UseStatusCodePagesWithRedirects方法,可以讓注冊(cè)的StatusCodePagesMiddleware中間件向指定的路徑發(fā)送一個(gè)客戶端重定向。從如下所示的實(shí)現(xiàn)代碼可以看出,這個(gè)作為參數(shù)locationFormat的重定向地址也是一個(gè)模板,它可以包含一個(gè)表示響應(yīng)狀態(tài)的占位符(“{0}”)。我們可以指定一個(gè)完整的地址,也可以指定一個(gè)相對(duì)于PathBase的相對(duì)路徑,后者需要包含表示基地址的“~/”前綴。

 public static class StatusCodePagesExtensions
 { 
 public static IApplicationBuilder UseStatusCodePagesWithRedirects(this IApplicationBuilder app, string locationFormat)
 {
 if (locationFormat.StartsWith("~"))
 {
 locationFormat = locationFormat.Substring(1);
 return app.UseStatusCodePages(context =>
 {
 var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode);
 context.HttpContext.Response.Redirect(context.HttpContext.Request.PathBase + location);
 return Task.CompletedTask;
 });
 }
 else
 {
 return app.UseStatusCodePages(context =>
 {
 var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode);
 context.HttpContext.Response.Redirect(location);
 return Task.CompletedTask;
 });
 }
 }
 }

我們通過(guò)一個(gè)簡(jiǎn)單的應(yīng)用來(lái)演示針對(duì)客戶端重定向的錯(cuò)誤頁(yè)面呈現(xiàn)方式。我們?cè)谌缦逻@個(gè)應(yīng)用中注冊(cè)了一個(gè)路由模板為“error/{statuscode}”的路由,路由參數(shù)“statuscode”自然代表響應(yīng)的狀態(tài)碼。在作為路由處理器的HandleError方法中,我們會(huì)直接響應(yīng)一個(gè)包含響應(yīng)狀態(tài)碼的字符串。我們調(diào)用UseStatusCodePagesWithRedirects方法注冊(cè)StatusCodePagesMiddleware中間件的時(shí)候?qū)⒅囟x路徑設(shè)置為“error/{0}”。

 public class Program
 {
 private static Random _random = new Random();
 public static void Main()
 {
 new WebHostBuilder()
 .UseKestrel()
 .ConfigureServices(svcs => svcs.AddRouting())
 .Configure(app => app
 .UseStatusCodePagesWithRedirects("~/error/{0}")
 .UseRouter(builder=>builder.MapRoute("error/{statuscode}", HandleError))
 .Run(context=>Task.Run(()=>context.Response.StatusCode = _random.Next(400,599))))
 .Build()
 .Run();
 }
 
 private async static Task HandleError(HttpContext context)
 {
 var statusCode = context.GetRouteData().Values["statuscode"];
 await context.Response.WriteAsync($"Error occurred ({statusCode})");
 }
 }

針對(duì)該應(yīng)用的請(qǐng)求總是會(huì)得到一個(gè)狀態(tài)碼在400~599之間的響應(yīng), StatusCodePagesMiddleware在此情況下會(huì)向我們指定的路徑(“~/error/{statuscode}”)發(fā)送一個(gè)客戶端重定向。由于重定向請(qǐng)求的路徑與注冊(cè)的路由相匹配,所以作為路由處理器的HandleError方法會(huì)響應(yīng)如圖11所示的這個(gè)錯(cuò)誤頁(yè)面。

UseStatusCodePagesWithReExecute

除了采用客戶端重定向的方式來(lái)呈現(xiàn)錯(cuò)誤頁(yè)面之外,我們還可以調(diào)用UseStatusCodePagesWithReExecute方法注冊(cè)StatusCodePagesMiddleware中間件并讓它采用服務(wù)端重定向的方式來(lái)處理錯(cuò)誤請(qǐng)求。如下面的代碼片段所示,當(dāng)我們調(diào)用這個(gè)方法的時(shí)候不僅可以指定重定向的路徑,還可以指定指定查詢字符串。這里作為重定向地址的參數(shù)pathFormat依舊是一個(gè)路徑模板,它可以包含一個(gè)表示響應(yīng)狀態(tài)的占位符(“{0}”)。

 public static class StatusCodePagesExtensions
 {
 public static IApplicationBuilder UseStatusCodePagesWithReExecute(this IApplicationBuilder app, string pathFormat, string queryFormat = null);
 }

現(xiàn)在我們對(duì)上面演示的這個(gè)實(shí)例略作修改來(lái)演示采服務(wù)端重定向呈現(xiàn)出來(lái)的錯(cuò)誤頁(yè)面。如下面的代碼片段所示,我們僅僅將針對(duì)UseStatusCodePagesWithRedirects方法的調(diào)用替換成針對(duì)UseStatusCodePagesWithReExecute方法的調(diào)用而已。

 public class Program
 {
 private static Random _random = new Random();
 public static void Main()
 {
 new WebHostBuilder()
 .UseKestrel()
 .ConfigureServices(svcs => svcs.AddRouting())
 .Configure(app => app
 .UseStatusCodePagesWithReExecute("/error/{0}")
 .UseRouter(builder=>builder.MapRoute("error/{statuscode}", HandleError))
 .Run(context=>Task.Run(()=>context.Response.StatusCode = _random.Next(400,599))))
 .Build()
 .Run();
 }
 
 private async static Task HandleError(HttpContext context)
 {
 var statusCode = context.GetRouteData().Values["statuscode"];
 await context.Response.WriteAsync($"Error occurred ({statusCode})");
 }
 }

對(duì)于前面演示的實(shí)例,由于錯(cuò)誤頁(yè)面是通過(guò)客戶端重定向的方式呈現(xiàn)出來(lái)的,所以瀏覽器地址欄顯示的是重定向地址。我們?cè)谶x擇這個(gè)實(shí)例中采用了服務(wù)端重定向,雖然顯示的頁(yè)面內(nèi)容并沒(méi)有不同,但是地址欄上的地址是不會(huì)發(fā)生改變的

之所以被命名為UseStatusCodePagesWithReExecute,是因?yàn)橥ㄟ^(guò)這方法注冊(cè)的StatusCodePagesMiddleware中間件進(jìn)行錯(cuò)誤處理的時(shí)候,它僅僅是提供的重定向路徑和查詢字符串應(yīng)用到當(dāng)前HttpContext,然后遞交給后續(xù)管道重新執(zhí)行。UseStatusCodePagesWithReExecute方法中注冊(cè)StatusCodePagesMiddleware中間件的實(shí)現(xiàn)總體上可以由如下所示的代碼片段來(lái)體現(xiàn)。

 public static class StatusCodePagesExtensions
 { 
 public static IApplicationBuilder UseStatusCodePagesWithReExecute(this IApplicationBuilder app,string pathFormat,string queryFormat = null)
 {
 return app.UseStatusCodePages(async context =>
 {
 var newPath = new PathString(string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode));
 var formatedQueryString = queryFormat == null ? null :string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode); 
 context.HttpContext.Request.Path = newPath;
 context.HttpContext.Request.QueryString = newQueryString;
 await context.Next(context.HttpContext);
 });
 }
 }

與ExceptionHandlerMiddleware中間價(jià)類似,StatusCodePagesMiddleware中間件在處理請(qǐng)求的過(guò)程中會(huì)改變當(dāng)前請(qǐng)求上下文的狀態(tài),具體體現(xiàn)在將指定的請(qǐng)求路徑和查詢字符串重新應(yīng)用到當(dāng)前請(qǐng)求上下文中。為了不影響前置中間件對(duì)請(qǐng)求的正常處理,StatusCodePagesMiddleware中間件在完成自身處理流程之后必須將當(dāng)前請(qǐng)求上下文恢復(fù)到原始的狀態(tài)。StatusCodePagesMiddleware中間件依舊是采用一個(gè)特性來(lái)保存原始的路徑和查詢字符串。這個(gè)特性對(duì)應(yīng)的接口為具有如下定義的IStatusCodeReExecuteFeature,令人費(fèi)解的是該接口僅僅包含兩個(gè)針對(duì)路徑的屬性,并沒(méi)有我們希望的用于攜帶原始查詢上下文的屬性,但是默認(rèn)實(shí)現(xiàn)類型StatusCodeReExecuteFeature包含了這個(gè)屬性。

 public interface IStatusCodeReExecuteFeature
 {
 string OriginalPath { get; set; }
 string OriginalPathBase { get; set; }
 }
 
 public class StatusCodeReExecuteFeature : IStatusCodeReExecuteFeature
 {
 public string OriginalPath { get; set; }
 public string OriginalPathBase { get; set; }
 public string OriginalQueryString { get; set; }
 }

當(dāng)StatusCodePagesMiddleware中間件在處理異常請(qǐng)求的過(guò)程中,在將指定的重定向路徑和查詢字符串應(yīng)用到當(dāng)前請(qǐng)求上下文上之前,它會(huì)根據(jù)原始的上下文創(chuàng)建一個(gè)StatusCodeReExecuteFeature特性對(duì)象并將其添加到當(dāng)前HttpContext之上。當(dāng)整個(gè)請(qǐng)求處理過(guò)程結(jié)束之后,StatusCodePagesMiddleware中間件還會(huì)負(fù)責(zé)將這個(gè)特性從當(dāng)前HttpContext中移除,并恢復(fù)原始的請(qǐng)求路徑和查詢字符串。如下所示的代碼片段體現(xiàn)了UseStatusCodePagesWithReExecute方法的真實(shí)邏輯。

 public static class StatusCodePagesExtensions
 {
 public static IApplicationBuilder UseStatusCodePagesWithReExecute(this IApplicationBuilder app,string pathFormat,string queryFormat = null)
 { 
 return app.UseStatusCodePages(async context =>
 {
 var newPath = new PathString(string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode));
 var formatedQueryString = queryFormat == null ? null :string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode);
 var newQueryString = queryFormat == null ? QueryString.Empty : new QueryString(formatedQueryString);
 
 var originalPath = context.HttpContext.Request.Path;
 var originalQueryString = context.HttpContext.Request.QueryString;
 
 context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
 {
 OriginalPathBase = context.HttpContext.Request.PathBase.Value,
 OriginalPath = originalPath.Value,
 OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
 });
 
 context.HttpContext.Request.Path = newPath;
 context.HttpContext.Request.QueryString = newQueryString;
 try
 {
 await context.Next(context.HttpContext);
 }
 finally
 {
 context.HttpContext.Request.QueryString = originalQueryString;
 context.HttpContext.Request.Path = originalPath;
 context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(null);
 }
 });
 }
 }

總結(jié)

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面

ASP.NET Core應(yīng)用錯(cuò)誤處理之StatusCodePagesMiddleware中間件針對(duì)響應(yīng)碼呈現(xiàn)錯(cuò)誤頁(yè)面:前言 StatusCodePagesMiddleware中間件與ExceptionHandlerMiddleware中間件比較類似,它們都是在后續(xù)請(qǐng)求處理過(guò)程中出錯(cuò)的情況下利用一個(gè)錯(cuò)誤處理器來(lái)完成最終的請(qǐng)求處理與響應(yīng)的任務(wù)。它們之間的差異在于對(duì)錯(cuò)誤的界定上,對(duì)于ExceptionHa
推薦度:
  • 熱門焦點(diǎn)

最新推薦

猜你喜歡

熱門推薦

專題
Top