封裝ResultVO實現統一返回結果

Hank-He發表於2020-11-24

背景

為了在開發中,返回到前端的資料內容格式趨於一致,我們在開發過程中最好能夠將返回資料物件的格式進行約定,以便於開發對接過程中的約定速成;本章將帶你瞭解如何設計統一返回物件,以及與其相關的知識內容。

封裝返回結果物件

ResultVO物件封裝

通過RestFul介面開發的介面,一般含有介面執行狀態(成功、失敗、失敗描述、成功的資料返回物件)

因此我們可以將返回結果物件結構定義如下如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "統一請求的返回物件")
public class ResultVO<T> {
    @ApiModelProperty(value = "錯誤程式碼")
    private Integer code;
    @ApiModelProperty(value = "訊息")
    private String msg;
    @ApiModelProperty(value = "對應返回資料")
    private T data;
}

定義了統一的的返回物件,那麼一般我們需要考慮不同場景的輸出和呼叫;比如成功,失敗,或其他異常等情況的便捷呼叫。如果是因為某種業務原因需要返回失敗操作,一般包含有錯誤碼和錯誤資訊,更有勝者包含對應的錯誤堆疊異常明細;那麼就需要我們對於錯誤碼做比較好的規劃和設計了;

錯誤程式碼結構設計

這裡我們設計瞭如下的錯誤碼規劃,首先定義一個IErrorCode(錯誤程式碼的介面類),裡面定義兩個方法,即獲取錯誤碼和錯誤訊息的介面方法,如下

public interface IErrorCode {
    /**
     * 描述:得到錯誤碼
     * @date 2020/11/21
     **/
    Integer getCode();
    /**
     * 描述:得到錯誤訊息
     * @Author Hank
     **/
    String getMsg();
}

定義了介面類,然後我們再定義ErrorCode的介面實現列舉;如下

public enum  ErrorCode implements IErrorCode {
    /***
     * 1. 以下錯誤碼的定義,需要提前與前端溝通
     * 2. 錯誤碼按模組進行錯誤碼規劃
     * 3. 所有錯誤碼列舉類均需要實現錯誤碼介面類
     */
    SUCCESS(0,"操作成功"),
    SYSTEM_BUSY(10000,"系統繁忙,請稍後再試!"),
    FORM_VALIDATION_ERROR(10001,"表單驗證錯誤"),
    // 使用者登入方面錯誤碼
    LOGIN_ERROR(101001, "你還未登陸,請及時登陸"),
    TOKEN_ERROR(101002, "登入憑證已過期,請重新登入");

   private Integer code;
   private String msg;

   ErrorCode(Integer code,String message){
       this.code=code;
       this.msg=message;
   }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}

小試牛刀

做了如上定義,我們就可以在對應的方法中去進行使用了,使用過程中,我們可以直接 return ResultVO;如下:

@RestController
@RequestMapping(value = "/test")
@Api(tags = "基礎模組介面")
public class IndexCtrl {

    @ApiOperation(value = "hello 介面",notes = "hello介面的描述")
    @GetMapping(value = "/index")
    public ResultVO hello(){
        ResultVO rv=new ResultVO();
        rv.setCode(ErrorCode.SUCCESS.getCode());
        rv.setMsg(ErrorCode.SUCCESS.getMsg());
        return rv;
    }
}

效果增強

從上面的程式碼中我們可以看到,要做這樣的返回,貌似還是比較繁瑣,基本上要四行程式碼才能有一個完成的返回,並且ErrorCode類是固定的。

 

我們嘗試將上面程式碼再做一次修改,希望達到如下效果:

  • 在使用返回物件的時候能夠儘量簡單,或開發中無感
  • 各個模組可以定義自己的錯誤碼實現類,融入到基礎框架中

ResultVO物件增強

需要達到如上兩點,我們首先修改ResultVO類,豐富建構函式和支援列舉方法的傳值,程式碼如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "統一請求的返回物件")
public class ResultVO<T> {
    @ApiModelProperty(value = "錯誤程式碼")
    private Integer code;
    @ApiModelProperty(value = "訊息")
    private String msg;
    @ApiModelProperty(value = "對應返回資料")
    private T data;

    public ResultVO(int code, String mesage) {
        setCode(code);
        setMsg(mesage);
    }
    
    public ResultVO(IErrorCode errorCode, T data) {
        setCodeMessage(errorCode);
        setData(data);
    }

    public ResultVO setCodeMessage(IErrorCode codeMessage) {
        setCode(codeMessage.getCode());
        setMsg(codeMessage.getMsg());
        return this;
    }
}

封裝助手工具

完成如上,我們還可以封裝一個工具類RV,方便使用:

public class RV {

    /***
     * 成功的返回物件
     * @param data
     * @return
     */
    public static ResultVO success(Object data) {
        return new ResultVO(ErrorCode.SUCCESS,data);
    }

    /**
     * 失敗的返回物件
     * @Param: ErrCodeInterface
     * @return: [ResultVO]
     *
     **/
    public static ResultVO fail(IErrorCode errorCode) {
        return new ResultVO().setCodeMessage(errorCode);
    }

    /**
     * 描述: 通過errorCode和資料物件引數,構建一個新的物件
     * @param [errorCode, data]
     * @return: [ResultVO]
     **/
    public static ResultVO result(IErrorCode errorCode,Object data){
        return new ResultVO(errorCode,data);
    }
}

簡潔性驗證

還是以上面應用程式碼為例,最終程式碼修改如下

