一次和前端的相互甩鍋的問題記錄

背景

咱們在APP上有個功能,須要獲取用戶當前定位,而後當用戶關閉了GPS後,沒有獲取到用戶定位,會觸發一個bug,彈窗內容以下。前端

問題分析

這個問題的直接緣由就是移動端的值取不到,致使沒有給變量賦值,就將"undefined"傳給了後端,後端的這個值定義的Integer,類型轉換失敗,報錯。後端

深層緣由是異常處理機制有問題,因而後端和前端開始撕逼了bash

前端觀點: 後端代碼太不健壯了, 就算前端傳錯了,也應該具有容錯性;此外APP是有版本的,就算hotfix,用戶也不必定升級,上一版本用戶仍是會有問題,因此這種問題儘可能是後端來修復。服務器

後端觀點:前端沒有異常兜底機制,用戶不該該看到任何這種程序異常。對於有定製需求的異常,報特定異常,沒有應該顯示通用異常,好比彈窗"服務不可用"。另外這種屬於http請求層面的約束,前端不聽從約束,還來怪我。我後端框架層面就給你攔截了,沒到業務代碼。網絡

雙方說的都好有道理,誰也說服不了誰。可是關於目標你們達成一致:堅定不能讓用戶看到這種類型的彈窗異常。框架

既然說服不了對方,就只能從更深刻的分析問題,看看更合理的解法spa

通用異常的處理方式

http一般錯誤有debug

  • 4開頭:客戶端參數有問題,須要後端提供debug信息。理論上應該只是聯調的時候會出現,可是實際上不必定(這不就打臉了嗎)
  • 5開頭:服務器端有錯誤,客戶端有統一提供的異常處理
  • 2開頭:業務異常,若是有UI要求,後端返回一個code碼,前端根據code碼,展現UI。若是沒有UI要求,前端直接展現後端返回的錯誤消息。

爲了統一異常處理,通常公司的作法都是API統一返回一套數據格式,日誌

{
    "error_code": "xx", // code碼,1表明正常,其餘表示異常。
    "error_msg": "xx" // msg,錯誤提示消息
    "data": "xx" // 正常數據
}

複製代碼

咱們也是,而且將4開頭的都統一處理成這套統一的數據格式。code

那麼前端處理異常的邏輯

此次的問題就是走到2的分支了。

先後端都沒作錯,問題是後端對於異常模型的抽象有問題,客戶端參數有問題,須要後端提供debug信息,而不是給用戶展現的錯誤信息。

其實服務端對於異常就分三種

  • 客戶端參數有問題的異常(前端須要debug信息和錯誤msg信息)
  • 須要用戶知道的業務異常,前端須要根據code展現的(前端須要code碼)
  • 通用的服務端異常,包裝成消息給前端。(前端須要錯誤msg信息)

解法

分析清楚了問題後,就有了解法。

解法1:錯誤消息和debug消息分離,返回的API統一格式中增長一種字段。debug_msg 給開發看的,error_msg 仍是給用戶看的

解法2:定義幾個枚舉code。做爲開發的約定

code error_msg 參數錯誤含義
010000 系統錯誤[010000] 請求方法不支持
010001 系統錯誤[010001] 缺乏路徑參數
010002 系統錯誤[010002] 缺乏必須的請求參數
010003 系統錯誤[010003] 類型不匹配
010004 系統錯誤[010004] 請求體異常
010005 系統錯誤[010005] // 參數校驗異常(body 或 param)
010006 系統錯誤[010006] 參數綁定異常(表單)

解法1定義比較清晰,可是爲了這種corner case增長了一個字段的開銷,網絡傳輸代價高了。另外還須要前端配合更改,改動成本比較大。

解法2兼容瞭如今的實現,前端不用更改,可是對於客戶端參數有問題這種錯誤提醒不是很友好,不能向前端顯示具體的參數錯誤了。只能打日誌。

和前端討論了下,肯定了解法2。

總結

因此這個問題,最後的解法

  • 前端獲取不到定位時,將undefined這個變量賦值
  • 後端針對移動端這個服務,改動了異常處理機制
@RestControllerAdvice
public class ApiStandardException {
    private static final String ERROR_MSG = "系統錯誤";
 
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Result> handle(final Exception ex, final WebRequest request)
            throws Exception {

        try {
            if (ex instanceof HttpRequestMethodNotSupportedException) {
                // 請求方法不支持
                LOG.warn("Request method is not supported");
                throw new BusinessException(WebRequestErrorEnum.METHOD_ERR.getCode(), ERROR_MSG);
            } else if (ex instanceof MissingPathVariableException) {
                LOG.warn("MISSING_PATHVAR" + ex.getMessage());
                // 缺乏路徑參數
                throw new BusinessException(WebRequestErrorEnum.MISSING_PARAM.getCode(), ERROR_MSG);
            } else if (ex instanceof MissingServletRequestParameterException) {
                // 缺乏必須的請求參數
            }
            // 省略其餘異常處理
複製代碼

關注【方丈的寺院】,第一時間收到文章的更新,與方丈一塊兒開始技術修行之路

在這裏插入圖片描述
相關文章
相關標籤/搜索