在使用常规 ASP.NET Web API 多年后,我将 ASP.NET Core 用于我的新 REST API 项目。我没有看到在 ASP.NET Core 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 一起工作一样。
那么我怎样才能捕获所有应用程序异常以及来自 Action Filters 的任何异常?
最佳答案
快速简单的异常处理
只需在 ASP.NET 路由到您的中间件注册之前添加此中间件。
app.UseExceptionHandler(c => c.Run(async context =>
{
var exception = context.Features
.Get<IExceptionHandlerPathFeature>()
.Error;
var response = new { error = exception.Message };
await context.Response.WriteAsJsonAsync(response);
}));
app.UseMvc(); // or .UseRouting() or .UseEndpoints()
完成!
为日志记录和其他目的启用依赖注入(inject)
第 1 步。在您的启动中,注册您的异常处理路由:
// It should be one of your very first registrations
app.UseExceptionHandler("/error"); // Add this
app.UseEndpoints(endpoints => endpoints.MapControllers());
第 2 步。创建将处理所有异常并产生错误响应的 Controller :
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorsController : ControllerBase
{
[Route("error")]
public MyErrorResponse Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error; // Your exception
var code = 500; // Internal Server Error by default
if (exception is MyNotFoundException) code = 404; // Not Found
else if (exception is MyUnauthException) code = 401; // Unauthorized
else if (exception is MyException) code = 400; // Bad Request
Response.StatusCode = code; // You can use HttpStatusCode enum instead
return new MyErrorResponse(exception); // Your error model
}
}
一些重要的注意事项和观察结果:
- 您可以将依赖项注入(inject)到 Controller 的构造函数中。
-
[ApiExplorerSettings(IgnoreApi = true)]
是需要的。否则,它可能会破坏你的虚张声势 - 再次,
app.UseExceptionHandler("/error");
必须是您的初创公司中 HitTest 门的注册之一Configure(...)
方法。将它放在方法的顶部可能是安全的。 app.UseExceptionHandler("/error")
中的路径在 Controller 中[Route("error")]
应该相同,以允许 Controller 处理从异常处理程序中间件重定向的异常。
这是 link Microsoft 官方文档。
响应模型的想法。
实现您自己的响应模型和异常。 这个例子只是一个很好的起点。每个服务都需要以自己的方式处理异常。使用所描述的方法,您可以完全灵活地控制异常处理和从您的服务返回正确的响应。
一个错误响应模型的例子(只是给你一些想法):
public class MyErrorResponse
{
public string Type { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public MyErrorResponse(Exception ex)
{
Type = ex.GetType().Name;
Message = ex.Message;
StackTrace = ex.ToString();
}
}
对于更简单的服务,您可能希望实现如下所示的 http 状态代码异常:
public class HttpStatusException : Exception
{
public HttpStatusCode Status { get; private set; }
public HttpStatusException(HttpStatusCode status, string msg) : base(msg)
{
Status = status;
}
}
可以这样从任何地方抛出:
throw new HttpStatusCodeException(HttpStatusCode.NotFound, "User not found");
然后您的处理代码可以简化为:
if (exception is HttpStatusException httpException)
{
code = (int) httpException.Status;
}
HttpContext.Features.Get<IExceptionHandlerFeature>()
什么?
ASP.NET Core 开发人员接受了中间件的概念,在中间件中,不同方面的功能(例如 Auth、MVC、Swagger 等)在请求处理管道中分离并按顺序执行。每个中间件都可以访问请求上下文,并且可以在需要时写入响应。如果以与 MVC 异常相同的方式处理来自非 MVC 中间件的错误很重要,那么从 MVC 中取出异常处理是有意义的,我发现这在现实世界的应用程序中很常见。所以因为内置的异常处理中间件不是 MVC 的一部分,MVC 本身对此一无所知,反之亦然,异常处理中间件并不真正知道异常来自哪里,当然它知道它发生在某个地方请求执行的管道。但两者可能都需要彼此“连接”。因此,当任何地方都没有捕获到异常时,异常处理中间件会捕获它并重新运行在其中注册的路由的管道。这就是您如何以一致的方式将异常处理“传递”回 MVC content negotiation或者如果您愿意,可以使用其他一些中间件。异常本身是从公共(public)中间件上下文中提取的。看起来很有趣,但完成了工作:)。
关于c# - ASP.NET Core Web API 异常处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38630076/