快速和简单的异常处理
简单地在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()
完成了!
为日志记录和其他目的启用依赖注入
步骤1。在启动过程中,注册异常处理路由:
// It should be one of your very first registrations
app.UseExceptionHandler("/error"); // Add this
app.UseEndpoints(endpoints => endpoints.MapControllers());
步骤2。创建控制器,处理所有异常并产生错误响应:
[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
}
}
一些重要的注意事项和观察:
You can inject your dependencies into the Controller's constructor.
[ApiExplorerSettings(IgnoreApi = true)] is needed. Otherwise, it may break your Swashbuckle swagger
Again, app.UseExceptionHandler("/error"); has to be one of the very top registrations in your Startup Configure(...) method. It's probably safe to place it at the top of the method.
The path in app.UseExceptionHandler("/error") and in controller [Route("error")] should be the same, to allow the controller handle exceptions redirected from exception handler middleware.
这里是微软官方文档的链接。
响应模型思想。
实现您自己的响应模型和异常。
这个例子只是一个很好的起点。每个服务都需要以自己的方式处理异常。使用所描述的方法,您可以完全灵活地处理异常并从服务返回正确的响应。
一个错误响应模型的例子(只是给你一些想法):
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功能。Get < IExceptionHandlerFeature >()什么?
ASP.NET Core developers embraced the concept of middlewares where different aspects of functionality such as Auth, MVC, Swagger etc. are separated and executed sequentially in the request processing pipeline. Each middleware has access to request context and can write into the response if needed. Taking exception handling out of MVC makes sense if it's important to handle errors from non-MVC middlewares the same way as MVC exceptions, which I find is very common in real world apps. So because built-in exception handling middleware is not a part of MVC, MVC itself knows nothing about it and vice versa, exception handling middleware doesn't really know where the exception is coming from, besides of course it knows that it happened somewhere down the pipe of request execution. But both may needed to be "connected" with one another. So when exception is not caught anywhere, exception handling middleware catches it and re-runs the pipeline for a route, registered in it. This is how you can "pass" exception handling back to MVC with consistent content negotiation or some other middleware if you wish. The exception itself is extracted from the common middleware context. Looks funny but gets the job done :).