重新整理 .net core 实践篇—————异常中间件[二十]
2023-09-14 08:59:24 时间
前言
简单介绍一下异常中间件的使用。
正文
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
这样写入中间件哈,那么在env环境下就会去执行UseDeveloperExceptionPage。
public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<DeveloperExceptionPageMiddleware>();
}
那么我们应该去看DeveloperExceptionPageMiddleware中间件哈。
那么这里介绍它是如何能够捕获其他中间件的异常的哈。
里面的invoke:
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
// 异常处理
}
}
其实它的操作是很简单的,直接在外面套了try catch。
里面的异常处理怎么处理的可以直接去看DeveloperExceptionPageMiddleware 中间件,里面的操作也比较简单处理。
测试:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new System.Exception("错误");
return 1;
}
结果:
因为上面说了,这个是dev环境下,那么生产环境不能直接给用户看到错误信息。
正式环境:
app.UseExceptionHandler("/error");
将错误转移到/error 处理。具体UseExceptionHandler细节篇里面介绍,有许多可以借鉴的地方。
[ApiController]
[Route("[controller]")]
public class ErrorController : Controller
{
public ILogger<ErrorController> _logger;
public ErrorController(ILogger<ErrorController> logger)
{
this._logger = logger;
}
public IActionResult Index()
{
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
var ex = exceptionHandlerPathFeature?.Error;
var knownException = ex as IKnownException;
if (knownException == null)
{
_logger.LogError(ex, ex.Message);
knownException = KnownException.Unknow;
}
else
{
knownException = KnownException.FromKnowException(knowException);
}
return View(knownException);
}
}
视图:
<html>
<head>
</head>
<body>
<div>
错误码: @Model.ErrorCode
</div>
<div>
错误信息: @Model.Message
</div>
</body>
</html>
IKnownException:
public interface IKnownException
{
public string Message { get; }
public int ErrorCode { get; }
public object[] ErrorData { get; }
}
KnownException:
public class KnownException : IKnownException
{
public string Message
{
get; private set;
}
public int ErrorCode
{
get; private set;
}
public object[] ErrorData
{
get;
private set;
}
public readonly static IKnownException Unknow = new KnownException { Message = "未知错误", ErrorCode = 99 };
public static IKnownException FromKnowException(IKnownException Exception)
{
return new KnownException{Message = Exception.Message, ErrorCode = Exception.ErrorCode, ErrorData = Exception.ErrorData};
}
}
测试1:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new System.Exception("错误");
return 1;
}
这种属于未知异常,结果:
现在弄一个支付异常:
public class PayErrorException : Exception, IKnownException
{
public PayErrorException(string message, int errorCode, params object[] errorData): base(message)
{
this.ErrorCode = errorCode;
this.ErrorData = errorData;
}
public int ErrorCode { get;private set; }
public object[] ErrorData { get;private set; }
}
测试2:
[HttpGet]
public int GetService([FromServices]ISelfService selfService)
{
throw new PayErrorException("支付错误",405,null);
return 1;
}
将异常处理放入到中间件分支中。
app.UseExceptionHandler(errApp =>
{
errApp.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
IKnownException knownException = exceptionHandlerPathFeature.Error as IKnownException;
if (knownException == null)
{
var logger = context.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(exceptionHandlerPathFeature.Error, exceptionHandlerPathFeature.Error.Message);
knownException = KnownException.Unknown;
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.Response.StatusCode = StatusCodes.Status200OK;
}
var jsonOptions = context.RequestServices.GetService<IOptions<JsonOptions>>();
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(knownException, jsonOptions.Value.JsonSerializerOptions));
});
});
效果一样就不演示了。如果是已知异常错误码应该为200,一个是500异常是系统无法处理,系统错误,但是已知错误是属于系统正常处理。另一个是监控系统,认为报500错误,是会持续放出系统警告。
还有一种局部异常,只在mvc中生效,而不是全局生效:
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
在mvc 中注册:
services.AddMvc(mvcOptions =>
{
mvcOptions.Filters.Add<MyExceptionFilter>();
}).AddJsonOptions(jsonOptions =>
{
jsonOptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
});
最后介绍一种,只作用于某个控制器,或者action:
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
查看一下ExceptionFilterAttribute头部:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ExceptionFilterAttribute : Attribute, IAsyncExceptionFilter, IExceptionFilter, IOrderedFilter
上面标志了可以放于类上也可以放于方法上。所以可以放至在controller上,也可以action上,看需求了。
结
以上只是个人整理,如有错误,望请指点。
下一节,静态文件,以前写过,重新整理一下。
相关文章
- 《ASP.NET Core 6框架揭秘》勘误
- .NET 扩展官方 Logger 实现将日志保存到本地文件
- 【愚公系列】2022年12月 .NET CORE工具案例-IHostedService实现任务调度
- .NET MAUI Preview5 状态预览(6月)
- [.net]若干小知识
- 【愚公系列】2022年12月 .NET CORE工具案例-Sundial实现任务调度
- .NET Github Actions 测试覆盖率
- 【愚公系列】2023年01月 .NET CORE工具案例-基于MediatR的CQRS模式
- 【愚公系列】2023年01月 .NET CORE工具案例-基于SqlSugar的多库多表融合查询
- 【愚公系列】2023年02月 .NET CORE工具案例-Workflow-Core轻量级工作流引擎(简单使用)
- 《ASP.NET Core 6框架揭秘》实例演示:自定义一个服务器
- .NET Core 全新认识详解编程语言
- 发布ASP.NET Core程序到Linux生产环境详解编程语言
- Oracle 等待事件 SQL*Net more data from dblink 官方解释,作用,如何使用及优化方法
- p.netMSSQL数据库搭配ASP.NET实现强大的网站功能(mssqlas)
- 原理揭秘.NET 运用Redis图灵缓存原理(.net缓存redis)
- NET环境下操作Oracle数据库的技巧(.net操作oracle)
- NET与Oracle携手走向未来(.net 对oracle)
- CMySQL与NET技术融会贯通(c mysql .net)
- NET操作MySQL数据库快速入门(.net读写mysql)
- Net框架如何使用MySQL数据库(.net能用mysql吗)
- Net和MySQL互相受益的强大组合(.net 与mysql)
- 探索Oracle中NET位置的背后秘密(oracle中net位置)
- Redis无法安装NET组件(redis装不上net)
- asp.net(c#)两种随机数的算法,可用抽考题
- VS初始化命令ASP.NET常用技巧
- ASP.NET笔记之Session、http、web开发原则、xss漏洞的详细介绍