在ASP.NET Core跨平台应用程序开发中如何捕获并处理全局异常

摘要:
ASP。NET Core WebApi的全局错误捕获和处理方法与ASP NET WebApi应用程序的处理方式不同。更好的解决方案之一是使用中间件。在ASP。NET核心Web Api应用程序开发,中间件是最好的解决方案。

问题描述

在传统的ASP.NET Web Api 应用程序开发中,我们处理全局异常的方法通常是实现一个ExceptionFilterAttribute的子类,如下:

public classErrorHandlingFilter : ExceptionFilterAttribute
{
    public override voidOnException(ExceptionContext context)
    {
        HandleExceptionAsync(context);
        context.ExceptionHandled = true;
    }

    private static voidHandleExceptionAsync(ExceptionContext context)
    {
        var exception =context.Exception;

        if (exception isMyNotFoundException)
            SetExceptionResult(context, exception, HttpStatusCode.NotFound);
        else if (exception isMyUnauthorizedException)
            SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
        else if (exception isMyException)
            SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
        elseSetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
    }

    private static voidSetExceptionResult(
        ExceptionContext context, 
        Exception exception, 
        HttpStatusCode code)
    {
        context.Result = new JsonResult(newApiResponse(exception))
        {
            StatusCode = (int)code
        };
    }
}

然后,在Startup过滤器中注册这个ErrorHandlingFilter自定义的错误处理属性类,如:

config.Filter.Add(new ErrorHandlingFilter());

这样,在ASP.NET Web Api 应用程序就可以捕获全局的错误并进行处理。

但在ASP.NET Core Web Api的应用程序开发开,以上的解决方案就不起作用了。ASP.NET Core Web Api的全局错误捕获与处理方式与ASP.NET Web Api应用程序的处理方式并非一样。

所以,我们得使用适合ASP.NET Core Web Api的全局错误捕获与处理的解决方案。那么,有哪些方案可以捕获和处理ASP.NET Core Web Api应用程序的全局错误呢?

方案一

一个比较好并且推荐的做法是使用中间件(middleware)来处理。在ASP.NET Core Web Api应用程序开发中,中间件是最好的解决方案。它不仅可以处理应用程序的异常,同时也可以捕获过滤器的异常并且也能创建自定义的响应返回结果(比如json的响应返回结果),以下是一个基于ASP.NET Core Web Api的异常处理的中间件:

public classErrorHandlingMiddleware
{
    private readonlyRequestDelegate next;

    publicErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next =next;
    }

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

    private staticTask 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;
        returncontext.Response.WriteAsync(result);
    }
}

使用方法,Startup.cs类文件中,在调用MVC之前注册错误处理中间件ErrorHandlingMiddleware,如:

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

如果中间件捕获到应用程序的异常,则会返回类似如下的响应结果:

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

在中间件ErrorHandlingMiddleware的方法HandleExceptionAsync中,可以对当前上下文的错误进行任意处理(比如,跟踪详细的错误信息,记录错误日志等等)。

方案二

在ASP.NET Core 2或者以上的.NET Core 版本中,我们可以使用UseExceptionHandler扩展方法来捕获并处理ASP.NET Core的全局错误。

首页,在ASP.NET Core 2的Startup.cs文件中注册错误处理扩展方法并指定一个错误处理页面(这里是/Error),如下:

public voidConfigure(IApplicationBuilder app, IHostingEnvironment env)
{
    if(env.IsDevelopment()) {
        //开发模式...
    } else{
        app.UseStatusCodePagesWithReExecute("/Error");
        app.UseExceptionHandler("/Error");
    }
    //其他...
}

接着,定义一个可以指定响应状态码(HttpStatusCode)的异常类,如下:

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

最后,在控制器中创建上文提到的/Error错误处理页面,ASP.NET Core 2捕获到的错误将转到这个错误处理页面。在这个页面,我们就可以处理这些错误/异常,如下:

[AllowAnonymous]
publicIActionResult Error()
{
    //捕获状态码
    var statusCode = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error is HttpException httpEx ?httpEx.StatusCode : (HttpStatusCode)Response.StatusCode;

    //如果是ASP.NET Core Web Api 应用程序,直接返回状态码(不跳转到错误页面,这里假设所有API接口的路径都是以/api/开始的)
    if (HttpContext.Features.Get<IHttpRequestFeature>().RawTarget.StartsWith("/api/", StringComparison.Ordinal))
        return StatusCode((int)statusCode);

    //创建一个友好的错误处理页面视图模型.
    string text = null;
    switch(statusCode) {
        case HttpStatusCode.NotFound: text = "Page not found."; break;
        //其他详细信息
}
    return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, ErrorText =text });
}

注: 如果需要让 API 接口返回json对象,则可以使用return Json(...)来替换return StatusCode(...)

方案三

在ASP.NET Core 2.1+版本中,我们可以使用中间件Community.AspNetCore.ExceptionHandling.Mvc[1]来处理,如下:

public voidConfigureServices(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();

        //确保所有的异常均被捕获
        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 voidConfigure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseExceptionHandlingPolicies();
    app.UseMvc();
}

免责声明:文章转载自《在ASP.NET Core跨平台应用程序开发中如何捕获并处理全局异常》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PHP基础-PHP中的函数声明如何解决找不到方法HttpServletRequest.getServletContext() ---- NoSuchMethodError下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

IdentityServer4-HybridAndClientCredentials

一、服务器 Client设置: 1 new Client 2 { 3 ClientId = "mvc1", 4 ClientName = "后台管理MVC客户端", 5 ClientSecret...

循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一、概述 本篇描述一些常用的数据库操作,包括:条件查询、排序、分页、事务等基本数据库操作。试验的数据库为MySQL。 二、条件查询1、查询所有记录 List<Article...

Web开发中20个很有用的CSS库

来源: 微信公众号文章 在过去的几年中,CSS已经成为一大部分开发者和设计者的最爱,因为它提供了一系列功能和特性。每个月都有无数个围绕CSS的工具被开发者发布以简化WEB开发。像CSS库,框架,应用这样的工具能够为开发者做很多事,而且可以使开发者创造出创新立异的WEB应用。 在过去的几年中,CSS已经成为一大部分开发者和设计者的最爱,因为它提供了一系列功能...

webapi-1 给现有MVC 项目添加 WebAPI

1. 增加一个WebApi Controller, VS 会自动添加相关的引用,主要有System.Web.Http,System.Web.Http.WebHost,System.Net.Http 2. 在App_Start 下创建 WebApiConfig.cs 并注册路由 using System; using System.Collections...

ASP.NET Core 四: Web核心与项目配置,静态内容(文件传输,npm包管理器),中间件,配置(环境配置,启动配置,用户密匙)

概念说明: web核心,本质是一个集成了为Http请求服务功能的承载(Host),一个请求(request)和对应的响应(response)都由HttpContext对象表示。因此就可以在服务的Run程序中对HttpContext的对象做出相关的操作(即对request和response做操作)。 静态内容,这里是关于如何返回静态文件,如html文件,或者...

swift3.0 coreData的使用-日记本demo

效果 需求分析 基于官方MasterDetail模板,官方写了很多复杂的coredata逻辑,在此基础上快速开发简单的日记本程序。 - 主要功能:增、删、改、查 - 界面用默认的界面,将detail页面改为`UITextView`可编辑 - 主页面进行 增、删、查操作 - 子页面进行 删、改、查操作 需求很简单,官方模板还添加了按时间排序的操作 创建工...