异常处理的集中方法:
- 配置HTTP错误代码页
- 使用MVC过滤器
- 异常捕获中间件
一、配置HTTP错误代码页
//在Startup.cs文件的Configure方法中添加如下代码
app.UseStatusCodePagesWithReExecute("/errors/{0}");
//创建Errors控制器返回指定错误页
public class ErrorsController : Controller
{
private IHostingEnvironment _env;
public ErrorsController(IHostingEnvironment env)
{
_env = env;
}
[Route("errors/{statusCode}")]
public IActionResult CustomError(int statusCode)
{
var filePath = $"{_env.WebRootPath}/errors/{(statusCode == 404 ? 404 : 500)}.html";
return new PhysicalFileResult(filePath, new MediaTypeHeaderValue("text/html"));
}
}
二、使用MVC过滤器
public class CustomerExceptionAttribute : ExceptionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
public CustomerExceptionAttribute(
IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public override void OnException(ExceptionContext filterContext)
{
if (!_hostingEnvironment.IsDevelopment())
{
return;
}
HttpRequest request = filterContext.HttpContext.Request;
Exception exception = filterContext.Exception;
//异常是否处理
if (filterContext.ExceptionHandled == true)
{
return;
}
if (exception is UserFriendlyException)
{
//filterContext.Result = new ApplicationErrorResult
filterContext.HttpContext.Response.StatusCode = 400;
filterContext.HttpContext.Response.WriteAsync(exception.Message);
}
//下面进行异常处理的逻辑,可以记录日志、返回前端友好提示等
//...
//设置异常已经处理,否则会被其他异常过滤器覆盖
filterContext.ExceptionHandled = true;
//在派生类中重写时,获取或设置一个值,该值指定是否禁用IIS自定义错误。
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
三、异常捕获中间件(Middleware)
使用MVC自带中间件
//在Startup.cs中添加如下代码
if (env.IsDevelopment())
{ //开发模式
app.UseDeveloperExceptionPage();
}
else
{ //使用默认的异常处理
//app.UseExceptionHandler();
//使用自定义处理
app.UseExceptionHandler(build =>
build.Run(async context =>
{
var ex = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error;
if (ex != null)
{
string innerException = String.Empty;
while (ex.InnerException != null)
{
ex = ex.InnerException;
innerException += ex.InnerException?.Message + "\r\n" + ex.InnerException?.StackTrace + "\r\n";
}
string message = $@"【{ex.Message}】内部错误【{ex.InnerException?.Message}】";
//这里可以进行异常记录和针对异常做不同处理,我这里示例返回500
context.Response.StatusCode = 500;
context.Response.ContentType = "text/plain;charset=utf-8";
await context.Response.WriteAsync("服务器变成蝴蝶飞走了!");
}
else
{
context.Response.StatusCode = 500;
if (context.Request.Headers["X-Requested-With"] != "XMLHttpRequest")
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync($@"{env.WebRootPath}/errors/500.html");
}
}
}
));
}
自定义中间件(可以进行日志记录)
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
//这里也可以进行请求和响应日志的的记录
await _next(context);
}
catch (Exception ex)
{
var statusCode = context.Response.StatusCode;
//进行异常处理
}
finally
{
var statusCode = context.Response.StatusCode;
var msg = String.Empty;
switch (statusCode)
{
case 500:
msg = "服务器系统内部错误";
break;
case 401:
msg = "未登录";
break;
case 403:
msg = "无权限执行此操作";
break;
case 408:
msg = "请求超时";
break;
}
if (!string.IsNullOrWhiteSpace(msg))
{
await HandleExceptionAsync(context, statusCode, msg);
}
}
}
private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg)
{
context.Response.ContentType = "application/json;charset=utf-8";
context.Response.StatusCode = statusCode;
return context.Response.WriteAsync(msg);
}
}