一、ASP.NETCore提供的记录全局异常的几种方式

1、MVC框架-异常处理页(开发环境/生产环境)

1)开发环境

  使用app.UseDeveloperExceptionPage()启用“异常处理页”;异常处理页显示真实的报错信息不可轻易让用户看到,一般在开发环境中使用。

builder.Services.AddControllers();  // 使用控制器
  builder.Services.AddMvc();          // 使用MVC

  var app = builder.Build();

  // Configure the HTTP request pipeline.
  if (app.Environment.IsDevelopment())
  {
      app.UseDeveloperExceptionPage();  // MVC开发环境下的异常处理页;WebAPI中不需要用,只使用SwaggerUI即可。
  }

  ...
  app.MapControllers();
  app.Run();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

ASP.NETCore-中间件Middleware(二)_全局异常_Net7_MVC

2)生产环境

使用app.UseExceptionHandler("/error")自定义“异常处理页”;一般用在生产环境中。

builder.Services.AddControllers();  // 使用控制器
  builder.Services.AddMvc();          // 使用MVC


  var app = builder.Build();

  // Configure the HTTP request pipeline.
  if (app.Environment.IsDevelopment())  // 开发环境下
  {
      app.UseDeveloperExceptionPage();  // MVC开发环境下的异常处理页;WebAPI中不需要用,只使用SwaggerUI即可。
  }
  else  // IsProduction、IsStaging等环境下
  {
      app.UseExceptionHandler("/error");  // MVC开发环境下的自定义的异常处理页(页面为/error);保证返回信息中不包含敏感信息;WebAPI中不需要用,使用异常中间件即可。
  }
  
  ...
  app.MapControllers();
  app.Run();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
 MVC视图(error)
@model fly_chat1_net7.Exceptions.IKnownException_UseExceptionHandler
@{
    ViewData["Title"] = "Index";
}

<h1>错误信息</h1>
<div>发生错误的时间:<label>@Model.ErrorDateTime</label></div>
<!-- 错误信息也可以作为敏感信息,不做展示 -->
<div>错误信息:<label>@Model.ErrorMessage</label></div>
<div>错误代码:<label>@Model.ErrorCode</label></div>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
 MVC控制器(error)
