文章目录
组件
HttpMessageConverter
处理HTTP请求和响应的转换器。
public interface HttpMessageConverter<T> {
// 对于该Media类型是否可读
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
// 获得当前消息转换器支持的类型
List<MediaType> getSupportedMediaTypes();
// 读写转换,将request或response中的参数转换为对应类型
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
AbstractHttpMessageConverter
HttpMessageConverter接口的基础实现
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
// 消息转换器支持的Content-Type
private List<MediaType> supportedMediaTypes = Collections.emptyList();
// 默认编码
private Charset defaultCharset;
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
// 是否支持类型和content-type
return supports(clazz) && canRead(mediaType);
}
// 开始read时转换
public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return readInternal(clazz, inputMessage);
}
}
MediaType
添加了对HTTP规范中定义的content-type的支持。
public class MediaType extends MimeType implements Serializable {
public static final String ALL_VALUE = "*/*";
public static final String APPLICATION_JSON_VALUE = "application/json";
}
RequestResponseBodyMethodProcessor
处理@RequestBody和@ResponseBody注解的参数转换和返回值转换器,根据MediaType来选择消息转换器处理。
// 支持方法POST PUT PATCH
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
// 持有的消息转换器
protected final List<HttpMessageConverter<?>> messageConverters;
// 支持的媒体类型
protected final List<MediaType> allSupportedMediaTypes;
// 事件通知
private final RequestResponseBodyAdviceChain advice;
}
源码分析
以RequestResponseBodyMethodProcessor为例,分析参数解析和返回值解析过程
参数解析
- 调用resolveArgument方法,通过消息转换器来解析request中的参数,返回解析后的参数值,如果有校验也在这进行。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 通过消息转换器将request中数据转换为所需的java类型
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());
}
}
// 将校验结果放入model中
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
// 返回适配Optional后的参数值
return adaptArgumentIfNecessary(arg, parameter);
}
- 将request请求装配为ServletServerHttpRequest,调用readWithMessageConverters方法解析参数。
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
// 获得httpRequest请求
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 将Request请求装配为ServletServerHttpRequest
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
// 真正解析方法
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
// 如果参数为null且必要则抛异常
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}
- 将请求体内容转换为java类型,首先获得请求的content-type(默认使用application/octet-stream),遍历消息转换器找到支持当前content-type和参数类型的消息转换器来解析请求体
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
// 获得request的content-type
contentType = inputMessage.getHeaders().getContentType();
}
···无效类型异常
// 如果请求未传送content-type,则使用application/octet-stream
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
// 获得当前参数所在的类
Class<?> contextClass = parameter.getContainingClass();
// 参数类型
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
// 泛型的情况下参数类型为null,解析参数类型
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
// 获得请求方法POST|GET
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);
// 消息转换器支持content-type和参数类型(优先使用支持泛型)
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);
// 请求体转换为对应的targetType类型
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;
}
}
}
···抛出io异常
// 如果请求体为空
if (body == NO_VALUE) {
// 如果method为null 或 不支持请求method 或 请求体为空且noContentType为true 返回null,否则抛出异常
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
···日志记录
return body;
}
返回值解析
- 封装request和response,调用消息转换器处理返回值
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 设置当前请求已被处理,不再进行视图渲染
mavContainer.setRequestHandled(true);
// 封装请求和响应
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// 调用消息转换器
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
- 处理返回值,判断返回值是否为Resource类型,通过accept和消息转换器支持的content-type选择合适的content-type作为响应头,通过消息转换器解析返回值(outputStream.flush响应客户端)
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
// 如果参数值为字符类型,设置返回值和返回值类型
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
// 如果返回值为资源类型
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
// 获得返回值类型,content-type
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
// 如果response中设置的返回值类型
selectedMediaType = contentType;
}
else {
// 未设置返回值
HttpServletRequest request = inputMessage.getServletRequest();
// 获得请求头的accept
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// 可返回类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
// 如果返回值不为null且可提供content-type为空抛出异常
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
// 获得可用的content-type
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
// 可用类型为null处理
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
// 选择合适的content-type
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 遍历消息转换器,选择合适的转换器解析返回值
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);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
···日志
}
return;
}
}
}
// 返回值不为空,且未被消息转换器处理
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}