c# - ASP.NET Core Web API 异常处理

标签 c# exception asp.net-core

在使用常规 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/

相关文章:

c++ - 奇怪的异常行为(执行某些未调用的方法)

angular - 将具有 IFormFile 属性的模型从 Angular 2 上传到 ASP.NET Core Web Api

angular - 部署Azure Web App(Angular)不起作用

c# - 包含枚举描述的发布请求

C#/WPF : Assign style programmatically

c# - 无论鼠标位置如何,都使 WPF 滚动查看器滚动

c# - 关于 if 语句的更短代码

c++ - 有没有一种简单的方法来检查 C++ 中的不安全表达式?

java - 使用目录路径创建新类型 "File"对象时出现无效 URL/非法参数异常

c# - vb.net LINQ 选择不同的列表