亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

詳解Spring MVC/Boot 統一異常處理最佳實踐

2024-07-14 08:43:25
字體:
來源:轉載
供稿:網友

前言

在 Web 開發中, 我們經常會需要處理各種異常, 這是一件棘手的事情, 對于很多人來說, 可能對異常處理有以下幾個問題:

  • 什么時候需要捕獲(try-catch)異常, 什么時候需要拋出(throws)異常到上層.
  • 在 dao 層捕獲還是在 service 捕獲, 還是在 controller 層捕獲.
  • 拋出異常后要怎么處理. 怎么返回給頁面錯誤信息.

異常處理反例

既然談到異常, 我們先來說一下異常處理的反例, 也是很多人容易犯的錯誤, 這里我們同時講到前端處理和后端處理 :

捕獲異常后只輸出到控制臺

前端代碼

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

后端代碼

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

這是見過最多的異常處理方式了, 如果這是一個添加商品的方法, 前臺通過 ajax 發送請求到后端, 期望返回 json 信息表示添加結果. 但如果這段代碼出現了異常:

  • 那么用戶看到的場景就是點擊了添加按鈕, 但沒有任何反應(其實是返回了 500 錯誤頁面, 但這里前端沒有監聽 error 事件, 只監聽了 success 事件. 但即使加上了error: function(data) {alert("添加失敗");}) 又如何呢? 到底因為啥失敗了呢, 用戶也不得而知.
  • 后臺 e.printStackTrace() 打印在控制臺的日志也會在漫漫的日志中被埋沒, 很可能會看不到輸出的異常. 但這并不是最糟的情況, 更糟糕的事情是連 e.printStackTrace() 都沒有, catch 塊中是空的, 這樣后端的控制臺中更是什么都看不到了, 這段代碼會像一個隱形的炸彈一樣一直埋伏在系統中.

混亂的返回方式

前端代碼

$.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")@ResponseBodypublic 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;}

這種方式捕獲異常后, 返回了錯誤信息, 且前臺做了一定的處理, 看起來很完善? 但用 HashMap 中的 flag 和 message 這種字符串來當鍵很容易處理, 例如你這里叫 message, 別人起名叫 msg, 甚至有時手抖打錯了, 怎么辦? 前臺再改成 msg 或其他的字符?, 前端后端這樣一直來回改?

更有甚者在情況 A 的情況下, 返回 json, 在情況 B 的情況下, 重定向到某個頁面, 這就更亂了. 對于這種不統一的結構處理起來非常麻煩.

異常處理規范

既然要進行統一異常處理, 那么肯定要有一個規范, 不能亂來. 這個規范包含前端和后端.

不要捕獲任何異常

對的, 不要在業務代碼中進行捕獲異常, 即 dao、service、controller 層的所以異常都全部拋出到上層. 這樣不會導致業務代碼中的一堆 try-catch 會混亂業務代碼.

統一返回結果集

不要使用 Map 來返回結果, Map 不易控制且容易犯錯, 應該定義一個 Java 實體類. 來表示統一結果來返回, 如定義實體類:

