关于@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 <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
                        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
  HttpServletRequest request = inputMessage.getServletRequest();
  // 获取请求头中的目标资源类型
  List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
  // 获取接口指定支持的资源类型
  List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
  // 获取能够输出资源类型
  List<MediaType> 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 ,导致不能平滑升级
优秀的个人博客,低调大师

微信关注我们

原文链接:https://yq.aliyun.com/articles/756425

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。