本文鏈接: https://www.cnblogs.com/hubaijia/p/about-exceptions-3.html
系列文章:
- 關于Exception的幾點思考和在專案中的使用(一)
- 關于Exception的幾點思考和在專案中的使用(二)
- 關于Exception的幾點思考和在專案中的使用(三)
本文目錄
目錄- Web Api 的錯誤回傳
- Exception的捕捉
- 不要吃掉例外
- Checked Exception
- .net vs java
- visual studio 擴展推薦
- 常見模式
- 不要重復的拋出
- 在asp.net core 中
- 在xamarin.forms中
- 結語
Web Api 的錯誤回傳
在使用.net 的 Web Api構建Endpoint對外提供訪問時,往往需要統一的錯誤回傳格式,
如果按照前面兩篇文章(一)、(二)所說,采用帶有ErrorCode的例外ErrorCodeException,那么在WebApi中回傳錯誤時,只要回傳ErrorCode即可,
代碼如下:
[HttpPut]
[Authorize]
public IActionResult Update(UpdatetNickNameRequest request)
{
try
{
long userId = User.GetUserId();
_identityService.SetNickName(userId, request.NickName);
return Ok();
}
catch (IdentityException ex)
{
return new BadRequestObjectResult(ex.ErrorCode);
}
}
ErrorCode類包含了Id, Name, Message屬性,方便客戶端在收到錯誤回傳后進行處理,
自定義的客戶端在接受到Api的回傳后,檢查HttpStatusCode,如果不成功(不是2xx),那么也直接拋出帶有ErrorCode的例外ErrorCodeException即可,
這樣,前后端的處理就變得一致和簡便,特別是如果你在前端也采用 .net 技術,比如 blazor、xamarin、wpf、winform等等,那前后端可以使用相同的CodeBase,
Exception的捕捉
前面幾篇文章,一直在關注Exception的拋出,現在我們來關注一下Exception的捕捉,
那些基礎的catch/finally這里就不再贅述,說幾個專案中實際出現的現象和問題,
不要吃掉例外
不要吃掉例外有兩個層面,一個是直接忽略,比如catch了所有的Exception,并不妥善處理(假處理,就列印一句log,甚至不處理),不過這種失誤要不是新手所犯,要不就是編碼程序中草草了事的結果,
這里主要注意第二個層面,即catch時不要隱藏例外的型別,即不要catch(Exception ex),這意味著隱藏了例外的型別,吃掉了針對不同的錯誤采取不同的措施的機會,
微軟的code analysis也直接給出了規則 CA1031:不要捕捉一般例外型別.
Checked Exception
CE是確保你所有的例外都得到捕捉,它往往在編譯層面提供對Exception的檢查,確保你的程式不會因為未處理的例外而終止,
在撰寫可靠的程式時,程式員需要知道呼叫的方法拋出什么例外,我是否需要處理,還是包裝,還是直接拋出不理,總之我們需要有途徑獲取這樣的資訊,
當然,Checked Exception肯定會帶來更多的代碼量,且在專案初始建立起來,往往需要修改一處,而動整個鏈條上的代碼,一層層的修改,
所以,有些人喜歡CE,認為它帶來了規矩,改善了團隊代碼質量;當然也有些人認為它帶來了繁瑣,
仁者見仁智者見智,這里不加入這些爭辯,爭辯1, 爭辯2, 爭辯3, 爭辯4
.net vs java
不得不拿這兩門語言進行對比,每個世界都要互相借鑒,
熟悉java語法的同學肯定會知道java方法定義上有throws這一關鍵詞:
public int div(int i,int j) throws Exception {...}
throws這一關鍵詞保證了從方法定義上就能知道一個方法拋出什么樣的例外,直接借助編譯器檢查或者IDE的智能提醒,就不會漏掉例外,
而c#中并沒有相同的實作,可以見Stackoverflow上的討論.
那么在 .net 世界中當你呼叫一個方法時,怎么妥善的知道這個方法拋出什么樣的例外呢?知道后我們才能決定是否處理這個例外,還是繼續拋出,
目前的答案是:注釋!(不要笑,很嚴肅的解決方案),
/// <exception cref="IdentityService.IdentityService">這樣寫Exception注釋</exception>
public void SetNickName(long userId, string newNickName)
{
//....
Ensure.NickNameNotExisted(newNickName);
//....
}
當你翻看.net的原始碼時,會看到所有方法的注釋中都良好的列出了有哪些例外,
有幾個問題:
- 我們團隊沒人寫注釋,怎么辦?
- 呼叫方法時,并沒有智能提示有哪些例外,所以我們經常忽略
- 想看例外就得F12看定義,太繁瑣,
有很多問題只從技術上沒法解決,但我們盡量可以借助一些Review工具來檢查團隊的代碼,提出要求,
此外你會喜歡上 ctrl + k, ctrl+i 這個快捷鍵的,他能幫助你快速查看注釋檔案,查看有哪些例外,
visual studio 擴展推薦
在這里,我推薦一個visual studio的擴展,是的,它的名字就叫 Checked Exceptions, 這是我必備的一款擴展,

