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

一、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();

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

  效果类似于下图:

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,表示异常已经被处理了
        }
    }
}

2)注册全局使用

  var builder = WebApplication.CreateBuilder(args);
  builder.Services.AddControllers();            // 添加Controller
  builder.Services.AddSingleton(new SampleExceptionFilter());  // 添加异常过滤器

3)WebAPI测试:

  #region WebAPI测试
  /// <summary>
  /// 异常处理测试API
  /// </summary>
  /// <returns></returns>
  [HttpGet]
  public IActionResult ErrorShow()
  {
      throw new Exception("异常处理测试");
  }
  #endregion WebAPI测试

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,表示异常已经被处理了
        }
    }
}

2)注册全局使用

  var builder = WebApplication.CreateBuilder(args);
  builder.Services.AddControllers();            // 添加Controller
  builder.Services.AddSingleton(new MyExceptionFilter());      // 添加全局异常中间件

3)WebAPI测试:

  #region WebAPI测试
  /// <summary>
  /// 异常处理测试API
  /// </summary>
  /// <returns></returns>
  [HttpGet]
  public IActionResult ErrorShow()
  {
      throw new Exception("异常处理测试");
  }
  #endregion WebAPI测试

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

  略

posted @   ꧁执笔小白꧂  阅读(313)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
历史上的今天:
2022-05-17 VS报错-无法解析位于global.json 中指定的 .NET SDK 版本
点击右上角即可分享
微信分享提示