Spring Boot Controller 統(tǒng)一返回格式

功能開發(fā)中,我們會有這種需求,希望所有的數(shù)據(jù)返回統(tǒng)一的格式,包含狀態(tài)碼,數(shù)據(jù),錯誤信息,當(dāng)前時間,比如:

{"code":200,"data":null,"message":"OK","timestamp":1559292578716}

首先我們需要有這么一個類

@Data
public class Result<T> {
    private long timestamp;
    private String message;
    private int code=SUCCESS;
    private T data;
    @Data
    public static class Builder<T> {
        //部分代碼略
    }
}

但是我們不能在每一個Controller上的返回格式都定義為Result

Spring Boot 如何在不改變原有的Controller的基礎(chǔ)上實現(xiàn)呢?
我們可以借助Spring提供的ResponseBodyAdvice實現(xiàn)

public interface ResponseBodyAdvice<T> {
    boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);

    @Nullable
    T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}

該接口只有兩個方法supportsbeforeBodyWrite

  • supports 方法返回boolean,判斷是否支持Controller的返回類型
  • beforeBodyWrite 寫出之前的處理,此文中主要依靠此方法,T var1就是Controller返回的對象

寫一個我們自己的實現(xiàn)

@ControllerAdvice()
public class ResponseAdvisor implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        return new Result.Builder<>().data(body).build();
    }
}

系統(tǒng)中原有的Controller

@RestController
public class DemoController {
    @GetMapping("/echo/{name}")
    public String echo(@PathVariable("name") String name){
        return "hello:"+name;
    }
    @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id") Long id){
        User user=new User(id,"Lucy",18);
        return user;
    }
    @GetMapping("/download")
    public ResponseEntity<FileSystemResource> download() {
        File file = new File("d:/1.jpg");
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        try {
            headers.add("Content-Disposition", "attachment; filename=" + new String(file.getName().getBytes("GB2312"),"ISO-8859-1"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new FileSystemResource(file));
    }
}

分別進行測試

{
    "timestamp": 1559293805111, 
    "message": "OK", 
    "code": 200, 
    "data": {
        "id": 1, 
        "name": "Lucy", 
        "age": 18
    }
}
java.lang.ClassCastException: com.podigua.demo.core.Result cannot be cast to java.lang.String
java.lang.ClassCastException: com.podigua.demo.core.Result cannot be cast to org.springframework.core.io.Resource

三個測試.發(fā)現(xiàn)有兩個出錯, 出錯的原因是找不到對應(yīng)的HttpMessageConverter,HttpMessageConverter 是一組消息轉(zhuǎn)換器,以下列出部分MessageConverter

  • StringHttpMessageConverter 負責(zé)字符串類型的數(shù)據(jù)的讀取或者寫出
  • ByteArrayHttpMessageConverter 負責(zé)二進制格式數(shù)據(jù)的讀取或者寫出
  • ResourceHttpMessageConverter 負責(zé)資源文件的讀取或者寫出
  • FormHttpMessageConverter 負責(zé)讀取form提交的數(shù)據(jù)application/x-www-form-urlencoded
  • MappingJacksonHttpMessageConverter 負責(zé)json格式數(shù)據(jù)的讀取或者寫出

原因就是缺少 MappingJackson2HttpMessageConverterResourceHttpMessageConverter

我們調(diào)整下代碼,增加這兩個消息轉(zhuǎn)換器

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer  {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new MappingJackson2HttpMessageConverter());
        converters.add(1, new ResourceHttpMessageConverter());
    }
}

資源類數(shù)據(jù),不能按照這種格式返回,最終返回的是文件流,我們調(diào)整下代碼ResponseAdvisorbeforeBodyWrite方法,若返回值已經(jīng)是Result類型也不需要處理

@Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        if (body instanceof Resource) {
            return body;
        }
        if (body instanceof Result) {
            return body;
        }
        return new Result.Builder<>().data(body).build();
    }

Coltroller拋出了異常呢? 需要AOP捕獲異常,并將異常信息與狀態(tài)碼封裝為Result的數(shù)據(jù)即可

項目github地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容