内容简介:您看此文用·秒,转发只需1秒呦~
在本文中,我将讲解如何通过自定义 ExceptionHandlerMiddleware ,以便在中间件管道中发生错误时创建自定义响应,而不是提供一个“重新执行”管道的路径。
作者:依乐祝
译文:https://www.cnblogs.com/yilezhu/p/12497937.html
原文:https://andrewlock.net/creating-a-custom-error-handler-middleware-function/
Razor页面中的异常处理
所有的.NET应用程序都有可能会产生错误,并且不幸地引发异常,因此在ASP.NET中间件管道中处理这些异常显得非常重要。服务器端呈现的应用程序(如Razor Pages)通常希望捕获这些异常并重定向到一个错误页面。
例如,如果您创建一个使用Razor Pages( dotnet new webapp )的新Web应用程序,您将在 Startup.Configure 中看到如下的中间件配置:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
// .. other middleware not shown
}
在 Development 环境中运行时,应用程序将捕获处理请求时引发的所有异常,并使用一个非常有用的 DeveloperExceptionMiddleware 方法将其以网页的形式进行显示:
开发人员例外页面
这在本地开发期间非常有用,因为它使您可以快速检查堆栈跟踪,请求标头,路由详细信息以及其他内容。
当然,这些都是您不想在生产中公开的敏感信息。因此,当不在开发阶段时,我们将使用其他异常处理程序 ExceptionHandlerMiddleware 。此中间件允许您提供一个请求路径,默认情况下是 "/Error" ,并使用它“重新执行”中间件管道,以生成最终响应:
使用以下命令重新执行管道
Razor Pages应用程序的最终结果是,每当生产中发生异常时,就会返回这个 Error.cshtml 的Razor 页面:
生产中的例外页面
这涵盖了razor 页面的异常处理,但是Web API呢?
Web API的异常处理
Web API模板( dotnet new webapi )中的默认异常处理类似于Razor Pages使用的异常处理,但有一个重要的区别:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// .. other middleware not shown
}
如您所见 DeveloperExceptionMiddleware ,在 Development 环境中仍会添加,但是在生产中根本没有添加错误处理!这没有听起来那么糟糕:即使没有异常处理中间件,ASP.NET Core也会在其底层架构中捕获该异常,将其记录下来,并向客户端返回一个空白的 500 响应:
一个例外
如果您正在使用该 [ApiController] 属性(你可能应该这样使用),并且该错误来自您的Web API控制器,那么 ProblemDetails 默认情况下会得到一个结果,或者您可以进一步对其进行自定义。
对于Web API客户端来说,这实际上还不错。您的API使用者应能够处理错误响应,因此最终用户将不会看到上面的“中断”页面。但是,它通常不是那么简单。
例如,也许您使用的是错误的标准格式,例如ProblemDetails格式。如果您的客户期望所有错误都具有该格式,那么在某些情况下生成的空响应很可能导致客户端中断。同样,在 Development 环境中,当客户端期望返回JSON时而你返回一个HTML开发人员异常页面,这可能会导致问题!
官方文档中描述了一种解决方案,建议您创建 ErrorController 并具有两个终结点的:
[ApiController]
public class ErrorController : ControllerBase
{
[Route("/error-local-development")]
public IActionResult ErrorLocalDevelopment() => Problem(); // Add extra details here
[Route("/error")]
public IActionResult Error() => Problem();
}
然后使用Razor Pages应用程序中使用的相同“重新执行”功能来生成响应:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseExceptionHandler("/error-local-development");
}
else
{
app.UseExceptionHandler("/error");
}
// .. other middleware
}
这可以正常工作,但是对于使用生成异常的同一基础结构(例如Razor Pages或MVC)来生成异常消息,总有一些困扰我。由于被第二次抛出异常,我多次被失败的 错误 响应所困扰!因此,我喜欢采取稍微不同的方法。
使用ExceptionHandler代替ExceptionHandlingPath
当我第一次开始使用ASP.NET Core时,解决此问题的方法是编写自己的自定义ExceptionHandler中间件来直接生成响应。“处理异常不是那么难,对吧”?
事实证明,这要复杂得多(我知道,令人震惊)。您需要处理各种边缘情况,例如:
-
如果在发生异常时响应已经开始发送,则您将无法拦截它。
-
如果在
EndpointMiddleware发生异常时已执行,则需要对选定的端点进行一些处理 -
您不想缓存错误响应
ExceptionHandlerMiddleware 处理所有这些情况,所以重新写你自己的版本不是一条要走的路。幸运的是,尽管通常显示的方法是为中间件提供重新执行的路径,但还有另一种选择-直接提供处理函数。
在 ExceptionHandlerMiddleware 中有一个 ExceptionHandlerOptions 参数。该选项对象具有两个属性:
public class ExceptionHandlerOptions
{
public PathString ExceptionHandlingPath { get; set; }
public RequestDelegate ExceptionHandler { get; set; }
}
当你向 UseExceptionHandler(path) 方法提供重新执行的路径时,实际上是在options对象上设置 ExceptionHandlingPath 。同样的,如果需要的话,您可以设置 ExceptionHandler 属性,并使用 UseExceptionHandler() 将 ExceptionHandlerOptions 的实例直接传递给中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
ExceptionHandler = // .. to implement
});
// .. othe middleware
}
另外,您可以使用 UseExceptionHandler() 的另一个重载方法并配置一个迷你中间件管道来生成响应:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseExceptionHandler(err => err.UseCustomErrors(env)); // .. to implement
// .. othe middleware
}
两种方法都是等效的,因此更多是关于喜好的问题。在本文中,我将使用第二种方法并实现该 UseCustomErrors() 功能。
创建自定义异常处理函数
对于此示例,我将假设我们在中间件管道中遇到异常时需要生成一个 ProblemDetails 的对象。我还要假设我们的API仅支持JSON。这就避免了我们不必担心XML内容协商等问题。在开发环境中, ProblemDetails 响应将包含完整的异常堆栈跟踪,而在生产环境中,它将仅显示一般错误消息。
ProblemDetails 是返回HTTP响应中错误的机器可读详细信息的行业标准方法。这是从ASP.NET Core 3.x(在某种程度上在2.2版中)的Web API返回错误消息的普遍支持的方法。
我们将从在静态帮助器类中定义 UseCustomErrors 函数开始。该帮助类将一个生成响应的中间件添加到 IApplicationBuilder 方法扩展中。在开发环境中,它最终会调用 WriteResponse 方法,并且设置includeDetails: true 。在其他环境中, includeDetails`设置为false。
using System;
using System.Diagnostics;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
public static class CustomErrorHandlerHelper
{
public static void UseCustomErrors(this IApplicationBuilder app, IHostEnvironment environment)
{
if (environment.IsDevelopment())
{
app.Use(WriteDevelopmentResponse);
}
else
{
app.Use(WriteProductionResponse);
}
}
private static Task WriteDevelopmentResponse(HttpContext httpContext, Func<Task> next)
=> WriteResponse(httpContext, includeDetails: true);
private static Task WriteProductionResponse(HttpContext httpContext, Func<Task> next)
=> WriteResponse(httpContext, includeDetails: false);
private static async Task WriteResponse(HttpContext httpContext, bool includeDetails)
{
// .. to implement
}
}
剩下的就是实现 WriteResponse 方法来生成我们的响应的功能。这将从 ExceptionHandlerMiddleware (通过 IExceptionHandlerFeature )中检索异常,并构建一个包含要显示的详细信息的 ProblemDetails 对象。然后,它使用 System.Text.Json 序列化程序将对象写入Response流。
private static async Task WriteResponse(HttpContext httpContext, bool includeDetails)
{
// Try and retrieve the error from the ExceptionHandler middleware
var exceptionDetails = httpContext.Features.Get<IExceptionHandlerFeature>();
var ex = exceptionDetails?.Error;
// Should always exist, but best to be safe!
if (ex != null)
{
// ProblemDetails has it's own content type
httpContext.Response.ContentType = "application/problem+json";
// Get the details to display, depending on whether we want to expose the raw exception
var title = includeDetails ? "An error occured: " + ex.Message : "An error occured";
var details = includeDetails ? ex.ToString() : null;
var problem = new ProblemDetails
{
Status = 500,
Title = title,
Detail = details
};
// This is often very handy information for tracing the specific request
var traceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier;
if (traceId != null)
{
problem.Extensions["traceId"] = traceId;
}
//Serialize the problem details object to the Response as JSON (using System.Text.Json)
var stream = httpContext.Response.Body;
await JsonSerializer.SerializeAsync(stream, problem);
}
}
您可以在序列化 ProblemDetails 之前记录从 HttpContext 中检索的自己喜欢的任何其他值。
请注意,在调用异常处理程序方法之前, ExceptionHandlerMiddleware 会 清除路由值,以使这些值不可用。
如果您的应用程序现在在 Development 环境中引发异常,则您将在响应中获取作为JSON返回的完整异常:
开发中的ProblemDetails响应
在生产环境中,您仍然会得到ProblemDetails响应,但是省略了详细信息:
生产中的ProblemDetails响应
与MVC /重新执行路径方法相比,此方法显然具有一些局限性,即您不容易获得模型绑定,内容协商,简单的序列化或本地化(取决于您的方法)。
如果您需要其中任何一个(例如,也许您使用PascalCase而不是camelCase从MVC进行序列化),那么使用此方法可能比其价值更麻烦。如果是这样,那么所描述的Controller方法可能是明智的选择。
如果您不关心这些,那么本文中显示的简单处理程序方法可能是更好的选择。无论哪种方式,都不要尝试实现自己的版本 ExceptionHandlerMiddleware -使用可用的扩展点!
总结
在这篇文章中,我描述了Razor Pages和Web API的默认异常处理中间件方法。我着重指出了默认Web API模板配置的问题,尤其是在客户端期望有效JSON的情况下,即使出现错误也是如此。
然后,我从官方文档中展示了建议的方法,该方法使用MVC控制器为API 生成 ProblemDetails 响应。这种方法效果很好,除非问题出在您的MVC配置本身上,否则尝试执行 ErrorController 将会失败。
作为替代方案,我展示了如何使用 ExceptionHandlerMiddleware 为生成响应提供定制的异常处理功能。我最后展示了一个示例处理程序,该处理程序将 ProblemDetails 对象序列化为JSON,包括 Development 环境中的详细信息,并在其他环境中将其排除在外。
往期 精彩 回顾
.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
【.NET Core微服务实战-统一身份认证】开篇及目录索引
Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)
.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
给我好看
您看此文用
·
秒,转发只需1秒呦~
好看你就
点点
我
以上所述就是小编给大家介绍的《如何创建一个自定义的`ErrorHandlerMiddleware`方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [译] 从 0 创建自定义元素
- 【自定义】通过Dockerfile创建httpd镜像
- 游戏制作之路(31)创建自定义的按钮
- 创建自定义PSObject PowerShell 2.0
- Angular 使用 ControlValueAccessor 创建自定义表单控件
- laravel 创建自定义的artisan make命令
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
An Introduction to Probability Theory and Its Applications
William Feller / Wiley / 1991-1-1 / USD 120.00
Major changes in this edition include the substitution of probabilistic arguments for combinatorial artifices, and the addition of new sections on branching processes, Markov chains, and the De Moivre......一起来看看 《An Introduction to Probability Theory and Its Applications》 这本书的介绍吧!