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读取指定类型的消息。

posted @ 2022-11-12 20:46  shigp1  阅读(252)  评论(0编辑  收藏  举报