基于SpringBoot自定義接口響應(yīng)消息格式及原理解析
環(huán)境:Springboot3.0.5
消息格式轉(zhuǎn)換原理
在默認(rèn)情況下,RestController接口返回的數(shù)據(jù)格式是json,如下接口:
@RestController
@RequestMapping("/rmf")
public class ResponseMessageFormatController {
@GetMapping("/index")
public Users index() {
return new Users(1, "張颯", 66, "男") ;
}
}
接口返回
接口默認(rèn)返回了json數(shù)據(jù)格式
通過Postman,我這里是沒有在Header中添加Accept。當(dāng)后臺(tái)在處理該請(qǐng)求時(shí)會(huì)認(rèn)為你能接收任意類型的數(shù)據(jù)格式也就是MediaType=*/*,緊接著會(huì)獲取當(dāng)前所有的HttpMessageConverter支持的MediaType,然后遍歷所有的MediaType,找到第一個(gè)類型中只要不包含'*'的MediaType就直接返回,默認(rèn)情況下application/json排在第一個(gè),所以最終確定返回客戶端的數(shù)據(jù)格式將會(huì)是application/json,最后再遍歷所有的HttpMessageConverter找出支持applicatioin/json類型的HttpMessageConverter,最終確定了MappingJackson2HttpMessageConverter直接進(jìn)行客戶端的輸出。
核心源碼:
public abstract class AbstractMessageConverterMethodProcessor {
protected <T> void writeWithMessageConverters(...) {
List<MediaType> acceptableTypes;
try {
// 獲取請(qǐng)求header中的Accept,如果請(qǐng)求中沒有,則返回 */* 接受任意類型
acceptableTypes = getAcceptableMediaTypes(request);
}
// 獲取當(dāng)前容器中所有的HttpMessageConverter支持的MediaType
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
List<MediaType> compatibleMediaTypes = new ArrayList<>();
// 確定具體輸出的應(yīng)該是那種消息格式,
determineCompatibleMediaTypes(acceptableTypes, producibleTypes, compatibleMediaTypes);
// 遍歷
for (MediaType mediaType : compatibleMediaTypes) {
// 判斷當(dāng)前的MediaType的類型[type] 和 子類型 [subtype] 中分別不是 * 和 *+
// 就符合條件,找到第一個(gè)直接返回,這就確定了將會(huì)給客戶端返回的消息格式
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 遍歷所有的HttpMessageConverter,是否有能支持上面確定的selectedMediaType
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter =(converter instanceof GenericHttpMessageConverter ghmc ? ghmc : null);
if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage);
// 輸出結(jié)果
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
}
}
}
以上是關(guān)于消息處理的部分源碼分析。
返回XML格式
如果需要返回xml格式的數(shù)據(jù),我們只需引入下面依賴即可。
引入依賴:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
postman進(jìn)行測(cè)試
正確的返回了xml格式
為什么引入上面的依賴后就可以直接通過設(shè)置Accept為application/xml就可以返回xml格式呢?
原理:
// 在這導(dǎo)入的JacksonHttpMessageConvertersConfiguration類中進(jìn)行了配置
@Import({ JacksonHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
class JacksonHttpMessageConvertersConfiguration {
@Configuration(proxyBeanMethods = false)
// 當(dāng)前的類路徑下有XmlMapper類,該類就在上面引入的包中
@ConditionalOnClass(XmlMapper.class)
@ConditionalOnBean(Jackson2ObjectMapperBuilder.class)
protected static class MappingJackson2XmlHttpMessageConverterConfiguration {
@Bean
@ConditionalOnMissingBean
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter(Jackson2ObjectMapperBuilder builder) {
return new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build());
}
}
}
當(dāng)當(dāng)前的類路徑下有XmlMapper就會(huì)自動(dòng)的創(chuàng)建處理XML格式的MappingJackson2XmlHttpMessageConverter
自定義消息格式
如果客戶端要求接收的數(shù)據(jù)格式是yaml格式,這時(shí)候就需要自定義HttpMessageConverter
引入依賴:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
自定義HttpMessageConverter:
@Component
public class YamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
public YamlHttpMessageConverter() {
super(new MediaType("application", "yaml")) ;
}
@Override
protected boolean supports(Class<?> clazz) {
return true ;
}
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return null ;
}
@Override
protected void writeInternal(Object t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try (OutputStream os = outputMessage.getBody()) {
YAMLFactory factory = new YAMLFactory();
// 該配置作用就是:去掉開頭的三個(gè) '---'
factory.configure(Feature.WRITE_DOC_START_MARKER, false) ;
ObjectMapper mapper = new ObjectMapper(factory) ;
os.write(mapper.writeValueAsString(t).getBytes(StandardCharsets.UTF_8)) ;
}
}
}
測(cè)試
正確的返回了yaml格式