這款插件會借助注釋的形式,協助實作Checked Exception的功能,并且可以快速添加相應注釋,
這個有個小提示:如果你從專案伊始采用這款插件,折磨小一點,如果半路使用,那么當作檢驗團隊代碼強健性的工具也不錯,
此外,這個款擴展可能還有些bug,導致即使注釋了Exception還不斷提示,所以我平時并不一直啟用它,而是在做Code Review時,使用它作為一個檢查工具,
這樣可以比較好的解決上面提到的Checked Exception的缺點,又利用它的優點,
常見模式
不要重復的拋出
這個只是簡單提醒下,見如下代碼,
void BadSmellMethod()
{
try
{
.....
}
catch(Exception ex)
{
//.... some thing
// 錯誤的做法
throw ex;
}
}
void GoodMethod()
{
try
{
.....
}
catch
{
//... some thing
// 錯誤的做法
throw;
}
}
簡單來說就是重復拋出,丟失了引發例外原始方法和當前方法呼叫之間的StackTrace,
在code analysis中也有相應規則,CA2200.
在asp.net core 中
在捕捉例外時,往往一個例外被一路拋出,或者包裝再拋出,直到終點,如果到了終點還沒有被捕獲,那么就會引發程式中止,這是誰都不想看到的,
在asp.net core中,這個終點就是Controller控制器,所以我們需要在Controller的方法里呼叫需要的Service,然后兜住所有的可能的例外,這也是可以直接捕獲Exception的地方,
代碼如下:
[HttpPut]
[Authorize]
public IActionResult Update(UpdatetNickNameRequest request)
{
try
{
long userId = User.GetUserId();
_identityService.SetNickName(userId, request.NickName);
return Ok();
}
catch (IdentityException ex)
{
// 具體例外
//...others
return new BadRequestObjectResult(ex.ErrorCode);
}
catch (OtherException ex)
{
//具體例外
//... others
return new BadRequestObjectResult(ex.ErrorCode);
}
catch(Exception ex)
{
//這里兜住其他未發現的一切例外
//做好日志
//分析后再優化處理
return new BadRequestObjectResult(ErrorCode.Empty);
}
}
在xamarin.forms中
如果你是同道中人,使用xamarin.forms,那么你肯定知道MVVM模式,
例外的終點往往就在MVVM模式中的ViewModel中,比如LoginPageViewModel中,同樣ViewModel呼叫各項Service,你需要在這里兜住所有可能出現的例外,
結語
本文,簡要介紹了具體專案中例外的捕捉問題,歡迎大家交流指正,
下篇,我們關注一下 異步編程中的Exception,以及全域錯誤處理,
謝謝閱讀,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/274963.html
標籤:.NET技术