public class ResultBean<T> {  private int code;  private String message;  private Collection<T> data;  private ResultBean() {  }  public static ResultBean error(int code, String message) {    ResultBean resultBean = new ResultBean();    resultBean.setCode(code);    resultBean.setMessage(message);    return resultBean;  }  public static ResultBean success() {    ResultBean resultBean = new ResultBean();    resultBean.setCode(0);    resultBean.setMessage("success");    return resultBean;  }  public static <V> ResultBean<V> success(Collection<V> data) {    ResultBean resultBean = new ResultBean();    resultBean.setCode(0);    resultBean.setMessage("success");    resultBean.setData(data);    return resultBean;  }  // getter / setter 略}

正常情況: 調用 ResultBean.success() 或 ResultBean.success(Collection<V> data), 不需要返回數據, 即調用前者, 需要返回數據, 調用后者. 如:

@RequestMapping("/goods/add")@ResponseBodypublic ResultBean<Goods> getAllGoods() {  List<Goods> goods = goodsService.findAll();  return ResultBean.success(goods);}
@RequestMapping("/goods/update")@ResponseBodypublic ResultBean updateGoods(Goods goods) {  goodsService.update(goods);  return ResultBean.success();}

一般只有查詢方法需要調用 ResultBean.success(Collection<V> data) 來返回 N 條數據, 其他諸如刪除, 修改等方法都應該調用 ResultBean.success(), 即在業務代碼中只處理正確的功能, 不對異常做任何判斷. 也不需要對 update 或 delete 的更新條數做判斷(個人建議, 實際需要根據業務). 只要沒有拋出異常, 我們就認為用戶操作成功了. 且操作成功的提示信息在前端處理, 不要后臺返回 “操作成功” 等字段.

前臺接受到的信息為:

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

拋出異常: 拋出異常后, 我們應該調用 ResultBean.error(int code, String message), 來將狀態碼和錯誤信息返回, 我們約定 code 為 0 表示操作成功, 1 或 2 等正數表示用戶輸入錯誤, -1, -2 等負數表示系統錯誤.
前臺接受到的信息為:

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

前端統一處理:

返回的結果集規范后, 前端就很好處理了:

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

showError  handlerResult 是公共方法, 分別用來顯示錯誤和統一處理結果集.

然后將主要精力放在發送請求和處理正確結果的方法上即可, 如這里的 deleteDone 函數, 用來處理操作成功給用戶的提示信息, 正所謂各司其職, 前端負責操作成功的消息提示更合理, 而錯誤信息只有后臺知道, 所以需要后臺來返回.

后端統一處理異常

說了這么多, 還沒講到后端不在業務層捕獲任何異常的事, 既然所有業務層都沒有捕獲異常, 那么所有的異常都會拋出到 Controller 層, 我們只需要用 AOP 對 Controller 層的所有方法處理即可.

好在 Spring 為我們提供了一個注解, 用來統一處理異常:

@ControllerAdvice@ResponseBodypublic 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("發生了未知異常", e);    // 發送郵件通知技術人員.    return ResultBean.error(-99, "系統出現錯誤, 請聯系網站管理員!");  }}

在這里統一配置需要處理的異常, 同樣, 對于未知的異常, 一定要及時發現, 并進行處理. 推薦出現未知異常后發送郵件, 提示技術人員.

總結

總結一下統一異常處理的方法:

  1. 不使用隨意返回各種數據類型, 要統一返回值規范.
  2. 不在業務代碼中捕獲任何異常, 全部交由 @ControllerAdvice 來處理.

一個簡單的演示項目: https://github.com/zhaojun1998/exception-handler-demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日本午夜精品理论片a级appf发布| 亚洲国产另类 国产精品国产免费| 欧洲精品久久久| 国产91在线播放精品91| 欧美福利视频在线观看| 亚洲综合在线做性| 久久久亚洲国产天美传媒修理工| 欧美一级片一区| 51视频国产精品一区二区| 日韩视频中文字幕| 日韩欧中文字幕| 久久久精品美女| 欧美成人国产va精品日本一级| 欧美另类第一页| 欧美激情一区二区三区高清视频| 午夜精品在线观看| 国产原创欧美精品| 亚洲日韩中文字幕在线播放| 成人疯狂猛交xxx| 国产精品美女网站| 欧美视频精品一区| 日韩在线视频网站| 久久777国产线看观看精品| 中文字幕欧美视频在线| 亚洲free性xxxx护士白浆| 久久精品国产96久久久香蕉| 97视频在线观看免费| 欧美一级淫片videoshd| 亚洲另类欧美自拍| 欧美性xxxxx| 日韩女在线观看| 成人欧美一区二区三区黑人| 国产精品久久久久久久久久东京| 在线国产精品视频| 亚洲综合中文字幕68页| 亚洲一区二区免费在线| 久久精品成人一区二区三区| 欧美中文在线视频| 久久中文字幕在线| 国产视频亚洲精品| www.久久色.com| 在线免费看av不卡| 精品国产户外野外| 欧美日韩美女在线观看| 久久久久久有精品国产| 一区二区亚洲欧洲国产日韩| 欧美劲爆第一页| 欧美高清在线播放| 45www国产精品网站| 91系列在线观看| 国产欧美日韩亚洲精品| 国产91av在线| 欧美日韩性视频在线| 亚洲一区二区日本| 久久久999成人| 韩日欧美一区二区| 国产亚洲精品va在线观看| 日韩av综合中文字幕| 亚洲国产成人精品久久| 日本高清久久天堂| 国产不卡av在线免费观看| 日韩高清免费观看| 国产精品三级久久久久久电影| 91久久国产婷婷一区二区| 国语对白做受69| 九九综合九九综合| 久久亚洲综合国产精品99麻豆精品福利| 日韩视频永久免费观看| 5252色成人免费视频| 日韩免费高清在线观看| 欧美黑人xxxⅹ高潮交| 亚洲天堂男人天堂女人天堂| 高清欧美性猛交xxxx黑人猛交| 亚洲图片欧美日产| 久久成人国产精品| 国产精品网站入口| 成人女保姆的销魂服务| 欧美性猛交xxxx富婆| 欧美日韩国产91| 国产日韩欧美视频在线| 久久91精品国产| 色琪琪综合男人的天堂aⅴ视频| 亚洲美女中文字幕| 精品福利在线观看| 亚洲视频999| 国产一区香蕉久久| 91久久久久久国产精品| 亚洲性夜色噜噜噜7777| 久久精品国产一区二区电影| 日韩福利伦理影院免费| 日本亚洲欧美三级| 欧美日韩国产在线播放| 欧美激情视频网站| 久久久久日韩精品久久久男男| 色婷婷av一区二区三区久久| 久久精品亚洲一区| 久久精品影视伊人网| 亚洲视频欧美视频| 成人情趣片在线观看免费| 黑人巨大精品欧美一区二区三区| 最近2019年日本中文免费字幕| 91色视频在线观看| 国产成人亚洲综合| 欧美亚洲视频在线观看| 热草久综合在线| 亚洲天堂免费观看| 亚洲人成网站777色婷婷| 中文字幕日韩欧美| 欧美激情在线观看| 国产精品99一区| 欧美国产激情18| 色综合视频网站| 成人福利视频网| 米奇精品一区二区三区在线观看| 粉嫩av一区二区三区免费野| 国产精品精品一区二区三区午夜版| 激情成人在线视频| 自拍偷拍亚洲一区| 国产精品高潮呻吟久久av野狼| 91精品国产综合久久香蕉的用户体验| 欧美丝袜一区二区三区| 国产精品观看在线亚洲人成网| 欧美激情中文字幕在线| 国产一区二区日韩精品欧美精品| 日韩av在线免费| 久久99精品久久久久久噜噜| 日韩在线观看精品| 国自在线精品视频| 亚洲精品美女在线| 国产在线视频一区| 国产精品九九九| 国产日本欧美视频| 午夜精品一区二区三区在线视| 欧美性xxxx极品hd欧美风情| 91av在线播放| 性欧美暴力猛交69hd| 欧美日韩在线免费观看| 久久的精品视频| 国产成人在线一区| 成人黄色在线播放| 国产精品高潮呻吟久久av无限| 日韩av在线一区二区| 久久国产精品影片| 亚洲在线www| 精品五月天久久| 久久精品中文字幕免费mv| 久久综合网hezyo| 亚洲激情国产精品| 久久成人精品一区二区三区| 国自在线精品视频| 97国产在线观看| 日韩欧美aⅴ综合网站发布| 亚洲精品一区二区久| 亚洲欧美中文日韩在线v日本| 欧美在线亚洲一区| 欧美日韩亚洲精品一区二区三区| 欧美另类69精品久久久久9999| 欧美大片在线看免费观看| 亚洲欧洲偷拍精品| 亚洲精品www| 国产一区二区在线免费视频| 久久免费视频在线| 欧美日韩国产va另类| 色悠悠久久88|