.NET Core 6.0 自定义异常处理中间件配置

发布时间 2023-07-29 14:55:38作者: 可乐加鸡翅

异常处理的几种方式:

1、通过异常过滤器捕获异常进行处理

2、自定义异常处理中间件,自定义异常处理中间件,中间件依托于请求管道运行,并且中间件的执行是有序的,与业务隔离开的,将中间件放置在请求管道开始能够捕获到全局异常。

 

配置异常处理中间件:

1、 编写自定义异常处理中间件,CustomExceptionHandleMiddleware.cs

 

using IPSP.Common;
using Microsoft.IdentityModel.Tokens;
using System.Net;
using System.Text.Json;

namespace IPSP.Middlewares
{
    public static class CutomExceptionHandleMiddlewareExtension
    {
        public static IApplicationBuilder UseCustomExceptionHandleMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<CustomExceptionHandleMiddleware>();
        }
    }
    public class CustomExceptionHandleMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IWebHostEnvironment _env;
        private readonly ILogger<CustomExceptionHandleMiddleware> _logger;

        public CustomExceptionHandleMiddleware(RequestDelegate next, IWebHostEnvironment env, ILogger<CustomExceptionHandleMiddleware> logger)
        {
            _next = next;
            _env = env;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            string message = string.Empty;
            HttpStatusCode code = HttpStatusCode.InternalServerError;

            ResposeResult result = new ResposeResult(code,message);

            try
            {
                await _next(context);
            }
            catch (SecurityTokenExpiredException exception)
            {
                _logger.LogError(exception,exception.Message);
                SetUnAuthorizeRespose(exception,ref result);
                await WriteToResposeAsync(context,result);
            }
            catch (UnauthorizedAccessException exception)
            {
                _logger.LogError(exception,exception.Message);

                SetUnAuthorizeRespose(exception,ref result);
                await WriteToResposeAsync(context,result);
            }
            catch (Exception exception)
            {
          //asp.net core 引入了 HttpContext.RequestAborted 来监听用户取消请求
                //由用户取消请求触发的异常
                if (context.RequestAborted.IsCancellationRequested && (exception is TaskCanceledException || exception is OperationCanceledException))
                {
                    _logger.LogInformation(exception, exception.Message);
                }
                else
                {
                    _logger.LogError(exception, exception.Message);
                }

                if (_env.IsDevelopment())
                {
                    var dic = new Dictionary<string, string>
                    {
                        ["Exception"] = exception.Message,
                        ["StackTrace"] = exception.StackTrace,
                    };
                    result.Message = JsonSerializer.Serialize(dic);
                }
                else
                {
                    result.Message = message;
                }
                await WriteToResposeAsync(context,result);
            }
        }

        public async Task WriteToResposeAsync(HttpContext context,ResposeResult result)
        {
            if (context.Response.HasStarted)
            {
                throw new InvalidOperationException("The response has already started, the http status code middleware will not be executed.");
            }

            string json = JsonSerializer.Serialize(result);
            context.Response.StatusCode = (int)result.Code;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(json);

        }


        public void SetUnAuthorizeRespose(Exception exception,ref ResposeResult result)
        {
            result.Code = HttpStatusCode.Unauthorized;

            if (_env.IsDevelopment())
            {
                var dic = new Dictionary<string, string>
                {
                    ["Exception"] = exception.Message,
                    ["StackTrace"] = exception.StackTrace
                };

                if (exception is SecurityTokenExpiredException tokenException)
                {
                    dic.Add("Expires",tokenException.Expires.ToString());
                    result.Message = JsonSerializer.Serialize(dic);
                }
            }
        }
    }
}

 

 

public class ResposeResult
    {
        public HttpStatusCode Code { get; set; }
        public string Message { get; set; }

        public ResposeResult(HttpStatusCode code, string message="")
        {
            Code = code;
            Message = message;
        }
    }

 

2、 在Program.cs文件将中间件放置在请求管道的开始处,这样才能捕获到全局的异常

var app = builder.Build();
//注意请将异常处理中间件放置在请求管道的开始处,这样才能捕获到全局的异常
//使用自定义异常处理中间件
app.UseCustomExceptionHandleMiddleware();

 

3、 测试捕捉异常,查看日志记录

[HttpGet]
        public async Task<IActionResult> UserLogin([FromQuery]LoginModel model)
        {
            User user = new User();
            user.Badge = model.Badge;
            user.Password = model.Password;

            int num = 0;
            int value = 10 / num;

            var result = await _userservice.Login(user.Badge,user.Password);
            if (result != null)
            {
                _logger.LogInformation($"{result.Badge}-{result.CName}在{DateTime.Now.ToString("yyyy-dd-MM HH:mm:ss")}登入了IPSP系统!");
            }
            return Ok(result);
        }

查看日志记录:

注意:异常处理中间件和异常过滤器都可以用来捕获程序的异常,只是捕获异常的范围不一样。IExceptionFilter 作为过滤器的一种,它只能捕获控制器内部的异常,如果我们的异常发生控制器之外就无法捕获了,将异常中间件放置在请求管道的第一个,可以捕获到全局的异常。