重慶安菲科技軟件開發公司
18696588163 18696588163
軟件開發 APP開發 微信/小程序開發 大(dà)型電商平台開發 數據挖掘
18696588163 18696588163
軟件開發 APP開發 微信/小程序開發 大(dà)型電商平台開發 數據挖掘

軟件開發公司 > 動态 > 行業資訊

Spring Boot統一異常處理(lǐ)最佳實踐-軟件開發 APP開發 軟件開發公司 APP開發公司

行業資訊 - 2019 - 04 - 19 Spring 軟件開發 異常處理(lǐ)


前言

在 Web 開發中, 我們經常會需要處理(lǐ)各種異常, 這(zhè)是一件棘手的(de)事情, 對(duì)于很多(duō)人(rén)來(lái)說, 可(kě)能對(duì)異常處理(lǐ)有以下(xià)幾個(gè)問題:

  • 什(shén)麽時(shí)候需要捕獲(try-catch)異常, 什(shén)麽時(shí)候需要抛出(throws)異常到上層.

  • 在 dao 層捕獲還(hái)是在 service 捕獲, 還(hái)是在 controller 層捕獲.

  • 抛出異常後要怎麽處理(lǐ). 怎麽返回給頁面錯誤信息.

異常處理(lǐ)反例

既然談到異常, 我們先來(lái)說一下(xià)異常處理(lǐ)的(de)反例, 也(yě)是很多(duō)人(rén)容易犯的(de)錯誤, 這(zhè)裏我們同時(shí)講到前端處理(lǐ)和(hé)後端處理(lǐ) :

捕獲異常後隻輸出到控制台

前端代碼

$.ajax({
type: "GET",
url: "/user/add",
dataType: "json",
success: function(data){
alert("添加成功");
}
});

後端代碼

try {
// do something
} catch (Exception e) {
e.printStackTrace();
}

這(zhè)是見過最多(duō)的(de)異常處理(lǐ)方式了(le), 如果這(zhè)是一個(gè)添加商品的(de)方法, 前台通(tōng)過 ajax 發送請求到後端, 期望返回 json 信息表示添加結果. 但如果這(zhè)段代碼出現了(le)異常:

  • 那麽用(yòng)戶看到的(de)場(chǎng)景就是點擊了(le)添加按鈕, 但沒有任何反應(其實是返回了(le) 500 錯誤頁面, 但這(zhè)裏前端沒有監聽(tīng) error 事件, 隻監聽(tīng)了(le) success 事件. 但即使加上了(le)error: function(data) {alert("添加失敗");}) 又如何呢(ne)? 到底因爲啥失敗了(le)呢(ne), 用(yòng)戶也(yě)不得(de)而知.

  • 後台 e.printStackTrace() 打印在控制台的(de)日志也(yě)會在漫漫的(de)日志中被埋沒, 很可(kě)能會看不到輸出的(de)異常. 但這(zhè)并不是最糟的(de)情況, 更糟糕的(de)事情是連 e.printStackTrace() 都沒有, catch 塊中是空的(de), 這(zhè)樣後端的(de)控制台中更是什(shén)麽都看不到了(le), 這(zhè)段代碼會像一個(gè)隐形的(de)炸彈一樣一直埋伏在系統中.

混亂的(de)返回方式

前端代碼

$.ajax({
type: "GET",
url: "/goods/add",
dataType: "json",
success: function(data) {
if (data.flag) {
alert("添加成功");
} else {
alert(data.message);
}
},
error: function(data){
alert("添加失敗");
}
});

後端代碼

@RequestMapping("/goods/add")
@ResponseBody
public Map add(Goods goods) {
Map map = new HashMap();
try {
// do something
map.put(flag, true);
} catch (Exception e) {
e.printStackTrace();
map.put("flag", false);
map.put("message", e.getMessage());
}
reutrn map;
}

這(zhè)種方式捕獲異常後, 返回了(le)錯誤信息, 且前台做(zuò)了(le)一定的(de)處理(lǐ), 看起來(lái)很完善? 但用(yòng) HashMap 中的(de) flag 和(hé) message 這(zhè)種字符串來(lái)當鍵很容易處理(lǐ), 例如你這(zhè)裏叫 message, 别人(rén)起名叫 msg, 甚至有時(shí)手抖打錯了(le), 怎麽辦? 前台再改成 msg 或其他(tā)的(de)字符?, 前端後端這(zhè)樣一直來(lái)回改?

