在《方法处理2-参数解析器》那一章,了解到获取body参数的解析器AbstractMessageConverterMethodArgumentResolver及其子类,并没有自己去解析request中的body参数,而是委托消息转换器HttpMessageConverter去解析body参数,并转换为目标方法的参数类型,本文稍微研究一下消息转换器;
消息转换器负责读取httpRequest请求中的body参数,并转换为目标方法的参数类型。并把目标方法的返回值写入到httpResponse中;
支持的数据格式 | 支持的参数类型 | 名称 | 支持的header类型 |
---|---|---|---|
字节 | Byte[] | ByteArrayHttpMessageConverter | 1.application/octet-stream类型读取和写入 2.*/*类型读取和写入 |
文件 | Resource | ResourceHttpMessageConverter | 1.*/*类型读取和写入 |
文本 | String | StringHttpMessageConverter | 1.text/plain类型读取和写入 2.*/*类型读取和写入 |
任意类型 | ObjectToStringHttpMessageConverter 先读取文本,然后使用ConversionService把文本转换为对象 | 1.text/plain类型读取和写入 | |
MultiValueMap | FormHttpMessageConverter 使用其它的消息转换器,把MultiValueMap返回值中包含的对象转换成字节/文本并写入响应 | 1.application/x-www-form-urlencoded类型读取和写入 2.multipart/form-data类型写入 3.multipart/mixed类型写入 4.*/*类型写入 | |
MultiValueMap | AllEncompassingFormHttpMessageConverter 使用其它的消息转换器,把MultiValueMap返回值中包含的对象转换成json/xml并写入响应 | ||
xml | Source | SourceHttpMessageConverter 使用jdk的api,把xml转换为Source对象 | 1.application/xml类型读取和写入 2.text/xml类型读取和写入 3.application/*+xml类型读取和写入 |
任意类型,类包含如下注解: 1.读取:@XmlType/@XmlRootElement 2.写入:@XmlRootElement | Jaxb2RootElementHttpMessageConverter 使用jakarta.xml,把xml转换为对象 | ||
任意类型 | MappingJackson2XmlHttpMessageConverter 使用jackson.xml,把json转换为对象 | ||
json | 任意类型 | FastJsonHttpMessageConverter 使用fastjson,把json转换为对象 | 1.*/*类型读取和写入 |
任意类型 | JsonbHttpMessageConverter 使用javax.json,把json转换为对象 | 1.application/json类型读取和写入 2.application/*+json类型读取和写入 | |
任意类型 | GsonHttpMessageConverter 使用google.gson,把json转换为对象 | ||
任意类型 | MappingJackson2HttpMessageConverter 使用jackson.json,把json转换为对象 |
消息转换器提供了两个接口HttpMessageConverter和GenericHttpMessageConverter,GenericHttpMessageConverter支持推断泛型类型。我们根据这两个接口把消息转换器分成两类:第一类是HttpMessageConverter及其子类,第二类为GenericHttpMessageConverter及其子类;
HttpMessageConverter是参数转换器的顶层接口,定义了参数转换器的行为,包括6个方法。分成三种类型,每种类型2个方法;
public interface HttpMessageConverter<T> {
//**第1类: 读取参数
//**是否支持读取目标参数类型和http contentType
boolean canRead(Class<?> clazz, MediaType mediaType);
//**读取http body数据,并转换为目标参数类型的对象
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
//**第2类: 写入参数
//**是否支持写入目标方法的返回值类型和http contentType
boolean canWrite(Class<?> clazz, MediaType mediaType);
//**把目标方法的返回值写入http response
void write(T t, MediaType contentType, HttpOutputMessage outputMessage);
//**第3类: 获取支持的contentType
//**获取支持的http contentType类型
List<MediaType> getSupportedMediaTypes();
//**根据目标参数类型获取支持的http contentType类型
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
}
FormHttpMessageConverter是HttpMessageConverter的直接子类,接收参数/返回值必须是MultiValueMap;
FormHttpMessageConverter支持application/x-www-form-urlencoded类型的读取(FormHttpMessageConverter及其子类AllEncompassingFormHttpMessageConverter是唯二可以读取x-www-form-urlencoded参数的实现类);
读取过程很简单(参数name=test1&sex=38name=test2),使用&和=切割,最后保存到MultiValueMap中,参数名和参数值都是字符串。同参数名可以有多个,所以值是数组;
FormHttpMessageConverter支持application/x-www-form-urlencodeda || multipart/form-data || multipart/mixed || */*类型的写入;
构造方法添加默认支持的contentType和用于转换multipart的消息转换器;
public FormHttpMessageConverter() {
//**添加支持的contentType
this.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
this.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
this.supportedMediaTypes.add(MediaType.MULTIPART_MIXED);
//**添加消息转换器,用于把MultiValueMap返回值中包含的对象转换成字节/文本并写入响应
this.partConverters.add(new ByteArrayHttpMessageConverter());
this.partConverters.add(new StringHttpMessageConverter());
this.partConverters.add(new ResourceHttpMessageConverter());
}
判断是否支持写入目标方法的返回值类型和http contentType;
@Override
public boolean canRead(Class> clazz, @Nullable MediaType mediaType) {
//**目标参数必须是MultiValueMap
if (!MultiValueMap.class.isAssignableFrom(clazz)) {
return false;
}
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
//**不支持包含multipart的contentType
if (supportedMediaType.getType().equalsIgnoreCase("multipart")) {
continue;
}
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
判断是是否multipart内容;
//**contentType包含multipart || contentType为null但是MultiValueMap返回值中的value有非字符串类型则认为内容是multipart
private boolean isMultipart(MultiValueMap map, @Nullable MediaType contentType) {
if (contentType != null) {
return contentType.getType().equalsIgnoreCase("multipart");
}
for (List> values : map.values()) {
for (Object value : values) {
if (value != null && !(value instanceof String)) {
return true;
}
}
}
return false;
}
如果是multipart内容,调用其它的消息转换器,把value转换后写入响应;
private void writePart(String name, HttpEntity> partEntity, OutputStream os) throws IOException {
Object partBody = partEntity.getBody();
if (partBody == null) {
throw new IllegalStateException("Empty body for part '" + name + "': " + partEntity);
}
Class> partType = partBody.getClass();
HttpHeaders partHeaders = partEntity.getHeaders();
MediaType partContentType = partHeaders.getContentType();
for (HttpMessageConverter> messageConverter : this.partConverters) {
if (messageConverter.canWrite(partType, partContentType)) {
Charset charset = isFilenameCharsetSet() ? StandardCharsets.US_ASCII : this.charset;
HttpOutputMessage multipartMessage = new MultipartHttpOutputMessage(os, charset);
multipartMessage.getHeaders().setContentDispositionFormData(name, getFilename(partBody));
if (!partHeaders.isEmpty()) {
multipartMessage.getHeaders().putAll(partHeaders);
}
((HttpMessageConverter
public abstract class AbstractHttpMessageConverter implements HttpMessageConverter {
private List supportedMediaTypes = Collections.emptyList();
//**是否支持读取目标参数类型/写入目标方法返回值类型
protected abstract boolean supports(Class> clazz);
//**读取http body数据,并转换为目标参数类型的对象
protected abstract T readInternal(Class extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
//**把目标方法的返回值写入http body
protected abstract void writeInternal(T t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConverter
public interface GenericHttpMessageConverter extends HttpMessageConverter {
//**第1类: 读取参数
//**是否支持读取目标参数类型和http contentType
boolean canRead(Type type, @Nullable Class> contextClass, @Nullable MediaType mediaType);
//**读取http body数据,并转换为目标参数类型的对象
T read(Type type, @Nullable Class> contextClass, HttpInputMessage inputMessage);
//**第2类: 写入参数
//**是否支持写入目标方法的返回值类型和http contentType
boolean canWrite(@Nullable Type type, Class> clazz, @Nullable MediaType mediaType);
//**把目标方法的返回值写入http response
void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}
public abstract class AbstractGenericHttpMessageConverter extends AbstractHttpMessageConverter
implements GenericHttpMessageConverter {
//**GenericHttpMessageConverter未实现的接口:读取http body数据,并转换为目标参数类型的对象
T read(Type type, @Nullable Class> contextClass, HttpInputMessage inputMessage);
//**AbstractHttpMessageConverter的抽象方法:读取http body数据,并转换为目标参数类型的对象
protected abstract T readInternal(Class extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
//**自己的抽象方法:把目标方法的返回值写入http response
protected abstract void writeInternal(T t, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
public abstract class AbstractJsonHttpMessageConverter extends AbstractGenericHttpMessageConverter