Dotnet Core异常处理的优雅实践
共 3570字,需浏览 8分钟
·
2020-10-02 18:10
异常处理,也可以做得很优雅。
一、前言
异常处理的重要性,老司机都清楚。
这篇文章,我们来理一下Dotnet Core异常处理的几种方式。
Try Catch方式
Exception Filter方式
内建的异常处理中间件
自定义的异常处理中间件
这是目前使用比较多的几种方式。其中,第1、2种其实算是一种,是C#两个语言版本的东西。
二、Try Catch方式
这是最通常使用的一种方式。
看例子:
[HttpGet]
public IActionResult Get()
{
try
{
List<string> example_list = null;
var item_count = example_list.Count();
return Ok(item_count);
}
catch (Exception ex)
{
return StatusCode(HttpContext.Response.StatusCode, ex.Message);
}
}
有时候,我们可能需要处理多种异常。
catch (Exception ex)
{
if(ex.InnerException == null)
return StatusCode(HttpContext.Response.StatusCode, "error");
else
return StatusCode(HttpContext.Response.StatusCode, "other error");
}
在C# 6以后,Try Catch加了一个过滤Exception Filter
语法,可以在catch
后跟一个条件语句。上面这个catch
可以写为:
catch (Exception ex) when (ex.InnerException == null)
{
return StatusCode(HttpContext.Response.StatusCode, "error");
}
catch (Exception ex)
{
return StatusCode(HttpContext.Response.StatusCode, "other error");
}
在这个语法中,when
后面是一个bool
的判断,为true
则进入catch
块,为false
则跳过。
在C#中,异常是从内向外逐层查找处理程序的,随着查找层数的增加,性能会逐渐降低。
概念上,try
块的运行效率和不加try
块的性能差不多,可以认为基本一致;但catch
块的性能会差很多。所以一般来说,一个基本的原则是,不要把try
、catch
作为程序的逻辑。
但是,如果我们需要又需要记录这个异常,该怎么办?
这时候,就可以利用Exception Filter
语法。
看代码:
[HttpGet]
public IActionResult Get()
{
try
{
List<string> example_list = null;
var item_count = example_list.Count();
return Ok(item_count);
}
catch (Exception ex) when (log(ex))
{
}
return StatusCode(HttpContext.Response.StatusCode, "error");
}
private bool log(Exception ex)
{
Debug.Print(ex.Message);
return false;
}
在这个代码中,when
条件后跟了一个返回bool
的方法。我们可以在这个方法中进行异常的记录处理,然后返回false
。
为什么要返回false
呢?是因为我们要记录异常,但为了性能的考虑,不希望代码进入到catch
块。返回false
后,程序执行了log
方法,却又没进入到catch
块。
当然,如果你想进入到catch
块,那返回true
就可以了。
嗯。这一段能看明白就行了,这不算是一个常规的解决办法,只能算一个旁门的小技巧。
三、内建的异常中间件
内建的异常处理,有两个。
一个是我们最常见的,在创建webapi
类型的工程时,Startup.cs
文件中已经默认生成好的:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
这个app.UseDeveloperExceptionPage
就是框架中用于显示异常的详细信息的中间件。
另一个,是app.UseExceptionHandler
,也是一个内置的用来处理异常的中间件,可以这样用:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseExceptionHandler(para...);
}
参数可以是一个Endpoint
,也可以是一个Action
。实际应用时,还可以写成一个IApplicationBuilder
的扩展。
四、自定义的中间件
如果对异常管理有很高的要求,自定义异常处理中间件,是最合适的一种方式。
中间件的写法,我在前文一文说通Dotnet Core的中间件中有详细的说明,这儿不再多说。
给一段例子看看:
public class CustomExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public CustomExceptionMiddleware(RequestDelegate next, ILogger logger)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
_logger.LogError($"Exception occur: {ex}");
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new CustomError()
{
StatusCode = context.Response.StatusCode,
Message = "custom middleware error."
}.ToString());
}
}
使用时,就在Configure
里引入即可。
(全文完)
本文的代码已上传到Github,位置在:https://github.com/humornif/Demo-Code/tree/master/0023/demo/demo
喜欢就来个三连,让更多人因你而受益