在 Asp.Net Core 中进行全局异常的捕获与配置

AspNet Core 中的全局处理

IExceptionFilter

Asp.Net Core 中我们可以使用 IExceptionFilter 过滤器来捕获 AspNet Core 中控制器产生的错误,它也提供更推荐使用的异步版本 IAsyncExceptionFilter

首先我们创建一个 Asp.Net Core MVC Web 应用程序,新建 MyExceptionFilter 类,使其实现 IAsyncExceptionFilter 接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyExceptionFilter : IAsyncExceptionFilter
{
public Task OnExceptionAsync(ExceptionContext context)
{
if (context.ExceptionHandled == false)
{
string msg = context.Exception.Message;
context.Result = new ContentResult
{
Content = msg,
StatusCode = StatusCodes.Status200OK,
ContentType = "text/html;charset=utf-8"
};
}
context.ExceptionHandled = true; //异常已处理了

return Task.CompletedTask;
}
}

startup.cs 类中修改 services

1
services.AddControllersWithViews(options => options.Filters.Add(new MyExceptionFilter()));

添加 MyExceptionController 控制器

1
2
3
4
5
6
7
8
public class MyExceptionController : Controller
{
[HttpGet]
public IActionResult GetException()
{
throw new Exception("手动抛出异常");
}
}

我们访问 /MyException/GetException 页面输出

1
Exception: 手动抛出异常

这样我们就能捕获到控制器抛出的异常然后将之更改为我们想要的功能,比如让其返回一个 Status Code 为 200 的页面。

中间件处理异常

AspNet Core 使用管道机制,管道的特点就是层层传递,这样我们可以在管道中捕获全局异常。

添加 MyExceptionMiddleware 管道类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MyExceptionMiddleware
{
private readonly RequestDelegate _next;
public MyExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
httpContext.Response.ContentType = "application/problem+json";

var title = "异常: " + ex.Message;
var details = ex.ToString();

var problem = new ProblemDetails
{
Status = 200,
Title = title,
Detail = details
};

var stream = httpContext.Response.Body;
var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
await JsonSerializer.SerializeAsync(stream, problem, options);
}
}
}

startup.cs 类中添加管道,如果你想捕获其他管道中的异常那就放在最前面,如果只想捕获控制器异常则放在最后,当然也可放在你认为合适的位置。

1
app.UseMiddleware<MyExceptionMiddleware>();

运行有输出

1
"title":"异常: 手动抛出异常"

注:项目默认会有一个给我们处理异常的中间件

在开发环境和生产环境分别对应,一个会输出详细的错误信息,供调试使用,另一个则跳转到错误页面。如果要使用自己的,可将这两个去除或者在其上进行配置拓展。

1
2
3
app.UseDeveloperExceptionPage();

app.UseExceptionHandler("/Home/Error");

UseExceptionHandler 中间件

我们重新配置此中间件

1
app.UseExceptionHandler(appbuilder => appbuilder.Use(ExceptionHandlerDemo));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private async Task ExceptionHandlerDemo(HttpContext httpContext, Func<Task> next)
{
//该信息由ExceptionHandlerMiddleware中间件提供,里面包含了ExceptionHandlerMiddleware中间件捕获到的异常信息。
var exceptionDetails = httpContext.Features.Get<IExceptionHandlerFeature>();
var ex = exceptionDetails?.Error;

if (ex != null)
{
httpContext.Response.ContentType = "application/problem+json";

var title = "An error occured: " + ex.Message;
var details = ex.ToString();

var problem = new ProblemDetails
{
Status = 500,
Title = title,
Detail = details
};

var stream = httpContext.Response.Body;
var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
await JsonSerializer.SerializeAsync(stream, problem, options);
}
}

输出

1
"title":"异常: 手动抛出异常"

本文章抄袭自:

【5min+】AspNet Core中的全局异常处理 - 句幽 - 博客园 (cnblogs.com)