優雅的寫 api 接口

  對於前後端分離項目,前端按照約定的URL路徑請求數據,後端根據前端傳過來的參數,處理業務,並返回結果。下面來介紹下,後端應該如何把數據返回給前端。

返回的數據格式

前後端數據傳輸一般用json格式,如下

{
  //狀態碼
  code : string,
  // 信息描述
  message : string,
  // 返回數據
  data : object
}



code狀態碼,對照http的狀態碼,我們設計自己的狀態碼

分類 分類描述
1* 信息,服務器收到請求,需要請求者繼續執行操作
2* 成功,操作被成功接收並處理
3* 重定向,需要進一步的操作以完成請求
4* 客戶端錯誤,請求包含語法錯誤或無法完成請求
5* 服務器錯誤,服務器在處理請求的過程中發生了錯誤

我們用四位數作爲我們的狀態碼

區間 分類描述
1000 - 1999 信息,服務器收到請求,需要請求者繼續執行操作
2000 - 2999 成功,操作被成功接收並處理
3000 - 3999 重定向,需要進一步的操作以完成請求
4000 - 4999 客戶端錯誤,請求包含語法錯誤或無法完成請求
5000 - 5999 服務器錯誤,服務器在處理請求的過程中發生了錯誤

代碼實現

返回體

/**
 * 通用的api返回格式
 *
 * @param <T>
 */
@Data
@AllArgsConstructor
public class ResponseObject<T> {

    private String code;

    private String message;

    private T data;

    public static <T> ResponseObject<T> success(T data) {
        return new ResponseObject<T>("2000", "success", data);
    }

}



Controller

用Controller層來控制返回的數據格式,一般在項目中,還會有sevice層來做業務處理。

@RestController
@RequestMapping("/simple")
public class SimpleApiController {
    
    @RequestMapping("/article")
    public Article article() {
        return new Article("內容", "標題", "作者");
    }
}



下面來改造一下返回的數據格式

@RestController
@RequestMapping("/simple")
public class SimpleApiController {
    
    @RequestMapping("/article")
    public ResponseObject<Article> article() {
        return ResponseObject.success(new Article("內容", "標題", "作者"));
    }
}



用ResponseObject對Article包裝賦值,然後進行返回。但是每次都要這樣寫,沒有第一種來得直接

優雅優化

優化思路

  1. 定義一個註解@ResponseResult,表示這個接口返回的值需要包裝一下

  2. 實現接口ResponseBodyAdvice和@ControllerAdvice,判斷方法或Controller是否有@ResponseResult註解,有就把Controller接口的返回值進行包裝。

具體實現

自定義註解ResponseResult

/**
 * 放在需要被包裝返回值的Controller或方法上
 */
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseResult {
}



對含有註解ResponseResult的類或方法的返回值進行包裝

/**
 * 通用的返回體處理
 */
@ControllerAdvice
public class ResponseResultAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {

        // 當類或方法上含有ResponseResult註解時
        return returnType.getDeclaringClass().isAnnotationPresent(ResponseResult.class) || returnType.hasMethodAnnotation(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 包裝返回值
        return ResponseObject.success(body);
    }

}



添加通用的錯誤處理

/**
 * 通用的錯誤處理
 */
@RestControllerAdvice
public class ErrorControllerAdvice {

    @ExceptionHandler(Throwable.class)
    public ResponseObject<String> error() {
        return new ResponseObject<>("5000", "服務器錯誤", null);
    }

}



改寫之前的controller

@RestController
@ResponseResult
@RequestMapping("/grace")
public class GraceApiController {

    @RequestMapping("/article")
    public Article article() {
        return new Article("內容", "標題", "作者");
    }

    @RequestMapping("/error")
    public Article error() {
        int a = 1 / 0;
        return new Article("內容", "標題", "作者");
    }

}



到此就可以了

源碼地址:[email protected]:liuyu_cupid/zeus.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章