功能開發(fā)中,我們會有這種需求,希望所有的數(shù)據(jù)返回統(tǒng)一的格式,包含狀態(tài)碼
,數(shù)據(jù)
,錯誤信息
,當前時間
,比如:
{"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
的基礎上實現(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);
}
該接口只有兩個方法supports
和beforeBodyWrite
- 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)有兩個出錯, 出錯的原因是找不到對應的HttpMessageConverter
,HttpMessageConverter
是一組消息轉換器
,以下列出部分MessageConverter
-
StringHttpMessageConverter
負責字符串類型的數(shù)據(jù)的讀取或者寫出 -
ByteArrayHttpMessageConverter
負責二進制格式數(shù)據(jù)的讀取或者寫出 -
ResourceHttpMessageConverter
負責資源文件的讀取或者寫出 -
FormHttpMessageConverter
負責讀取form提交的數(shù)據(jù)application/x-www-form-urlencoded
-
MappingJacksonHttpMessageConverter
負責json格式數(shù)據(jù)的讀取或者寫出
原因就是缺少 MappingJackson2HttpMessageConverter
和ResourceHttpMessageConverter
我們調整下代碼,增加這兩個消息轉換器
@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ù),不能按照這種格式返回,最終返回的是文件流,我們調整下代碼ResponseAdvisor
的beforeBodyWrite
方法,若返回值已經(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地址