ASP.NET Core Web API异常处理

在使用常规ASP.NET Web API多年后,我开始为新的REST API项目使用ASP.NET Core。 我没有看到在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 Core Web API应用程序。 它处理应用程序异常以及过滤器的异常,我完全控制异常处理并创建响应json。 这是我的异常处理中间件:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var code = HttpStatusCode.InternalServerError; // 500 if unexpected

        if      (exception is MyNotFoundException)     code = HttpStatusCode.NotFound;
        else if (exception is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
        else if (exception is MyException)             code = HttpStatusCode.BadRequest;

        var result = JsonConvert.SerializeObject(new { error = exception.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        return context.Response.WriteAsync(result);
    }
}

Startup类中的MVC之前注册它:

app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();

这是一个异常响应的例子:

{ "error": "Authentication token is not valid." }

你可以添加堆栈跟踪,异常类型名称,错误代码或任何你想要的。 非常灵活。 希望这对你来说是一个很好的起点!


你最好的选择是使用中间件来实现你正在寻找的日志。 您希望将异常记录放入一个中间件中,然后处理显示给不同中间件中用户的错误页面。 这允许逻辑分离,并遵循微软用2个中间件组件设计的设计。 这是一个很好的链接到微软的文档:ASP.Net核心错误处理

对于您的具体示例,您可能需要使用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中间件,下面是一些例子:Elm Exception Middleware

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 2 Startup配置为重新执行到错误页面,以查找来自Web服务器的任何错误和任何未处理的异常。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment()) {
        // Debug config here...
    } else {
        app.UseStatusCodePagesWithReExecute("/Error");
        app.UseExceptionHandler("/Error");
    }
    // More config...
}

接下来,定义一个异常类型,它可以让你用HTTP状态代码引发错误。

public class HttpException : Exception
{
    public HttpException(HttpStatusCode statusCode) { StatusCode = statusCode; }
    public HttpStatusCode StatusCode { get; private set; }
}

最后,在错误页面的控制器中,根据错误原因自定义响应,以及最终用户是否直接看到响应。 此代码假定所有API URL都以/api/开头。

[AllowAnonymous]
public IActionResult Error()
{
    // Gets the status code from the exception or web server.
    var statusCode = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error is HttpException httpEx ?
        httpEx.StatusCode : (HttpStatusCode)Response.StatusCode;

    // For API errors, responds with just the status code (no page).
    if (HttpContext.Features.Get<IHttpRequestFeature>().RawTarget.StartsWith("/api/", StringComparison.Ordinal))
        return StatusCode((int)statusCode);

    // Creates a view model for a user-friendly error page.
    string text = null;
    switch (statusCode) {
        case HttpStatusCode.NotFound: text = "Page not found."; break;
        // Add more as desired.
    }
    return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, ErrorText = text });
}

ASP.NET Core将记录错误详细信息供您进行调试,因此状态码可能是您想要提供给(可能不受信任的)请求者的所有内容。 如果你想显示更多信息,你可以增强HttpException来提供它。 对于API错误,您可以将JSON编码的错误信息替换为return StatusCode...return Json...

链接地址: http://www.djcxy.com/p/20521.html

上一篇: ASP.NET Core Web API exception handling

下一篇: Difference between MVC 5 Project and Web Api Project