Dotnet Core異常處理的優(yōu)雅實(shí)踐
異常處理,也可以做得很優(yōu)雅。
?
一、前言
異常處理的重要性,老司機(jī)都清楚。
?
這篇文章,我們來理一下Dotnet Core異常處理的幾種方式。
Try Catch方式
Exception Filter方式
內(nèi)建的異常處理中間件
自定義的異常處理中間件
這是目前使用比較多的幾種方式。其中,第1、2種其實(shí)算是一種,是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則進(jìn)入catch塊,為false則跳過。
?
在C#中,異常是從內(nèi)向外逐層查找處理程序的,隨著查找層數(shù)的增加,性能會逐漸降低。
概念上,try塊的運(yùn)行效率和不加try塊的性能差不多,可以認(rèn)為基本一致;但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的方法。我們可以在這個方法中進(jìn)行異常的記錄處理,然后返回false。
為什么要返回false呢?是因?yàn)槲覀円涗洰惓#珵榱诵阅艿目紤],不希望代碼進(jìn)入到catch塊。返回false后,程序執(zhí)行了log方法,卻又沒進(jìn)入到catch塊。
當(dāng)然,如果你想進(jìn)入到catch塊,那返回true就可以了。
?
嗯。這一段能看明白就行了,這不算是一個常規(guī)的解決辦法,只能算一個旁門的小技巧。
三、內(nèi)建的異常中間件
內(nèi)建的異常處理,有兩個。
一個是我們最常見的,在創(chuàng)建webapi類型的工程時,Startup.cs文件中已經(jīng)默認(rèn)生成好的:
public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
{
????if?(env.IsDevelopment())
????{
????????app.UseDeveloperExceptionPage();
????}
}
這個app.UseDeveloperExceptionPage就是框架中用于顯示異常的詳細(xì)信息的中間件。
?
另一個,是app.UseExceptionHandler,也是一個內(nèi)置的用來處理異常的中間件,可以這樣用:
public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
{
????app.UseExceptionHandler(para...);
}
參數(shù)可以是一個Endpoint,也可以是一個Action。實(shí)際應(yīng)用時,還可以寫成一個IApplicationBuilder的擴(kuò)展。
四、自定義的中間件
如果對異常管理有很高的要求,自定義異常處理中間件,是最合適的一種方式。
中間件的寫法,我在前文一文說通Dotnet Core的中間件中有詳細(xì)的說明,這兒不再多說。
給一段例子看看:
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
喜歡就來個三連,讓更多人因你而受益
