C# .NET6 WebAPI 全局异常过滤器
自定义返回级别
View Code
View Code
View Code
View Code
View Code
View Code
View Code
View Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Demo { /// <summary> /// 自定义返回级别 /// </summary> public enum ResultLevel : int { /// <summary> /// 正确 /// </summary> OK = 0, /// <summary> /// 警告 /// </summary> Warning = 1, /// <summary> /// 异常 /// </summary> Error = 2 } }
自定义返回代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Demo { /// <summary> /// 自定义返回代码 /// </summary> public enum ResultCode : int { /// <summary> /// 正确 /// </summary> OK = 0, /// <summary> /// 用户名或密码错误 /// </summary> LoginFailed = 1, /// <summary> /// 身份认证失败 /// </summary> IdentityAuthFailed = 2, /// <summary> /// 数据模型已存在 /// </summary> ModelIsExist = 101, /// <summary> /// 数据模型不存在 /// </summary> ModelNoExist = 102, /// <summary> /// 数据模型错误 /// </summary> ModelError = 103, /// <summary> /// 文件校验失败 /// </summary> FileError = 104, /// <summary> /// MQTT回执超时 /// </summary> MqttRecriptTimeout = 105, /// <summary> /// MQTT回执结果失败 /// </summary> MqttRecriptResultFail = 106, /// <summary> /// HTTP请求失败 /// </summary> HttpRequestFail = 107, /// <summary> /// 请求内容类型错误 /// </summary> ContentTypeError = 996, /// <summary> /// 用户信息异常 /// </summary> UserInfoError = 997, /// <summary> /// 系统异常 /// </summary> LogicError = 998, /// <summary> /// 系统异常 /// </summary> OtherError = 999 } }
自定义返回信息
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Demo { /// <summary> /// 自定义返回信息 /// </summary> public class ResultMsg { #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 public const string OK = "ok"; public const string LoginFailed = "用户名或密码错误"; public const string IdentityAuthFailed = "身份认证失败"; public const string ModelIsExist = "数据模型已存在"; public const string ModelNoExist = "数据模型不存在"; public const string ModelError = "数据模型错误"; public const string FileError = "文件校验失败"; public const string MqttRecriptTimeout = "等待MQTT回执超时"; public const string ContentTypeError = "请求内容类型错误"; public const string UserInfoError = "用户信息异常"; public const string LogicError = "业务逻辑异常"; public const string OtherError = "系统内部异常"; public const string HttpRequestFail = "HTTP请求失败:{0}"; #pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 } }
自定义返回结构
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Demo { /// <summary> /// 自定义返回结构 /// </summary> public class CustomResult<T> { /// <summary> /// 返回码 /// </summary> public string? Code { get; set; } /// <summary> /// 返回信息 /// </summary> public string? Msg { get; set; } = ResultMsg.OK; /// <summary> /// 返回数据 /// </summary> public T? Data { get; set; } /// <summary> /// 构造函数 /// </summary> public CustomResult() { Code = ((int)ResultLevel.OK).ToString() + ((int)ResultCode.OK).ToString("D3"); } /// <summary> /// 构造函数 /// </summary> /// <param name="t"></param> public CustomResult(T? t) { if (t is CustomException cex) { Code = ((int)cex.Level).ToString() + ((int)cex.Code).ToString("D3"); Msg = cex.Message; } else if (t is Exception) { Code = ((int)ResultLevel.Error).ToString() + ((int)ResultCode.OtherError).ToString("D3"); Msg = ResultMsg.OtherError; } else { Code = ((int)ResultLevel.OK).ToString() + ((int)ResultCode.OK).ToString("D3"); Data = t; } } /// <summary> /// 构造函数 /// </summary> /// <param name="level"></param> /// <param name="code"></param> /// <param name="t"></param> public CustomResult(ResultLevel level, ResultCode code, T t) { Code = ((int)level).ToString() + ((int)code).ToString("D3"); Data = t; } } }
自定义异常类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Demo { /// <summary> /// 自定义异常类 /// </summary> [Serializable] public class CustomException : Exception { /// <summary> /// 错误级别 /// </summary> public ResultLevel Level { get; set; } = ResultLevel.Error; /// <summary> /// 错误码 /// </summary> public ResultCode Code { get; set; } = ResultCode.OtherError; /// <summary> /// 构造函数 /// </summary> public CustomException() { } /// <summary> /// 构造函数 /// </summary> /// <param name="message"></param> public CustomException(string? message) : base(message) { } /// <summary> /// 构造函数 /// </summary> /// <param name="errLevel"></param> /// <param name="errCode"></param> /// <param name="message"></param> public CustomException(ResultLevel errLevel, ResultCode errCode, string? message) : base(message) { Level = errLevel; Code = errCode; } /// <summary> /// 构造函数 /// </summary> /// <param name="errLevel"></param> /// <param name="errCode"></param> public CustomException(ResultLevel errLevel, ResultCode errCode) : base() { Level = errLevel; Code = errCode; } } }
自定义异常过滤器(控制器全局异常过滤器)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Demo { /// <summary> /// 自定义异常过滤器(控制器异常) /// </summary> public class CustomExceptionFilterAttribute : ExceptionFilterAttribute { /// <summary> /// 日志 /// </summary> private readonly ILogger _logger; /// <summary> /// 构造函数 /// </summary> /// <param name="logger"></param> public CustomExceptionFilterAttribute(ILogger<CustomExceptionFilterAttribute> logger) { _logger = logger; } /// <summary> /// 发生异常事件 /// </summary> /// <param name="context"></param> public override void OnException(ExceptionContext context) { // 自定义异常 if (context.Exception is CustomException ex) { _logger.LogError(ex, ResultMsg.LogicError); var result = new CustomResult<CustomException>(ex); context.Result = new BadRequestObjectResult(result); } // 系统异常 else { _logger.LogError(context.Exception, ResultMsg.OtherError); var result = new CustomResult<Exception>(context.Exception); context.Result = new ObjectResult(result) { StatusCode = StatusCodes.Status500InternalServerError }; } // 标记异常已处理 context.ExceptionHandled = true; } /// <summary> /// 发生异常事件 /// </summary> /// <param name="context"></param> /// <returns></returns> public override Task OnExceptionAsync(ExceptionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } OnException(context); return Task.CompletedTask; } } }
自定义扩展类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Text.Json; namespace Demo { /// <summary> /// 自定义扩展类 /// </summary> public static class CustomExpand { /// <summary> /// 添加模型绑定异常处理 /// </summary> /// <param name="services"></param> /// <exception cref="CustomException"></exception> public static void AddModelBindingExceptionHandling(this IServiceCollection services) { services.Configure<ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = actionContext => { // 获取验证失败的模型字段 //var errors = actionContext.ModelState // .Where(s => s.Value != null && s.Value.ValidationState == ModelValidationState.Invalid) // .SelectMany(s => s.Value!.Errors.ToList()) // .Select(e => e.ErrorMessage) // .ToList(); var error = actionContext.ModelState .Where(s => s.Value != null && s.Value.ValidationState == ModelValidationState.Invalid) .SelectMany(s => s.Value!.Errors.ToList()) .Select(e => e.ErrorMessage).FirstOrDefault(); // 统一返回格式 throw new CustomException(ResultLevel.Error, ResultCode.ModelError, error); //var result = new CustomResult<List<string>>(ResultLevel.Error, ResultCode.ModelError, errors); //return new BadRequestObjectResult(result); }; }); } /// <summary> /// 添加身份认证事件 /// </summary> /// <param name="options"></param> /// <returns></returns> public static JwtBearerOptions AddAuthenticationEvents(this JwtBearerOptions options) { // JWT options.Events = new JwtBearerEvents() { // 未登录 OnChallenge = context => AuthenticationFailed(context), // 身份认证失败 OnAuthenticationFailed = context => AuthenticationFailed(context), // 没有权限 OnForbidden = context => AuthenticationFailed(context), }; return options; } /// <summary> /// 身份认证失败 /// </summary> /// <param name="context"></param> /// <returns></returns> private static Task AuthenticationFailed(BaseContext<JwtBearerOptions> context) { var ex = new CustomException(ResultLevel.Error, ResultCode.IdentityAuthFailed, ResultMsg.IdentityAuthFailed); var result = new CustomResult<CustomException>(ex); context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.ContentType = "application/json"; context.Response.Body.Flush(); context.Response.Body.Position = 0; return JsonSerializer.SerializeAsync(context.Response.Body, result, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } ); } } }
自定义异常处理中间件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Text.Json; namespace Demo { /// <summary> /// 自定义异常处理中间件 /// </summary> public class ExceptionMiddleware { /// <summary> /// 请求委托 /// </summary> private readonly RequestDelegate _next; /// <summary> /// 日志 /// </summary> private readonly ILogger _logger; /// <summary> /// 构造函数 /// </summary> /// <param name="next"></param> /// <param name="logger"></param> public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger) { _next = next; _logger = logger; } /// <summary> /// 异步调用 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task Invoke(HttpContext context) { try { // 向下执行(等待返回) await _next(context); } catch (Exception ex) { // 日志 _logger.LogError(ex, ResultMsg.OtherError); // 响应信息 var result = new CustomResult<Exception>(ex); context.Response.ContentType = "application/json; charset=utf-8"; context.Response.StatusCode = StatusCodes.Status500InternalServerError; var stream = context.Response.Body; await JsonSerializer.SerializeAsync(stream, result, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } ); } } } }
使用方法:Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Serilog; using Serilog.Events; using System.Reflection; using System.Text; // 创建应用程序 var builder = WebApplication.CreateBuilder(args); // 创建Serilog Log.Logger = new LoggerConfiguration() .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() .CreateBootstrapLogger(); try { Log.Information("Starting web host"); // 配置文件 var configuration = builder.Configuration; // 配置Serilog builder.Host.UseSerilog((context, services, configuration) => configuration .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services)); // 向容器中添加服务。 // 添加控制器 builder.Services.AddControllers(options => { // 自定义异常过滤器(仅对控制器级别的错误进行处理) options.Filters.Add(typeof(CustomExceptionFilterAttribute)); }) // 响应数据的格式配置 .AddNewtonsoftJson(options => { options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); // 添加JWT身份验证服务 builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) // Jwt验证配置 .AddJwtBearer(options => { // 身份认证参数 options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = configuration["Jwt:Issuer"], ValidAudience = configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:IssuerSigningKey"])) }; // 身份认证事件 options.AddAuthenticationEvents(); }); // 添加模型绑定异常处理 builder.Services.AddModelBindingExceptionHandling(); // 生成应用程序 var app = builder.Build(); // 配置HTTP请求管道。 // 开发者模式 if (app.Environment.IsDevelopment()) { // 使用开发者异常页面 app.UseDeveloperExceptionPage(); } // 使用Swagger app.UseCustomSwagger(configuration); // 使用Serilog记录Request app.UseSerilogRequestLogging(); // 使用WebSocket app.UseWebSockets(); // 使用自定义中间件(异常处理中间件) app.UseMiddleware<ExceptionMiddleware>(); // 使用HTTPS重定向 app.UseHttpsRedirection(); // 使用身份认证 app.UseAuthentication(); // 使用授权 app.UseAuthorization(); // 映射控制器 app.MapControllers(); // 运行应用程序 app.Run(); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); } finally { Log.CloseAndFlush(); }