更有甚者在情況 A 的(de)情況下(xià), 返回 json, 在情況 B 的(de)情況下(xià), 重定向到某個(gè)頁面, 這(zhè)就更亂了(le). 對(duì)于這(zhè)種不統一的(de)結構處理(lǐ)起來(lái)非常麻煩.

異常處理(lǐ)規範

既然要進行統一異常處理(lǐ), 那麽肯定要有一個(gè)規範, 不能亂來(lái). 這(zhè)個(gè)規範包含前端和(hé)後端.

不要捕獲任何異常

對(duì)的(de), 不要在業務代碼中進行捕獲異常, 即 dao、service、controller 層的(de)所以異常都全部抛出到上層. 這(zhè)樣不會導緻業務代碼中的(de)一堆 try-catch 會混亂業務代碼.

統一返回結果集

不要使用(yòng) Map 來(lái)返回結果, Map 不易控制且容易犯錯, 應該定義一個(gè) Java 實體類. 來(lái)表示統一結果來(lái)返回, 如定義實體類:

public class ResultBean

  • 正常情況: 調用(yòng) ResultBean.success() 或 ResultBean.success(Collection

@RequestMapping("/goods/add")
@ResponseBody
public ResultBean

一般隻有查詢方法需要調用(yòng) ResultBean.success(Collection

前台接受到的(de)信息爲:

{
"code": 0,
"message": "success",
"data": [
{
"name": "商品1",
"price": 50.00,
},
{
"name": "商品2",
"price": 99.99,
}
]
}

  • 抛出異常: 抛出異常後, 我們應該調用(yòng) ResultBean.error(int code, String message), 來(lái)将狀态碼和(hé)錯誤信息返回, 我們約定 code 爲 0 表示操作成功, 1 或 2 等正數表示用(yòng)戶輸入錯誤, -1, -2 等負數表示系統錯誤.

前台接受到的(de)信息爲:

{
"code": -1,
"message": "XXX 參數有問題, 請重新填寫",
"data": null
}

前端統一處理(lǐ):

返回的(de)結果集規範後, 前端就很好處理(lǐ)了(le):

/**
* 顯示錯誤信息
* @param result: 錯誤信息
*/
function showError(s) {
alert(s);
}
/**
* 處理(lǐ) ajax 請求結果
* @param result: ajax 返回的(de)結果
* @param fn: 成功的(de)處理(lǐ)函數 ( 傳入data: fn(result.data) )
*/
function handlerResult(result, fn) {
// 成功執行操作,失敗提示原因
if (result.code == 0) {
fn(result.data);
}
// 用(yòng)戶操作異常, 這(zhè)裏可(kě)以對(duì) 1 或 2 等錯誤碼進行單獨處理(lǐ), 也(yě)可(kě)以 result.code > 0 來(lái)粗粒度的(de)處理(lǐ), 根據業務而定.
else if (result.code == 1) {
showError(result.message);
}
// 系統異常, 這(zhè)裏可(kě)以對(duì) -1 或 -2 等錯誤碼進行單獨處理(lǐ), 也(yě)可(kě)以 result.code > 0 來(lái)粗粒度的(de)處理(lǐ), 根據業務而定.
else if (result.code == -1) {
showError(result.message);
}
// 如果進行細粒度的(de)狀态碼判斷, 那麽就應該重點注意這(zhè)裏沒出現過的(de)狀态碼. 這(zhè)個(gè)判斷僅建議(yì)在開發階段保留用(yòng)來(lái)發現未定義的(de)狀态碼.
else {
showError("出現未定義的(de)狀态碼:" + result.code);
}
}
/**
* 根據 id 删除商品
*/
function deleteGoods(id) {
$.ajax({
type: "DELETE",
url: "/goods/delete",
dataType: "json",
success: function(result){
handlerResult(result, deleteDone);
}
});
}
function deleteDone(data) {
alert("删除成功");
}

showError 和(hé) handlerResult 是公共方法, 分(fēn)别用(yòng)來(lái)顯示錯誤和(hé)統一處理(lǐ)結果集.

然後将主要精力放在發送請求和(hé)處理(lǐ)正确結果的(de)方法上即可(kě), 如這(zhè)裏的(de) deleteDone 函數, 用(yòng)來(lái)處理(lǐ)操作成功給用(yòng)戶的(de)提示信息, 正所謂各司其職, 前端負責操作成功的(de)消息提示更合理(lǐ), 而錯誤信息隻有後台知道, 所以需要後台來(lái)返回.

後端統一處理(lǐ)異常

說了(le)這(zhè)麽多(duō), 還(hái)沒講到後端不在業務層捕獲任何異常的(de)事, 既然所有業務層都沒有捕獲異常, 那麽所有的(de)異常都會抛出到 Controller 層, 我們隻需要用(yòng) AOP 對(duì) Controller 層的(de)所有方法處理(lǐ)即可(kě).

好在 Spring 爲我們提供了(le)一個(gè)注解, 用(yòng)來(lái)統一處理(lǐ)異常:

@ControllerAdvice
@ResponseBody
public class WebExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(WebExceptionHandler.class);
@ExceptionHandler
public ResultBean unknownAccount(UnknownAccountException e) {
log.error("賬号不存在", e);
return ResultBean.error(1, "賬号不存在");
}
@ExceptionHandler
public ResultBean incorrectCredentials(IncorrectCredentialsException e) {
log.error("密碼錯誤", e);
return ResultBean.error(-2, "密碼錯誤");
}
@ExceptionHandler
public ResultBean unknownException(Exception e) {
log.error("發生了(le)未知異常", e);
// 發送郵件通(tōng)知技術人(rén)員(yuán).
return ResultBean.error(-99, "系統出現錯誤, 請聯系網站管理(lǐ)員(yuán)!");
}
}

在這(zhè)裏統一配置需要處理(lǐ)的(de)異常, 同樣, 對(duì)于未知的(de)異常, 一定要及時(shí)發現, 并進行處理(lǐ). 推薦出現未知異常後發送郵件, 提示技術人(rén)員(yuán).

總結

總結一下(xià)統一異常處理(lǐ)的(de)方法:

  • 不使用(yòng)随意返回各種數據類型, 要統一返回值規範.

  • 不在業務代碼中捕獲任何異常, 全部交由 @ControllerAdvice 來(lái)處理(lǐ).

下(xià)一章(zhāng):重慶軟件開發公司推薦關于後端架構師技術圖譜!安菲科技
软件开发
關于安菲科技

安菲科技遵循嚴格的(de)質量和(hé)安全标準, 實施嚴密的(de)安全措施, 擁有成熟可(kě)靠的(de)管理(lǐ)和(hé)開發流程, 公司憑借多(duō)年的(de)行業積累、深厚的(de) 行業專長(cháng)和(hé)成熟的(de)行業實踐,爲客戶持續創造關鍵價值。我們始終關 注前沿技術,保持國際領先的(de)眼界和(hé)技術儲備。公司自 成立以來(lái), 在團隊成員(yuán)的(de)共同努力下(xià),已經成功服務于上百家企業,其中包括 我愛(ài)我家、聯東集團、優财CMA、5100、奔馳、華爲、伊利、寶馬、 迪思公關、航天國旅、HOTWIND、重慶電通(tōng)等衆多(duō)知名企業。

咨詢熱(rè)線:18696588163

推薦閱讀

軟件外包公司、應标軟件外包或軟件定制類項目怎麽做(zuò)-重慶安菲科技 Tag: app開發 小程序開發 軟件開發 重慶軟件開發-軟件産品開發管理(lǐ)的(de)四個(gè)方面-重慶安菲科技 Tag: app開發 小程序開發 軟件開發 軟件開發公司|選擇軟件開發外包公司的(de)關鍵要素-需求 Tag: app開發 小程序開發 軟件開發 軟件開發公司-開發教育軟件平台需要做(zuò)哪些?-軟件定制 Tag: app開發 小程序開發 軟件開發 預約挂号APP開發提供哪些便捷-重慶軟件開發 Tag: app開發 小程序開發 軟件開發 軟件開發需要多(duō)少錢,開發一款APP軟件要多(duō)少錢? Tag: app開發 小程序開發 軟件開發 如何制作app-定制開發必須考慮的(de)四個(gè)問題-軟件開發公司 Tag: app開發 小程序開發 軟件開發 早教app怎麽解決兒(ér)童啓蒙早教問題-重慶軟件開發 Tag: app開發 小程序開發 軟件開發 軟件開發公司有哪些、軟件開發前期需要做(zuò)哪些準備-重慶軟件開發 Tag: app開發 小程序開發 軟件開發

提交需求,獲取工期與報價

立即咨詢