Misunderstanding about the default output of @ResponseBody

background

  • What is the format of the data returned by @ResponseBody by default? The so-called default background interface does not specify Produces MediaType
@Controller
public class DemoController {
  @ResponseBody
  @GetMapping(value = "/demo")
  public DemoVO demo() {
    return new DemoVO("lengleng", "123456");
  }
}
复制代码
  • Using Baidu to search for @ ResponseBody's number one answer, @ ResponseBody's role is actually to convert java objects to json formatted data.

correct answer

Let's first publish the correct answer.

@ResponseBody output format, depending on the client's default Acceptrequest header.

Source code analysis

  • 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; } } } 复制代码

Why should I study this problem

  • When upgrading to spring cloud alibaba 2.2.1 time, sentinel introduce the following dependent module
  • When the dataformat jar appears in the dependency, RestTemplate will increase in the default Accept request header

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");
}
复制代码
  • When we use RestTemplate to call the interface, XML will be returned if Accept is not specified, resulting in a smooth upgrade
image
image

Guess you like

Origin juejin.im/post/5e93f6b6e51d4546f27ff50a