前言

简单介绍一下异常中间件的使用。

正文

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上,看需求了。

以上只是个人整理,如有错误,望请指点。

下一节,静态文件,以前写过,重新整理一下。

重新整理 .net core 实践篇—————异常中间件[二十]的更多相关文章

  1. 重新整理 .net core 实践篇—————静态中间件[二十一]

    前言 简单整理一下静态中间件. 正文 我们使用静态文件调用: app.UseStaticFiles(); 那么这个默认会将我们根目录下的wwwroot作为静态目录. 这个就比较值得注意的,可能刚开始学 ...

  2. 重新整理 .net core 实践篇—————Mediator实践[二十八]

    前言 简单整理一下Mediator. 正文 Mediator 名字是中介者的意思. 那么它和中介者模式有什么关系呢?前面整理设计模式的时候,并没有去介绍具体的中介者模式的代码实现. 如下: https ...

  3. 重新整理 .net core 实践篇——— UseEndpoints中间件[四十八]

    前言 前文已经提及到了endponint 是怎么匹配到的,也就是说在UseRouting 之后的中间件都能获取到endpoint了,如果能够匹配到的话,那么UseEndpoints又做了什么呢?它是如 ...

  4. 重新整理 .net core 实践篇—————领域事件[二十九]

    前文 前面整理了仓储层,工作单元模式,同时简单介绍了一下mediator. 那么就mediator在看下领域事件启到了什么作用吧. 正文 这里先注册一下MediatR服务: // 注册中间者:Medi ...

  5. 重新整理 .net core 实践篇——— 权限中间件源码阅读[四十六]

    前言 前面介绍了认证中间件,下面看一下授权中间件. 正文 app.UseAuthorization(); 授权中间件是这个,前面我们提及到认证中间件并不会让整个中间件停止. 认证中间件就两个作用,我们 ...

  6. 重新整理 .net core 实践篇————缓存相关[四十二]

    前言 简单整理一下缓存. 正文 缓存是什么? 缓存是计算结果的"临时"存储和重复使用 缓存本质是用空间换取时间 缓存的场景: 计算结果,如:反射对象缓存 请求结果,如:DNS 缓存 ...

  7. 重新整理 .net core 实践篇————cookie 安全问题[三十八]

    前言 简单整理一下cookie的跨站攻击,这个其实现在不常见,因为很多公司都明确声明不再用cookie存储重要信息,不过对于老站点还是有的. 正文 攻击原理: 这种攻击要达到3个条件: 用户访问了我们 ...

  8. 重新整理 .net core 实践篇————重定向攻击[三十九]

    前言 简单介绍一下重定向攻击. 正文 攻击思路: 看着上面挺复杂的,其实是一些很简单的步骤. 攻击者通过某些手段,让用户打开了一个好站点,打开的这个地址里面带有重定向信息,重定向信息就是自己伪造的站点 ...

  9. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

随机推荐

  1. Windows XP sp3 系统安装 Windbg 符号文件 Symbols 时微软失去支持的解决方案

    0x01 前言 Windbg 是微软的正宗调试器,在正常的情况下调试一些程序并没有什么问题,但是如果需要调试分析程序的堆栈,或者是一些特殊的功能时则需要微软的符号文件的支持,所以符号文件是非常重要的, ...

  2. 【vue-04】模块化开发

    组件化开发,一般是将重复的代码抽取成一个组件,供其他地方复用,一般情况下,提到组件化开发,都是指前端的组件化开发. 模块化开发,一般是将同一类功能模块的代码放到一起统一进行管理,是基于代码层面的,一般 ...

  3. 【小技巧】启动Tomcat 提示端口被占用 怎么办?一句命令解决

    windows环境: 方法1: 1.win+r 打开黑界面 2.输入命令 netstat -ano|findstr 8080 3.输入命令 taskkill /pid xxxx /f Linux环境: ...

  4. 一句 Task.Result 就死锁, 这代码还怎么写?

    一:背景 1. 讲故事 前些天把 .NET 高级调试 方面的文章索引到 github 的过程中,发现了一个有意思的评论,详见 文章,截图如下: 大概就是说在 Winform 的主线程下执行 Task. ...

  5. C#常见的文件路径Api

    我们经常有遇到要处理文件路径的需求,那么一般我们常见的有几种: 程序下面的文件 临时目录下的文件 获取程序下面的文件 首先我们创建了实例解决方案: 其中调用链是:Main.Shell->FooA ...

  6. Django(21)migrate报错的解决方案

    前言 在讲解如何解决migrate报错原因前,我们先要了解migrate做了什么事情,migrate:将新生成的迁移脚本.映射到数据库中.创建新的表或者修改表的结构. 问题1:migrate怎么判断哪 ...

  7. 【转载】windows linux cent 7 制作U盘 启动盘

    1 镜像iso文件存放在linux环境下用dd if=/dev/sdb of=/镜像存放路径/镜像iso文件 bs=1M u盘的盘符是/dev/sdb 2 镜像iso文件存放在windows环境下ul ...

  8. shell判断一个变量是否为空方法总结

    shell中如何判断一个变量是否为空 shell编程中,对参数的错误检查项中,包含了变量是否赋值(即一个变量是否为空),判断变量为空方法如下: 1.变量通过" "引号引起来 1 2 ...

  9. 自动做bond的脚本

    #!/bin/bash # 先备份原来的网卡配置文件 find /etc/sysconfig/network-scripts/ -type f -name "ifcfg*"|xar ...

  10. Hive 安装和配置

    环境准备 已安装 HDFS 和 Yarn 集群环境 Windows 已安装 MySQL 8 (开启远程连接用户权限) 安装步骤 1. 上传并解压 Hive 安装文件 将 apache-hive-3.1 ...