zoukankan      html  css  js  c++  java
  • 在ASP.NET Core跨平台应用程序开发中如何捕获并处理全局异常

    问题描述

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

    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
            };
        }
    }

    然后,在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 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.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 void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment()) {
            // 开发模式...
        } else {
            app.UseStatusCodePagesWithReExecute("/Error");
            app.UseExceptionHandler("/Error");
        }
        // 其他...
    }

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

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

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

    [AllowAnonymous]
    public IActionResult 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 void ConfigureServices(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 void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseExceptionHandlingPolicies();
        app.UseMvc();
    }
  • 相关阅读:
    POJ 1502 MPI Maelstrom
    BNUOJ4359 无爱编号
    BNUOJ 6727 Bone Collector
    SAP成都研究院郑晓霞:Shift Left Testing和软件质量保证的一些思考
    聊聊C语言和ABAP
    小技巧:不用任何媒体处理软件进行视频压缩
    如何处理Docker错误消息:please add——insecure-registry
    如何处理Docker的错误消息request canceled:Docker代理问题
    推荐一个高大上的网易云音乐命令行播放工具:musicbox
    Windows下使用python库 curses遇到错误消息的解决方案
  • 原文地址:https://www.cnblogs.com/zoro-zero/p/13491061.html
Copyright © 2011-2022 走看看