我使用ASP。NET核心为我的新的REST API项目后使用常规的ASP。NET Web API很多年了。我看不出在ASP中有什么处理异常的好方法。NET核心Web API。我尝试实现一个异常处理过滤器/属性:
public class ErrorHandlingFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
HandleExceptionAsync(context);
context.ExceptionHandled = true;
}
private static void HandleExceptionAsync(ExceptionContext context)
{
var exception = context.Exception;
if (exception is MyNotFoundException)
SetExceptionResult(context, exception, HttpStatusCode.NotFound);
else if (exception is MyUnauthorizedException)
SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
else if (exception is MyException)
SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
else
SetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
}
private static void SetExceptionResult(
ExceptionContext context,
Exception exception,
HttpStatusCode code)
{
context.Result = new JsonResult(new ApiResponse(exception))
{
StatusCode = (int)code
};
}
}
这是我的启动过滤器注册:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilter());
options.Filters.Add(new ErrorHandlingFilter());
});
我遇到的问题是,当我的AuthorizationFilter发生异常时,它没有被ErrorHandlingFilter处理。我希望它能像以前的ASP一样被捕获。NET Web API。
那么我如何捕捉所有应用程序异常以及任何异常从动作过滤器?
最好的办法是使用中间件来实现您正在寻找的日志记录。您希望将异常日志记录放在一个中间件中,然后在另一个中间件中处理显示给用户的错误页面。这允许逻辑分离,并遵循微软在2个中间件组件上的设计。这里有一个很好的微软文档的链接:ASP中的错误处理。网络核心
对于您的特定示例,您可能希望使用StatusCodePage中间件中的一个扩展,或者像这样使用自己的扩展。
您可以在这里找到记录异常的示例:ExceptionHandlerMiddleware.cs
public void Configure(IApplicationBuilder app)
{
// app.UseErrorPage(ErrorPageOptions.ShowAll);
// app.UseStatusCodePages();
// app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
// app.UseStatusCodePages("text/plain", "Response, status code: {0}");
// app.UseStatusCodePagesWithRedirects("~/errors/{0}");
// app.UseStatusCodePagesWithRedirects("/base/errors/{0}");
// app.UseStatusCodePages(builder => builder.UseWelcomePage());
app.UseStatusCodePagesWithReExecute("/Errors/{0}"); // I use this version
// Exception handling logging below
app.UseExceptionHandler();
}
如果你不喜欢这个特定的实现,那么你也可以使用ELM中间件,这里有一些例子
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
// Exception handling logging below
app.UseElmCapture();
app.UseElmPage();
}
如果这不能满足您的需求,您总是可以通过查看它们的ExceptionHandlerMiddleware和ElmMiddleware的实现来生成自己的中间件组件,以掌握构建自己的中间件的概念。
重要的是要在StatusCodePages中间件下面添加异常处理中间件,但要在所有其他中间件组件之上添加异常处理中间件。这样,您的Exception中间件将捕获异常,记录它,然后允许请求继续到StatusCodePage中间件,后者将向用户显示友好的错误页面。
有一个内置的中间件:
ASP。NET Core 5版本:
app.UseExceptionHandler(a => a.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = exceptionHandlerPathFeature.Error;
await context.Response.WriteAsJsonAsync(new { error = exception.Message });
}));
旧版本(他们没有WriteAsJsonAsync扩展):
app.UseExceptionHandler(a => a.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = exceptionHandlerPathFeature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));
它应该做几乎相同的事情,只是要编写的代码少了一点。
重要:记住将它添加在MapControllers \ UseMvc(或。net Core 3中的UseRouting)之前,因为顺序很重要。
最好的办法是使用中间件来实现您正在寻找的日志记录。您希望将异常日志记录放在一个中间件中,然后在另一个中间件中处理显示给用户的错误页面。这允许逻辑分离,并遵循微软在2个中间件组件上的设计。这里有一个很好的微软文档的链接:ASP中的错误处理。网络核心
对于您的特定示例,您可能希望使用StatusCodePage中间件中的一个扩展,或者像这样使用自己的扩展。
您可以在这里找到记录异常的示例:ExceptionHandlerMiddleware.cs
public void Configure(IApplicationBuilder app)
{
// app.UseErrorPage(ErrorPageOptions.ShowAll);
// app.UseStatusCodePages();
// app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
// app.UseStatusCodePages("text/plain", "Response, status code: {0}");
// app.UseStatusCodePagesWithRedirects("~/errors/{0}");
// app.UseStatusCodePagesWithRedirects("/base/errors/{0}");
// app.UseStatusCodePages(builder => builder.UseWelcomePage());
app.UseStatusCodePagesWithReExecute("/Errors/{0}"); // I use this version
// Exception handling logging below
app.UseExceptionHandler();
}
如果你不喜欢这个特定的实现,那么你也可以使用ELM中间件,这里有一些例子
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
// Exception handling logging below
app.UseElmCapture();
app.UseElmPage();
}
如果这不能满足您的需求,您总是可以通过查看它们的ExceptionHandlerMiddleware和ElmMiddleware的实现来生成自己的中间件组件,以掌握构建自己的中间件的概念。
重要的是要在StatusCodePages中间件下面添加异常处理中间件,但要在所有其他中间件组件之上添加异常处理中间件。这样,您的Exception中间件将捕获异常,记录它,然后允许请求继续到StatusCodePage中间件,后者将向用户显示友好的错误页面。
要配置每种异常类型的异常处理行为,您可以使用NuGet包中的中间件:
Community.AspNetCore.ExceptionHandling.NewtonsoftJson
ASP。NET Core 2.0
ASP. aspnetcore . exceptionhandling . mvcNET Core 2.1+。
代码示例:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddExceptionHandlingPolicies(options =>
{
options.For<InitializationException>().Rethrow();
options.For<SomeTransientException>().Retry(ro => ro.MaxRetryCount = 2).NextPolicy();
options.For<SomeBadRequestException>()
.Response(e => 400)
.Headers((h, e) => h["X-MyCustomHeader"] = e.Message)
.WithBody((req,sw, exception) =>
{
byte[] array = Encoding.UTF8.GetBytes(exception.ToString());
return sw.WriteAsync(array, 0, array.Length);
})
.NextPolicy();
// Ensure that all exception types are handled by adding handler for generic exception at the end.
options.For<Exception>()
.Log(lo =>
{
lo.EventIdFactory = (c, e) => new EventId(123, "UnhandlerException");
lo.Category = (context, exception) => "MyCategory";
})
.Response(null, ResponseAlreadyStartedBehaviour.GoToNextHandler)
.ClearCacheHeaders()
.WithObjectResult((r, e) => new { msg = e.Message, path = r.Path })
.Handled();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandlingPolicies();
app.UseMvc();
}