Spring Boot統(tǒng)一返回體的踩坑記錄
前言
在Spring Boot項目中我們可以通過RestControllerAdvice配合實現(xiàn)ResponseBodyAdvice<T>接口來保證Spring MVC接口具有統(tǒng)一的返回格式,以保證前端同學(xué)能夠封裝統(tǒng)一的數(shù)據(jù)接收工具。但是很多網(wǎng)上的文章并沒有對實際開發(fā)中的細節(jié)作出更多的講解。今天胖哥就來分享一下我的采坑經(jīng)歷,也算作一個總結(jié)。
控制作用范圍
我記得在前面關(guān)于Swagger3的文章中提過,如果我們不指定范圍將導(dǎo)致Swagger無法識別接口的元信息。因此如果你使用了Swagger必須指定其范圍,這里你可以通過指定掃描包來指定其作用域:
@RestControllerAdvice("cn.felord.controller")
如果你的Spring MVC控制器有統(tǒng)一的父類控制器的話,
@RestController @RequestMapping("/foo") public class FooController extends BaseController { //todo 省略 }
也可以這樣:
@RestControllerAdvice(assignableTypes = BaseController.class)
白名單
有些接口可能根據(jù)業(yè)務(wù)需要或者協(xié)議需要不能使用統(tǒng)一返回體,例如支付的通知應(yīng)答。這就需要一個類似白名單的機制來繞過統(tǒng)一返回體控制器通知類。我們可以借助于ResponseBodyAdvice<T>的下列方法實現(xiàn):
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
這個方法如果返回false就表示不執(zhí)行統(tǒng)一返回體的封裝邏輯。這里我推薦注解實現(xiàn)。定義一個標記注解,可以定義在類上或者方法上:
@Documented @Inherited @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface IgnoreRestBody { }
然后上面的supports方法這樣實現(xiàn):
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return !returnType.hasMethodAnnotation(IgnoreRestBody.class); }
如果某個Controller下所有的方法都繞過,就把這個注解標記在控制器類上;如果只想忽略某個方法上就把它標記在該方法上即可。
返回獨立字符串的問題
有些接口我們會返回一個字符串:
@GetMapping("/get") public String getStr(){ //返回了一個字符串 return "felord.cn"; }
我們希望這個字符串被統(tǒng)一返回體處理,類似這樣:
{ code: 200, data: "felord.cn", msg: "返回成字符串", }
但是你會發(fā)現(xiàn)并沒有達到期望的效果,會拋出類型轉(zhuǎn)換異常。這是因為當我們的Spring MVC接口返回數(shù)據(jù)時,會根據(jù)Content-Type來選擇一個HttpMessageConverter來處理,而字符串在不聲明Content-Type的情況下優(yōu)先使用StringHttpMessageConverter ,就導(dǎo)致了轉(zhuǎn)換異常,需要設(shè)定成MappingJackson2HttpMessageConverter用Jackson來處理,Spring MVC的對應(yīng)配置如下:
@Configuration(proxyBeanMethods = false) public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 解決 String 統(tǒng)一封裝RestBody的問題 converters.add(0, new MappingJackson2HttpMessageConverter()); } }
嗯,這樣就起效了!你以為這樣就完了?你會發(fā)現(xiàn)你的JSON序列化不按照你設(shè)置的策略執(zhí)行了。因為你new了一個而不是采用系統(tǒng)初始化的那個。解決方法為,將Spring IoC中的ObjectMapper注入到MappingJackson2HttpMessageConverter中去。或者你使用Debug調(diào)試出系統(tǒng)默認的MappingJackson2HttpMessageConverter的位置,比如我的索引為7,就可以這樣配置:
@Configuration(proxyBeanMethods = false) public class SpringMvcConfiguration implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 解決 String 統(tǒng)一封裝RestBody的問題 HttpMessageConverter<?> httpMessageConverter = converters.get(7); if (!(httpMessageConverter instanceof MappingJackson2HttpMessageConverter)) { // 確保正確,如果有改動就重新debug throw new RuntimeException("MappingJackson2HttpMessageConverter is not here"); } converters.add(0, httpMessageConverter); } }
Data的類型問題
曾經(jīng)一個安卓開發(fā)同學(xué)說,你這統(tǒng)一結(jié)構(gòu)中的data如果是數(shù)組:
{ code: 200, data: ['a','b'], msg: "返回成字符串", }
后續(xù)如果data添加其它與數(shù)組沒有關(guān)系的屬性就不兼容了,你應(yīng)該保證這個data是個Map。是的,這也是問題,實際中發(fā)現(xiàn)不僅僅是數(shù)組,如果是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>實現(xiàn)中增加一個判斷:
// 增強擴展性 if (checkPrimitive(body)) { return RestBody.okData(Collections.singletonMap("result", body)); }
就解決問題了。
總結(jié)
今天對Spring Boot中統(tǒng)一返回體的一些細節(jié)問題進行了分享,希望能夠幫助你解決一些實際開發(fā)中遇到的同樣問題。
到此這篇關(guān)于Spring Boot統(tǒng)一返回體踩坑記錄的文章就介紹到這了,更多相關(guān)Spring Boot統(tǒng)一返回體內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中StringRedisTemplate和RedisTemplate的區(qū)別及使用方法
本文主要介紹了Java中StringRedisTemplate和RedisTemplate的區(qū)別及使用方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04SpringBoot結(jié)合Tess4J實現(xiàn)拍圖識字的示例代碼
圖片中的文字提取已經(jīng)越來越多地應(yīng)用于數(shù)據(jù)輸入和自動化處理過程,本文主要介紹了SpringBoot結(jié)合Tess4J實現(xiàn)拍圖識字的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-06-06Java 17 更新后的 strictfp 關(guān)鍵字
strictfp 可能是最沒有存在感的關(guān)鍵字了,很多人寫了多年 Java 甚至都不知道它的存在,strictfp,字面意思就是嚴格的浮點型。這玩意兒居然還有個關(guān)鍵字,可見其地位還是很高的。下面文章小編就帶大家詳細介紹其關(guān)鍵字,需要的朋友可以參考一下2021-09-09idea 安裝 Mybatis 開發(fā)幫助插件 MyBatisCodeHelper-Pro 插件破解版的方法
MyBatisCodeHelper-Pro 插件可以幫助我們快速的開發(fā) mybatis,這篇文章給大家介紹idea 安裝 Mybatis 開發(fā)幫助插件 MyBatisCodeHelper-Pro 插件破解版的相關(guān)知識,感興趣的朋友跟隨小編一起看看吧2020-09-09spring-Kafka中的@KafkaListener深入源碼解讀
本文主要通過深入了解源碼,梳理從spring啟動到真正監(jiān)聽kafka消息的這套流程,從spring啟動開始處理@KafkaListener,本文結(jié)合實例流程圖給大家講解的非常詳細,需要的朋友參考下2023-02-02