using fly_chat1_net7.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace fly_chat1_net7.Controllers
{
    /// <summary>
    /// MVC展示用户自定义异常信息页(KnownException_UseExceptionHandler)
    /// </summary>
    [AllowAnonymous]
    public class ErrorController : Controller
    {
        /// <summary>
        /// MVC展示用户自定义异常信息页(KnownException_UseExceptionHandler)
        /// </summary>
        /// <returns>返回用户自定义的Error信息</returns>
        [Route("/error")]
        public IActionResult Index()
        {
            // 获取当前上下文里面报出的异常信息
            var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

            var ex = exceptionHandlerPathFeature?.Error;

            // 特殊处理,尝试转换为 IKnownException
            var knownException = ex as IKnownException_UseExceptionHandler;
            // 对于未知异常,我们并不应该把错误异常完整地输出给客户端,而是应该定义一个特殊的信息 Unknown 传递给用户
            // Unknown 其实也是一个 IKnownException 的实现,它的 Message = "未知错误", ErrorCode = 9999
            // 也就是说我们在控制器 throw new Exception("报个错"); 就会看到错误信息
            if (knownException == null)
            {
                knownException = KnownException_UseExceptionHandler.UnKnownException;
            }
            else// 当识别到异常是已知的业务异常时,输出已知的异常,包括异常消息,错误状态码和错误信息,就是在 IKnownException 中的定义
            {
                knownException = KnownException_UseExceptionHandler.SetKnownException(knownException);
            }
            return View(knownException);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
 MVC实体(error)
namespace fly_chat1_net7.Exceptions
{
    /// <summary>
    /// 自定义异常处理页接口_UseExceptionHandler
    /// </summary>
    public interface IKnownException_UseExceptionHandler
    {
        /// <summary>
        /// 异常发生的时间
        /// </summary>
        public DateTime ErrorDateTime { get; }

        /// <summary>
        /// 异常信息(错误信息也可以作为敏感信息,不做展示)
        /// </summary>
        public string ErrorMessage { get; }

        /// <summary>
        /// 异常代码
        /// </summary>
        public int ErrorCode { get; }

        /// <summary>
        /// 异常对象
        /// </summary>
        public object[] ErrorData { get; }
    }
    
     /// <summary>
    /// 自定义异常处理页_UseExceptionHandler
    /// </summary>
    public class KnownException_UseExceptionHandler : IKnownException_UseExceptionHandler
    {
        #region 变量
        /// <summary>
        /// 异常发生的时间
        /// </summary>
        public DateTime ErrorDateTime { get; private set; }

        /// <summary>
        /// 异常信息
        /// </summary>
        public string ErrorMessage { get; private set; }

        /// <summary>
        /// 异常代码
        /// </summary>
        public int ErrorCode { get; private set; }

        /// <summary>
        /// 异常对象
        /// </summary>
        public object[] ErrorData { get; private set; }
        #endregion 变量

        /// <summary>
        /// 未知错误信息实体
        /// </summary>
        public readonly static IKnownException_UseExceptionHandler UnKnownException = new KnownException_UseExceptionHandler { ErrorDateTime = DateTime.Now, ErrorMessage = "未知错误", ErrorCode = 9999, ErrorData = null };

        /// <summary>
        /// 设置“自定义异常处理页”的内容
        /// </summary>
        /// <param name="exception">“自定义异常处理页”的内容</param>
        /// <returns></returns>
        public static IKnownException_UseExceptionHandler SetKnownException(IKnownException_UseExceptionHandler exception)
        {
            return new KnownException_UseExceptionHandler { ErrorDateTime = exception.ErrorDateTime, ErrorMessage = exception.ErrorMessage, ErrorCode = exception.ErrorCode, ErrorData = exception.ErrorData };
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.

  效果类似于下图:

ASP.NETCore-中间件Middleware(二)_全局异常_Net7_异常处理_02

2、异常过滤器  IExceptionFilter

  实现  IExceptionFilter 或  IAsyncExceptionFilter

  可用于实现常见的错误处理策略(如:处理 Razor 页面或控制器创建、 模型绑定、操作筛选器或操作方法中发生的未经处理的异常)。但是,请不要捕获资源筛选器、结果筛选器或 MVC 结果执行中发生的异常。

 ExceptionFilterAttribute)那么灵活。

1)实现 IExceptionFilter
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace fly_chat1_net7.Middlewares
{
    /// <summary>
    /// 异常过滤器(筛选器)
    /// 可用于实现常见的错误处理策略(如:处理 Razor 页面或控制器创建、模型绑定、操作筛选器或操作方法中发生的未经处理的异常)。
    /// 但是,请不要捕获资源筛选器、结果筛选器或 MVC 结果执行中发生的异常。
    /// 建议使用中间件处理异常。 基于所调用的操作方法,仅当错误处理不同时,才使用异常筛选器。
    /// </summary>
    public class SampleExceptionFilter : IExceptionFilter
    {
        #region 微软官方示例
        //private readonly IHostEnvironment _hostEnvironment;

        //public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        //    _hostEnvironment = hostEnvironment;

        //public void OnException(ExceptionContext context)
        //{
        //    if (!_hostEnvironment.IsDevelopment())
        //    {
        //        // Don't display exception details unless running in Development.
        //        return;
        //    }

        //    context.Result = new ContentResult
        //    {
        //        Content = context.Exception.ToString()
        //    };
        //}
        #endregion 微软官方示例

        /// <summary>
        /// 实现OnException方法
        /// </summary>
        /// <param name="context">异常内容</param>
        public void OnException(ExceptionContext context)
        {
            if (context.ExceptionHandled == false)  // 如果异常没有被处理则进行处理
            {
                // 写自己的记录日志的方法
                //_loggerHelper.Error(context.HttpContext.Request.Path, context.Exception);

                context.Result = new ContentResult
                {
                    // 返回状态码设置为200,表示成功
                    StatusCode = StatusCodes.Status200OK,
                    ContentType = "application/json;charset=utf-8",  // 设置返回格式

                    Content = $"{{\r\n  \"Code\": 500,\r\n  \"Message\": \"{context.Exception.ToString()}\"\r\n}}\r\n"
                };
            }

            context.ExceptionHandled = true;  // 设置为true,表示异常已经被处理了
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
2)注册全局使用
var builder = WebApplication.CreateBuilder(args);
  builder.Services.AddControllers();            // 添加Controller
  builder.Services.AddSingleton(new SampleExceptionFilter());  // 添加异常过滤器
  • 1.
  • 2.
  • 3.
3)WebAPI测试:
#region WebAPI测试
  /// <summary>
  /// 异常处理测试API
  /// </summary>
  /// <returns></returns>
  [HttpGet]
  public IActionResult ErrorShow()
  {
      throw new Exception("异常处理测试");
  }
  #endregion WebAPI测试
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

ASP.NETCore-中间件Middleware(二)_全局异常_Net7_异常处理_03

3、异常属性标识  ExceptionFilterAttribute(推荐)

ExceptionFilterAttribute继承IExceptionFilter。但可以获取比IExceptionFilter更多的异常。建议使用中间件处理异常。 基于所调用的操作方法,仅当错误处理不同时,才使用异常筛选器。

1)重写 ExceptionFilterAttribute的OnException方法
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;

namespace fly_chat1_net7.Middlewares
{
    /// <summary>
    /// 全局异常中间件(使用ExceptionFilterAttribute属性标识)
    /// ExceptionFilterAttribute继承IExceptionFilter。但可以获取比IExceptionFilter更多的异常。建议使用中间件处理异常。 基于所调用的操作方法,仅当错误处理不同时,才使用异常筛选器。
    /// </summary>
    public class MyExceptionFilter: ExceptionFilterAttribute
    {
        /// <summary>
        /// 重写OnException方法
        /// </summary>
        /// <param name="context"></param>
        public override void OnException(ExceptionContext context)
        {
            if (context.ExceptionHandled == false)  // 如果异常没有被处理则进行处理
            {
                // 写入日志
                //_loggerHelper.Error(context.HttpContext.Request.Path, context.Exception);

                context.Result = new ContentResult
                {
                    // 返回状态码设置为200,表示成功
                    StatusCode = StatusCodes.Status200OK,
                    ContentType = "application/json;charset=utf-8",  // 设置返回格式

                    Content = $"{{\r\n  \"Code\": 500,\r\n  \"Message\": \"{context.Exception.ToString()}\"\r\n}}\r\n"
                };
            }

            context.ExceptionHandled = true;  // 设置为true,表示异常已经被处理了
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
2)注册全局使用
var builder = WebApplication.CreateBuilder(args);
  builder.Services.AddControllers();            // 添加Controller
  builder.Services.AddSingleton(new MyExceptionFilter());      // 添加全局异常中间件
  • 1.
  • 2.
  • 3.
3)WebAPI测试:
#region WebAPI测试
  /// <summary>
  /// 异常处理测试API
  /// </summary>
  /// <returns></returns>
  [HttpGet]
  public IActionResult ErrorShow()
  {
      throw new Exception("异常处理测试");
  }
  #endregion WebAPI测试
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

ASP.NETCore-中间件Middleware(二)_全局异常_Net7_筛选器_04

4、异常处理-UseExceptionHandler()+委托方法(不推荐)

  略

作者:꧁执笔小白꧂