详解自定义SpringMVC的Http信息转换器的使用

网友投稿 573 2023-03-10


详解自定义SpringMVC的Http信息转换器的使用

在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制。使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上。

HttpInputMessage

这个类是SpringMVC内部对一次Http请求报文的抽象,在HttpMessageConverter的read()方法中,有一个HttpInputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“请求消息”的内部抽象,消息转换器从“请求消息”中按照规则提取消息,转换为方法形参中声明的对象。

package org.springframework.http;

import java.io.IOExcepthttp://ion;

import java.io.InputStream;

public interface HttpInputMessage extends HttpMessage {

InputStream getBody() throws IOException;

}

HttpOutputMessage

在HttpMessageConverter的write()方法中,有一个HttpOutputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“响应消息”的内部抽象,消息转换器将“响应消息”按照一定的规则写到响应报文中。

package org.springframework.http;

import java.io.IOException;

import java.io.OutputStream;

public interface HttpOutputMessage extends HttpMessage {

OutputStream getBody() throws IOException;

}

HttpMessageConverter

/*

* Copyright 2002-2010 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.http.converter;

import java.io.IOException;

import java.util.List;

import org.springframehttp://work.http.HttpInputMessage;

import org.springframework.http.HttpOutputMessage;

import org.springframework.http.MediaType;

public interface HttpMessageConverter {

boolean canRead(Class> clazz, MediaType mediaType);

boolean canWrite(Class> clazz, MediaType mediaType);

List getSupportedMediaTypes();

T read(Class extends T> clazz, HttpInputMessage inputMessage)

throws IOException, HttpMessageNotReadableException;

void write(T t, MediaType contentType, HttpOutputMessage outputMessage)

throws IOException, HttpMessageNotWritableException;

}

HttpMessageConverter 接口提供了5个方法:

canRead :判断该转换器是否能将请求内容转换成Java对象

canWrite :判断该转换器是否可以将Java对象转换成返回内容

getSupportedMediaTypes :获得该转换器支持的MediaType类型

read :读取请求内容并转换成Java对象

write :将Java对象转换后写入返回内容

其中 read 和 write 方法的参数分别有有 HttpInputMessage 和 HttpOutputMessage 对象,这两个对象分别代表着一次Http通讯中的请求和响应部分,可以通过 getBody 方法获得对应的输入流和输出流。

当前Spring中已经默认提供了相当多的转换器,分别有:

名称

作用

读支持MediaType

写支持MediaType

ByteArrayHttpMessageConverter

数据与字节数组的相互转换

/

application/octet-stream

StringHttpMessageConverter

数据与String类型的相互转换

text/*

text/plain

FormHttpMessageConverter

表单与MultiValueMap的相互转换

application/x-www-form-urlencoded

applicatijDFOFCrBon/x-www-form-urlencoded

SourceHttpMessageConverter

数据与javax.xml.transform.Source的相互转换

text/xml和application/xml

text/xml和application/xml

MarshallingHttpMessageConverter

使用SpringMarshaller/Unmarshaller转换XML数据

text/xml和application/xml

text/xml和application/xml

MappingJackson2HttpMessageConverter

使用Jackson的ObjectMapper转换json数据

application/json

application/json

MappingJackson2XmlHttpMessageConverter

使用Jackson的XmlMapper转换XML数据

application/xml

application/xml

BufferedImageHttpMessageConverter

数据与java.awt.image.BufferedImage的相互转换

Java I/O API支持的所有类型

Java I/O API支持的所有类型

HttpMessageConverter匹配过程:

@RequestBody注解时: 根据Request对象header部分的Content-Type类型,逐一匹配合适的HttpMessageConverter来读取数据。

private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType) throws Exception {

MediaType contentType = inputMessage.getHeaders().getContentType();

if (contentType == null) {

StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));

String paramName = methodParam.getParameterName();

if (paramName != null) {

builder.append(' ');

builder.append(paramName);

}

throw new HttpMediaTypeNotSupportedException("Cannot extract parameter (" + builder.toString() + "): no Content-Type found");

}

List allSupportedMediaTypes = new ArrayList();

if (this.messageConverters != null) {

for (HttpMessageConverter> messageConverter : this.messageConverters) {

allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());

if (messageConverter.canRead(paramType, contentType)) {

if (logger.isDebugEnabled()) {

logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" + messageConverter + "]");

}

return messageConverter.read(paramType, inputMessage);

}

}

}

throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);

}

@ResponseBody注解时:根据Request对象header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历找到能处理的HttpMessageConverter。

private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage, HttpOutputMessage outputMessage)

throws IOException, HttpMediaTypeNotAcceptableException {

List acceptedMediaTypes = inputMessage.getHeaders().getAccept();

if (acceptedMediaTypes.isEmpty()) {

acceptedMediaTypes = Collections.singletonList(MediaType.ALL);

}

MediaType.sortByQualityValue(acceptedMediaTypes);

Class> returnValueType = returnValue.getClass();

List allSupportedMediaTypes = new ArrayList();

if (getMessageConverters() != null) {

for (MediaType acceptedMediaType : acceptedMediaTypes) {

for (HttpMessageConverter messageConverter : getMessageConverters()) {

if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {

messageConverter.write(returnValue, acceptedMediaType, outputMessage);

if (logger.isDebugEnabled()) {

MediaType contentType = outputMessage.getHeaders().getContentType();

if (contentType == null) {

contentType = acceptedMediaType;

}

logger.debug("Written [" + returnValue + "] as \"" + contentType +

"\" using [" + messageConverter + "]");

}

this.responseArgumentUsed = true;

return;

}

}

}

for (HttpMessageConverter messageConverter : messageConverters) {

allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());

}

}

throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);

}

自定义一个JSON转换器

class CustomJsonHttpMessageConverter implements HttpMessageConverter {

//Jackson的Json映射类

private ObjectMapper mapper = new ObjectMapper();

//该转换器的支持类型:application/json

private List supportedMediaTypes = Arrays.asList(MediaType.APPLICATION_JSON);

/**

* 判断转换器是否可以将输入内容转换成Java类型

* @param clazz 需要转换的Java类型

* @param mediaType 该请求的MediaType

* @return

*/

