SpringMVC-解析@RequestBody
@RequestBody标注的参数由RequestResponseBodyMethodProcessor解析。
RequestResponseBodyMethodProcessor.supportsParameter(MethodParameter parameter)
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
RequestResponseBodyMethodProcessor解析@RequestBody标注的参数。
RequestResponseBodyMethodProcessor.resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
RequestResponseBodyMethodProcessor.readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType)
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
调用messageConverter转换消息。
AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType)
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType,
getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
}
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});
return body;
}
遍历messageConverters,调用消息转换器的canRead判断messageConverter是否可读取消息体。如果可以读取调用read读取消息。
GenericHttpMessageConverter类型的消息转换器是通过MediaType来判断是否可读取消息。其他消息转换器是通过参数类型和MediaType来判断是否可读取消息。
MappingJackson2HttpMessageConverter和ResourceRegionHttpMessageConverter是GenericHttpMessageConverter类型。MappingJackson2HttpMessageConverter支持的MediaType是application/json和application/*+json。ResourceRegionHttpMessageConverter不支持读取消息。
StringHttpMessageConverter支持的参数类型是String,支持的MediaType是text/plain和MediaType.ALL(所有类型)。StringHttpMessageConverter默认编码是ISO-8859-1。可能是中文乱码产生的根源。要解决编码引起的乱码,可配置:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>text/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
ByteArrayHttpMessageConverter支持的参数类型是byte[],支持的MediaType是application/octet-stream和MediaType.ALL(所有类型)。
ResourceHttpMessageConverter支持的参数类型是Resource,支持的MediaType是MediaType.ALL(所有类型)。
SourceHttpMessageConverter支持的参数类型是DOMSource,SAXSource,StAXSource,StreamSource,Source,支持的MediaType是application/xml,text/xml,application/*-xml。
AllEncompassingFormHttpMessageConverter不支持的参数类型是MultiValueMap,支持的MediaType是application/x-www-form-urlencoded,multipart/form-data,multipart/mixed,默认编码是utf-8。
@RequestBody标注的参数由MappingJackson2HttpMessageConverter进行读取。
MappingJackson2HttpMessageConverter的父类AbstractJackson2HttpMessageConverter.read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(type, contextClass);
return readJavaType(javaType, inputMessage);
}
AbstractJackson2HttpMessageConverter.readJavaType(JavaType javaType, HttpInputMessage inputMessage)
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
MediaType contentType = inputMessage.getHeaders().getContentType();
Charset charset = getCharset(contentType);
ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
boolean isUnicode = ENCODINGS.containsKey(charset.name()) ||
"UTF-16".equals(charset.name()) ||
"UTF-32".equals(charset.name());
try {
if (inputMessage instanceof MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if (deserializationView != null) {
ObjectReader objectReader = objectMapper.readerWithView(deserializationView).forType(javaType);
if (isUnicode) {
return objectReader.readValue(inputMessage.getBody());
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectReader.readValue(reader);
}
}
}
if (isUnicode) {
return objectMapper.readValue(inputMessage.getBody(), javaType);
}
else {
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectMapper.readValue(reader, javaType);
}
}
catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);
}
}
调用objectMapper.readValue读取指定类型的消息。