在Spring Boot項目中咱們能夠經過RestControllerAdvice
配合實現ResponseBodyAdvice<T>
接口來保證Spring MVC接口具備統一的返回格式,以保證前端同窗可以封裝統一的數據接收工具。可是不少網上的文章並無對實際開發中的細節做出更多的講解。今天胖哥就來分享一下個人採坑經歷,也算做一個總結。前端
我記得在前面關於Swagger3的文章中提過,若是咱們不指定範圍將致使Swagger沒法識別接口的元信息。所以若是你使用了Swagger必須指定其範圍,這裏你能夠經過指定掃描包來指定其做用域:java
@RestControllerAdvice("cn.felord.controller")
若是你的Spring MVC控制器有統一的父類控制器的話,編程
@RestController @RequestMapping("/foo") public class FooController extends BaseController { //todo 省略 }
也能夠這樣:json
@RestControllerAdvice(assignableTypes = BaseController.class)
有些接口可能根據業務須要或者協議須要不能使用統一返回體,例如支付的通知應答。這就須要一個相似白名單的機制來繞過統一返回體控制器通知類。咱們能夠藉助於ResponseBodyAdvice<T>
的下列方法實現:數組
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
這個方法若是返回false
就表示不執行統一返回體的封裝邏輯。這裏我推薦註解實現。定義一個標記註解,能夠定義在類上或者方法上:app
@Documented @Inherited @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface IgnoreRestBody { }
而後上面的supports
方法這樣實現:ide
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return !returnType.hasMethodAnnotation(IgnoreRestBody.class); }
若是某個Controller下全部的方法都繞過,就把這個註解標記在控制器類上;若是隻想忽略某個方法上就把它標記在該方法上便可。工具
有些接口咱們會返回一個字符串:debug
@GetMapping("/get") public String getStr(){ //返回了一個字符串 return "felord.cn"; }
咱們但願這個字符串被統一返回體處理,相似這樣:調試
{ code: 200, data: "felord.cn", msg: "返回成字符串", }
可是你會發現並無達到指望的效果,會拋出類型轉換異常。這是由於當咱們的Spring MVC接口返回數據時,會根據Content-Type
來選擇一個HttpMessageConverter
來處理,而字符串在不聲明Content-Type
的狀況下優先使用StringHttpMessageConverter
,就致使了轉換異常,須要設定成MappingJackson2HttpMessageConverter
用Jackson來處理,Spring MVC的對應配置以下:
@Configuration(proxyBeanMethods = false) public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 解決 String 統一封裝RestBody的問題 converters.add(0, new MappingJackson2HttpMessageConverter()); } }
嗯,這樣就起效了!你覺得這樣就完了?你會發現你的JSON序列化不按照你設置的策略執行了。由於你new了一個而不是採用系統初始化的那個。解決方法爲,將Spring IoC中的ObjectMapper
注入到MappingJackson2HttpMessageConverter
中去。或者你使用Debug調試出系統默認的MappingJackson2HttpMessageConverter
的位置,好比個人索引爲7
,就能夠這樣配置:
@Configuration(proxyBeanMethods = false) public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 解決 String 統一封裝RestBody的問題 HttpMessageConverter<?> httpMessageConverter = converters.get(7); if (!(httpMessageConverter instanceof MappingJackson2HttpMessageConverter)) { // 確保正確,若是有改動就從新debug throw new RuntimeException("MappingJackson2HttpMessageConverter is not here"); } converters.add(0, httpMessageConverter); } }
曾經一個安卓開發同窗說,你這統一結構中的data
若是是數組:
{ code: 200, data: ['a','b'], msg: "返回成字符串", }
後續若是data
添加其它與數組沒有關係的屬性就不兼容了,你應該保證這個data
是個Map
。是的,這也是問題,實際中發現不單單是數組,若是是int
、long
等原始類型或者String
類型都面臨這種狀況,須要加一個額外的判斷body
是否是可能改變data
類型的類型:
private boolean checkPrimitive(Object body) { Class<?> clazz = body.getClass(); return clazz.isPrimitive() || clazz.isArray() || Collection.class.isAssignableFrom(clazz) || body instanceof Number || body instanceof Boolean || body instanceof Character || body instanceof String; }
而後咱們在ResponseBodyAdvice<T>
實現中增長一個判斷:
// 加強擴展性 if (checkPrimitive(body)) { return RestBody.okData(Collections.singletonMap("result", body)); }
就解決問題了。
今天對Spring Boot中統一返回體的一些細節問題進行了分享,但願可以幫助你解決一些實際開發中遇到的一樣問題。多多關注:碼農小胖哥 分享更多有用的編程知識。
關注公衆號:Felordcn 獲取更多資訊