全局异常处理中间件、过滤器
前言
1、使用中间件或者全局异常过滤器可以很大程度上减少你的冗余代码,提升代码的美观以及可维护性。
2、这种做法在行内,也称之为 微服务。
废话不多说,直接上代码
// 异常处理过滤器
using log4net;
using log4net.Config;
using log4net.Repository;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace AspDotNetGo.Common
{
public class ExceptionFilter : IExceptionFilter, IAsyncExceptionFilter
{
private readonly ILog log;
public ExceptionFilter(ILog _log)
{
log = _log;
}
public void OnException(ExceptionContext context)
{
//捕获到程序未处理的异常,通常是用文档记录下来
#region 此代码块中代码可以修改为自己的代码
if (context.HttpContext.Request.IsHttps)
{
//ILoggerRepository repository = LogManager.CreateRepository("NETCoreRepository");
// 默认简单配置,输出至控制台
//BasicConfigurator.Configure(repository);
//ILog log = LogManager.GetLogger(repository.Name, "NETCorelog4net");
log.Info(typeof(ExceptionHandle).ToString() + $"捕获全局异常:{context.Exception.Message},{context.Exception.StackTrace},【错误】{context.Exception.ToString()}");
Console.ReadKey();
context.ExceptionHandled = true;
}
else
{
context.ExceptionHandled = true;
}
#endregion
}
public Task OnExceptionAsync(ExceptionContext context)
{
OnException(context);
return Task.CompletedTask;
}
}
}
// 异常处理中间件
using log4net;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace AspDotNetGo.Common
{
public class ExceptionHandle
{
private readonly ILog log;
private readonly RequestDelegate next;
/// <summary>
///
/// </summary>
/// <param name="next"></param>
public ExceptionHandle(RequestDelegate next, ILog _log)
{
log = _log;
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception e)
{
await HandleExceptionAsync(context, e).ConfigureAwait(false);
}
}
public async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
if (exception == null)
{
return;
}
await WriteExceptionAsync(context, exception).ConfigureAwait(false);
}
private async Task WriteExceptionAsync(HttpContext context, Exception exception)
{
try
{
var baseException = exception.GetBaseException();
if (baseException == null)
{
baseException = exception;
}
Console.WriteLine(typeof(ExceptionHandle).ToString()+ $"捕获全局异常:{baseException.Message},{baseException.StackTrace},【错误】{baseException.ToString()}");
log.Info(typeof(ExceptionHandle).ToString() + $"捕获全局异常:{baseException.Message},{baseException.StackTrace},【错误】{baseException.ToString()}");
var response = context.Response;
response.ContentType = "application/json;charset=utf-8";
string message = "调用API失败";
object data = null;
//#if DEBUG
message = baseException.Message;
data = baseException.StackTrace;
//#endif
//var result = ExcutedResult.FailedResult(message);
await response.WriteAsync(message).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(typeof(ExceptionHandle).ToString()+ "处理全局异常时引发额外的异常:" + ex.Message);
}
}
}
}
// 注入相关组件 我这里有EF以及Freesql的一些东西,不需要的可以自己注释掉
using AspDotNetGo.Common;
using AspDotNetGo.Models;
using log4net;
using log4net.Config;
using log4net.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MyLogin.WebApi.Utility.AutoMapper;
using System.IO;
namespace AspDotNetGo
{
public class Startup
{
public IConfiguration Configuration { get; }
public static ILoggerRepository repository { get; set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<EFCoreContext>();
services.AddMvc(options =>
{
// 注入全局处理异常过滤器(选其一)
options.Filters.Add<ExceptionFilter>();
});
repository = LogManager.CreateRepository("NETCoreRepository");
XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
var log = LogManager.GetLogger(repository.Name, typeof(Startup));
services.AddSingleton(log);
IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, Configuration["ConnectionStrings:MySQL"]) //可以写入json文件
.UseAutoSyncStructure(true) //自动同步实体结构到数据库,FreeSql不会扫描程序集,只有CRUD时才会生成表。
.Build(); //请务必定义成 Singleton 单例模式
services.AddSingleton(fsql);
services.AddControllers();
services.AddSwaggerGen();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 注入全局处理异常中间件
//app.UseMiddleware<ExceptionHandle>();
app.UseRouting();
app.UseSwagger();
app.UseSwaggerUI();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
// 添加log4net.config配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<root>
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="WarnErrorFatalAppender" />
</root>
<!--按文件大小与个数建立日志文件-->
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/" />
<appendToFile value="true" />
<!--日志保存份数,设置无限备份=-1 ,最大备份数为1000-->
<param name="MaxSizeRollBackups" value="-1"/>
<!--单个日志文件大小-->
<param name="MaximumFileSize" value="10MB"/>
<!--日志文件名是否是固定不变的-->
<param name="StaticLogFileName" value="false" />
<!--日志根据日期滚动-->
<param name="RollingStyle" value="Composite" />
<param name="DatePattern" value="yyyy/yyyy-MM/'Info_'yyyy-MM-dd'.log'" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="————————————Begin———————————— 记录时间:%d%n线程ID:%t%n日志等级:%-5p%n日志类型:%c%n%m%n————————————End————————————— " />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG" />
<levelMax value="INFO" />
</filter>
</appender>
<!--按文件大小与个数建立日志文件-->
<appender name="WarnErrorFatalAppender" type="log4net.Appender.RollingFileAppender">
<file value="Logs/" />
<appendToFile value="true" />
<!--日志保存份数,设置无限备份=-1 ,最大备份数为1000-->
<param name="MaxSizeRollBackups" value="-1"/>
<!--单个日志文件大小-->
<param name="MaximumFileSize" value="10MB"/>
<!--日志文件名是否是固定不变的-->
<param name="StaticLogFileName" value="false" />
<!--日志根据日期滚动-->
<param name="RollingStyle" value="Composite" />
<param name="DatePattern" value="yyyy/yyyy-MM/'Error_'yyyy-MM-dd'.log'" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="————————————Begin———————————— 记录时间:%d%n线程ID:%t%n日志等级:%-5p%n日志类型:%c%n%m%n————————————End————————————— " />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN" />
<levelMax value="FATAL" />
</filter>
</appender>
</log4net>
</configuration>
运行结果:
异常截获成功,并记入日志。
如果你觉得本文对你有帮助,请点击“推荐”,谢谢。