@RestController
@RequestMapping(value = "/test")
@Api(tags = "基礎模組介面")
public class IndexCtrl {

    @ApiOperation(value = "hello 介面",notes = "hello介面的描述")
    @GetMapping(value = "/index")
    public ResultVO hello(){
        return RV.success(null);;
    }
}

擴充套件性驗證

前面我們描述了那麼多,這裡所謂的擴充套件性怎麼理解呢,這裡所謂的擴充套件性,更多是在不同業務系統,對於錯誤碼的定義的擴充套件,比如一個我們專案裡面,分成了多個不同的模組,但每個模組的實現都依賴於基礎common包中封裝的工具;對於錯誤碼,我們不可能一次性在common中定義出所有模組的錯誤碼;因此我們在設計的時候,特意定義了IErrorCode介面庫類,預設由ErrorCode做了實現;

也就意味著,在common包中定義的這些類,沒有特殊情況不用高頻的修改,那我們在其他業務模組要定義自己的錯誤碼可以怎麼做呢。

如下,我們只需要在對應的業務模組,定義自己的錯誤碼列舉類即可,比如在裝置管理模組

/**
 * new-retail
 * <p>
 * 錯誤碼定義範圍 10101-10200
 * </p>
 * @author Hank
 * @since 2020-11-21
 */
public enum DeviceErrorCode implements IErrorCode {
    DEVICE_OFFLINE(10101,"裝置已離線"),
    COMMAND_ERROR(10102,"指令錯誤");

    private Integer code;
    private String msg;
    DeviceErrorCode(Integer code,String message){
        this.code=code;
        this.msg=message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMsg() {
        return msg;
    }
}

在Controller中的應用

我們根據裝置管理模組定義了對應的錯誤列舉類,也就意味著,在做該模組實際業務的時候,我們可以直接對外丟擲對應的錯誤碼,而不必考慮與其他模組的適配問題;其應用如下;

@PostMapping(value = "/restart")
public ResultVO restart(@RequestBody DeviceInfo deviceInfo) {
    load(deviceInfo);
    if (!deviceInfo.online()) {
        return RV.result(DeviceErrorCode.DEVICE_OFFLINE, deviceInfo);
    }
    return RV.success(deviceInfo);
}

在異常中的應用

在前章節我們講到異常BusinessException的封裝,但是我們只是做了簡單的繼承RuntimeException而已,沒有繼續深入;那我們再結合本章所講到的錯誤程式碼進行完善增強

/**
 * new-retail-lesson
 * <p>
 * 自定義業務異常類
 * </p>
 * @author Hank
 * @since 2020-10-31
 */
public class BusinessException extends RuntimeException {
    private int code;
    private String detailMessage;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
        this.detailMessage = message;
    }

    public BusinessException(IErrorCode errorCode) {
        this(errorCode.getCode(),errorCode.getMsg());
    }

    public int getCode() {
        return code;
    }

    public String getDetailMessage() {
        return detailMessage;
    }
}

從上面程式碼中,我們可以看出,我們在BusinessException中增加了兩個變數code,detailMessage;並對建構函式做了多樣性處理,值得注意的是我們在建構函式中,增加了IErrorCode介面的引數;

既然在異常做了增強,那我們的異常攔截處也需要做響應的處理,找到我們前面定義的全域性異常類GlobalExceptionHander,在對應的攔截BusinessException處做對應的處理,如下

@RestControllerAdvice
public class GlobalException {
    /**
     * 描述:業務異常攔截
     * @param
     * @date 2020/10/31
     * @Author Hank
     **/
    @ExceptionHandler(value = BusinessException.class)
    public ResultVO businessException(BusinessException e){
        ResultVO rv= new ResultVO(e.getCode(),e.getDetailMessage());
        return rv;
    }
}

完成了上面的基礎工作,我們接下來看下在編碼中能夠如何使用;

  1. 在Controller中有業務異常丟擲時,拿我們剛才的例子,程式碼調整如下,不用直接return,可以直接丟擲對應的業務異常,由GlobalExceptionHander來兜底就行
@PostMapping(value = "/restart")
public ResultVO restart(@RequestBody DeviceInfo deviceInfo) {
    load(deviceInfo);
    if (!deviceInfo.online()) {
        throw new BusinessException(DeviceErrorCode.DEVICE_OFFLINE);
    }
    return RV.success(deviceInfo);
}
  1. 在業務程式碼中,可以直接丟擲對應的業務異常,最終由GlobalException來兜底

 

當然在異常處理部分,我們可以根據場景需要,定義不同型別的異常,結構與上面類似,即可達到相同的效果

 

小結

上面我們主要介紹了

  1. ResultVO物件的封裝
  2. 然後介紹了統一錯誤程式碼介面類和錯誤列舉類的設計
  3. 以及結合ResultVO物件和IErrorCode介面做了整合
  4. 通過IErrorCode與自定義異常結合再結合全域性異常攔截與ResultVO物件結合做全域性異常攔截;
  5. 最後還介紹了結合定義的ResultVO物件和IErrorCode介面類,可以如何做到異常方面的擴充套件。

 

以上為本章介紹的所有內容,希望對你有幫助,如果你在統一返回物件封裝和異常封裝方面有更好的不同實踐,也歡迎在留言區進行留言。

 

想要了解更多資訊,可關注本公眾號(一起學開源);或請長按以下二維碼新增助手。將拉你加入社群進行更多交流

在這裡插入圖片描述

 

 

相關文章