Spring Boot統(tǒng)一返回體的踩坑記錄
前言
在Spring Boot項(xiàng)目中我們可以通過RestControllerAdvice配合實(shí)現(xiàn)ResponseBodyAdvice<T>接口來保證Spring MVC接口具有統(tǒng)一的返回格式,以保證前端同學(xué)能夠封裝統(tǒng)一的數(shù)據(jù)接收工具。但是很多網(wǎng)上的文章并沒有對(duì)實(shí)際開發(fā)中的細(xì)節(jié)作出更多的講解。今天胖哥就來分享一下我的采坑經(jīng)歷,也算作一個(gè)總結(jié)。
控制作用范圍
我記得在前面關(guān)于Swagger3的文章中提過,如果我們不指定范圍將導(dǎo)致Swagger無法識(shí)別接口的元信息。因此如果你使用了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)答。這就需要一個(gè)類似白名單的機(jī)制來繞過統(tǒng)一返回體控制器通知類。我們可以借助于ResponseBodyAdvice<T>的下列方法實(shí)現(xiàn):
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
這個(gè)方法如果返回false就表示不執(zhí)行統(tǒng)一返回體的封裝邏輯。這里我推薦注解實(shí)現(xiàn)。定義一個(gè)標(biāo)記注解,可以定義在類上或者方法上:
@Documented
@Inherited
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreRestBody {
}
然后上面的supports方法這樣實(shí)現(xiàn):
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.hasMethodAnnotation(IgnoreRestBody.class);
}
如果某個(gè)Controller下所有的方法都繞過,就把這個(gè)注解標(biāo)記在控制器類上;如果只想忽略某個(gè)方法上就把它標(biāo)記在該方法上即可。
返回獨(dú)立字符串的問題
有些接口我們會(huì)返回一個(gè)字符串:
@GetMapping("/get")
public String getStr(){
//返回了一個(gè)字符串
return "felord.cn";
}
我們希望這個(gè)字符串被統(tǒng)一返回體處理,類似這樣:
{
code: 200,
data: "felord.cn",
msg: "返回成字符串",
}
但是你會(huì)發(fā)現(xiàn)并沒有達(dá)到期望的效果,會(huì)拋出類型轉(zhuǎn)換異常。這是因?yàn)楫?dāng)我們的Spring MVC接口返回?cái)?shù)據(jù)時(shí),會(huì)根據(jù)Content-Type來選擇一個(gè)HttpMessageConverter來處理,而字符串在不聲明Content-Type的情況下優(yōu)先使用StringHttpMessageConverter ,就導(dǎo)致了轉(zhuǎn)換異常,需要設(shè)定成MappingJackson2HttpMessageConverter用Jackson來處理,Spring MVC的對(duì)應(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());
}
}
嗯,這樣就起效了!你以為這樣就完了?你會(huì)發(fā)現(xiàn)你的JSON序列化不按照你設(shè)置的策略執(zhí)行了。因?yàn)槟鉵ew了一個(gè)而不是采用系統(tǒng)初始化的那個(gè)。解決方法為,將Spring IoC中的ObjectMapper注入到MappingJackson2HttpMessageConverter中去?;蛘吣闶褂肈ebug調(diào)試出系統(tǒng)默認(rèn)的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)) {
// 確保正確,如果有改動(dòng)就重新debug
throw new RuntimeException("MappingJackson2HttpMessageConverter is not here");
}
converters.add(0, httpMessageConverter);
}
}
Data的類型問題
曾經(jīng)一個(gè)安卓開發(fā)同學(xué)說,你這統(tǒng)一結(jié)構(gòu)中的data如果是數(shù)組:
{
code: 200,
data: ['a','b'],
msg: "返回成字符串",
}
后續(xù)如果data添加其它與數(shù)組沒有關(guān)系的屬性就不兼容了,你應(yīng)該保證這個(gè)data是個(gè)Map。是的,這也是問題,實(shí)際中發(fā)現(xiàn)不僅僅是數(shù)組,如果是int、long等原始類型或者String類型都面臨這種情況,需要加一個(gè)額外的判斷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;
}
然后我們?cè)赗esponseBodyAdvice<T>實(shí)現(xiàn)中增加一個(gè)判斷:
// 增強(qiáng)擴(kuò)展性
if (checkPrimitive(body)) {
return RestBody.okData(Collections.singletonMap("result", body));
}
就解決問題了。
總結(jié)
今天對(duì)Spring Boot中統(tǒng)一返回體的一些細(xì)節(jié)問題進(jìn)行了分享,希望能夠幫助你解決一些實(shí)際開發(fā)中遇到的同樣問題。
到此這篇關(guān)于Spring Boot統(tǒng)一返回體踩坑記錄的文章就介紹到這了,更多相關(guān)Spring Boot統(tǒng)一返回體內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解spring cloud分布式整合zipkin的鏈路跟蹤
這篇文章主要介紹了詳解spring cloud分布式整合zipkin的鏈路跟蹤,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
SpringBoot動(dòng)態(tài)定時(shí)功能實(shí)現(xiàn)方案詳解
在SpringBoot項(xiàng)目中簡(jiǎn)單使用定時(shí)任務(wù),不過由于要借助cron表達(dá)式且都提前定義好放在配置文件里,不能在項(xiàng)目運(yùn)行中動(dòng)態(tài)修改任務(wù)執(zhí)行時(shí)間,實(shí)在不太靈活?,F(xiàn)在我們就來實(shí)現(xiàn)可以動(dòng)態(tài)修改cron表達(dá)式的定時(shí)任務(wù),感興趣的可以了解一下2022-11-11
spring boot jar部署控制臺(tái)日志亂碼的解決
這篇文章主要介紹了spring boot jar部署控制臺(tái)日志亂碼的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)版ATM系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)版ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級(jí)數(shù)據(jù)的具體實(shí)現(xiàn)
ThreadPoolTaskExecutor是Spring提供的任務(wù)執(zhí)行器實(shí)現(xiàn)之一,允許開發(fā)者配置線程池參數(shù)以適應(yīng)不同的應(yīng)用場(chǎng)景,創(chuàng)建 ThreadPoolTaskExecutor 實(shí)例并設(shè)置核心和最大線程數(shù)等屬性可以優(yōu)化性能,本文介紹了SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級(jí)數(shù)據(jù)的具體實(shí)現(xiàn)2024-12-12
SpringBoot?如何使用sharding?jdbc進(jìn)行分庫(kù)分表
這篇文章主要介紹了SpringBoot?如何使用sharding?jdbc進(jìn)行分庫(kù)分表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
spring中Mapstruct屬性映射的實(shí)現(xiàn)
本文主要介紹了spring中Mapstruct屬性映射的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
Java設(shè)計(jì)模式中的裝飾器模式簡(jiǎn)析
這篇文章主要介紹了Java設(shè)計(jì)模式中的裝飾器模式簡(jiǎn)析,裝飾模式能夠?qū)崿F(xiàn)動(dòng)態(tài)的為對(duì)象添加功能,是從一個(gè)對(duì)象外部來給對(duì)象添加功能,通常給對(duì)象添加功能,要么直接修改對(duì)象添加相應(yīng)的功能,要么派生對(duì)應(yīng)的子類來擴(kuò)展,抑或是使用對(duì)象組合的方式,需要的朋友可以參考下2023-12-12