@Override

public boolean canRead(Class clazz, MediaType mediaType) {

if (mediaType == null) {

return true;

}

for (MediaType supportedMediaType : getSupportedMediaTypes()) {

if (supportedMediaType.includes(mediaType)) {

return true;

}

}

return false;

}

/**

* 判断转换器是否可以将Java类型转换成指定输出内容

* @param clazz 需要转换的Java类型

* @param mediaType 该请求的MediaType

* @return

*/

@Override

public boolean canWrite(Class clazz, MediaType mediaType) {

if (mediaType == null || MediaType.ALL.equals(mediaType)) {

return true;

}

for (MediaType supportedMediaType : getSupportedMediaTypes()) {

if (supportedMediaType.includes(mediaType)) {

return true;

}

}

return false;

}

/**

* 获得该转换器支持的MediaType

* @return

*/

@Override

public List getSupportedMediaTypes() {

return supportedMediaTypes;

}

/**

* 读取请求内容,将其中的Json转换成Java对象

* @param clazz 需要转换的Java类型

* @param inputMessage 请求对象

* @return

* @throws IOException

* @throws HttpMessageNotReadableException

*/

@Override

public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

return mapper.readValue(inputMessage.getBody(), clazz);

}

/**

* 将Java对象转换成Json返回内容

* @param o 需要转换的对象

* @param contentType 返回类型

* @param outputMessage 回执对象

* @throws IOException

* @throws HttpMessageNotWritableException

*/

@Override

public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

mapper.writeValue(outputMessage.getBody(), o);

}

}

自定义MappingJackson2HttpMessage

从 MappingJackson2HttpMessageConverter 的父类 AbstractHttpMessageConverter 中的 write 方法可以看出,该方法通过 writeInternal 方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:

@Bean

public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {

return new MappingJackson2HttpMessageConverter() {

//重写writeInternal方法,在返回内容前首先进行加密

@Override

protected void writeInternal(Object object,

HttpOutputMessage outputMessage) throws IOException,

HttpMessageNotWritableException {

//使用Jackson的ObjectMapper将Java对象转换成Json String

ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(object);

LOGGER.error(json);

//加密

String result = json + "加密了!";

LOGGER.error(result);

//输出

outputMessage.getBody().write(result.getBytes());

}

};

}

在这之后还需要将这个自定义的转换器配置到Spring中,这里通过重写 WebMvcConfigurer 中的 configureMessageConverters 方法添加自定义转换器:

//添加自定义转换器

@Override

public void configureMessageConverters(List> converters) {

converters.add(mappingJackson2HttpMessageConverter());

super.configureMessageConverters(converters);

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:基于单片机usb接口设计(单片机usb电路)
下一篇:远程路由器管理员密码(路由器 远程管理)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~