关于@ResponseBody 默认输出的误区的解答

网友投稿 227 2022-12-09


关于@ResponseBody 默认输出的误区的解答

背景

@ResponseBody 默认情况返回的数据格式是什么?所谓默认情况 后台接口不指定 produces MediaType

@Controller

public class DemoController {

@ResponseBody

@GetMapping(value = "/demo")

public DemoVO demo() {

return new DemoVO("lengleng", "123456");

}

}

使用百度搜索 @ResponseBody 排名第一的答案, @ResponseBody 的作用其实是将 java 对象转为 json 格式的数据。

正确答案

我们先来公布正确的答案。

@ResponseBody 的输出格式,默认情况取决于客户端的 Accept 请求头。

源码剖析

RequestResponseBodyMethodProcessor

public class RequestResponseBodyMethodProcessor {

// 处理 ResponseBody 标注的方法

@Override

public boolean supportsReturnType(MethodParameter returnType) {

return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||

returnType.hasMethodAnnotation(ResponseBody.class));

}

// 处理返回值

@Override

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,

ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {

mavContainer.setRequestHandled(true);

ServletServerHttpRequest inputMessage = createInputMessage(webRequest);

ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

// 处理返回值

writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);

}

}

writeWithMessageConverters

protected void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,

ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {

HttpServletRequest request = inputMessage.getServletRequest();

/KhgzzB/ 获取请求头中的目标资源类型

List acceptableTypes = getAcceptableMediaTypes(requeshttp://t);

// 获取接口指定支持的资源类型

List producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

// 获取能够输出资源类型

List mediaTypesToUse = new ArrayList<>();

for (MediaType requestedType : acceptableTypes) {

for (MediaType producibleType : producibleTypes) {

if (requestedType.isCompatibleWith(producibleType)) {

mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));

}

}

}

/// 排序

MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

for (MediaType mediaType : mediaTypesToUse) {

// 判断资源类型是否是具体的类型,而不是带通配符 * 这种

if (mediaType.isConcrete()) {

selectedMediaType = mediaType;

break;

}

else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {

selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;

break;

}

}

selectedMediaType = selectedMediaType.removeQualityValue();

// 查找支持选中资源类型的 HttpMessageConverter,输出body

for (HttpMessageConverter> converter : this.messageConverters) {

GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?

(GenericHttpMessageConverter>) converter : 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);

return;

}

}

}

为什么我要去研究这个问题

当升级至 spring cloud alibaba 2.2.1 时, sentinel 模块 引入以下依赖

当依赖中出现 dataformat jar 时候, RestTemplate ,会在默认 Accept 请求头增加

application/xml | text/xml | application/*+xml

public MappingJackson2XmlHttpMessageConverter(ObjectMapper objectMapper) {

super(objectMapper, new MediaType("application", "xml", StandardCharsets.UTF_8),

new MediaType("text", "xml", StandardCharsets.UTF_8),

new MediaType("application", "*+xml", StandardCharsets.UTF_8));

Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required");

}

当我们使用 RestTemplate 调用接口时候,若不指定 Accept 会返回 XML ,导致不能平滑升级


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Cloud Zuul路由网关服务过滤实现代码
下一篇:IDEA下因Lombok插件产生的Library source does not match the bytecode报错问题及解决方法(亲测可用)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~