SpringBoot2(十三)HttpMessageConverter
HttpMessageConverter可以根据不同的数据类型(json,xml 等等),做不同的数据加工,一个项目可以设置多个不同的HttpMessageConverter;
HttpMessageConverter单独看,可能不理解它是什么,但是这个接口相关的开源框架,我们就非常熟悉了:Jackson、FastJSON。
注意:
本文的代码设计其实是失败的,仅供学习使用,实际生产中,不需要自己定义,因为已经有jackson、FastJSON,并不需要自己写一个更牛的;
也不要轻易改动这些框架的源码,如果集成了其它公司的产品,我们的HttpMessageConverter有可能顺带地改掉他们的数据格式,会给维护带来额外负担。
案例:
同样使用这个案例:对你的Controller返回值进行一个二次封装。
如下:code是结果码(1、成功,0、失败,2、未登录...),data为携带数据
{"code":"1","data":{"name":"xiaoming","age":"30"}}
GenericHttpMessageConverter
代码改编自com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter,此接口负责 Controller 的数据包装,并且最后写回到客户端。
为了满足上述需求,这是我最早提出的解决方案,直接改动 jar 包中的 writeInternal() 函数。
如果只是公司内部用,的确很开心,后来项目加入带前端界面的jar包,而HttpMessageConverter 是全局有效的,我们的 Converter 影响到了他们的功能。
HandlerMethodReturnValueHandler 和 ResponseBodyAdvice 两个接口,可以通过包名进行过滤,只对部分代码生效,更适合用于解决上述问题。
import cn.seaboot.common.core.FastJsonUtils;
import cn.seaboot.plugin.util.Result;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
/**
* 改造FastJson,所有返回值进行二次封装
* @author Mr.css
* @date 2018年7月15日 上午12:11:14 v1
* 2019年10月16日 上午11:08 v2
*/
public class FastJsonConverter extends AbstractHttpMessageConverter<Object> implements GenericHttpMessageConverter<Object> {
private FastJsonConfig fastJsonConfig;
public FastJsonConverter() {
super(MediaType.ALL);
this.fastJsonConfig = new FastJsonConfig();
this.fastJsonConfig.setCharset(Charset.defaultCharset());
//FastJsonUtils代码未全部给出,参考FastJson配置
this.fastJsonConfig.setSerializeConfig(FastJsonUtils.serializeConfig);
this.fastJsonConfig.setSerializerFeatures(FastJsonUtils.features);
}
@Override
protected boolean supports(Class<?> clazz) {
return true;
}
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
InputStream in = inputMessage.getBody();
return JSON.parseObject(in, this.fastJsonConfig.getCharset(), clazz, this.fastJsonConfig.getFeatures());
}
/**
* 重点改造这一函数
*/
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
OutputStream out;
String text;
if (obj instanceof Integer) {
//Result做返回值二次封装的工具类,这里直接省略与之相关的代码,按照自己的需求封装即可
//如果是Integer 类型,我们当做数据库受影响行数处理,大于0直接返回“操作成功”
text = Result.doUpdate((int) obj);
} else if (obj instanceof String) {
//如果是字符串类型,则直接原样返回
text = obj.toString();
} else {
//如果是Java对象,通过工具类处理
text = Result.succeed(obj);
}
out = outputMessage.getBody();
out.write(text.getBytes(this.fastJsonConfig.getCharset()));
if(this.fastJsonConfig.isWriteContentLength()){
headers.setContentLength((long) text.length());
}
outnew.close();
}
@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
return super.canRead(contextClass, mediaType);
}
@Override
public boolean canWrite(Type type, Class<?> contextClass, MediaType mediaType) {
return super.canWrite(contextClass, mediaType);
}
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
InputStream in = inputMessage.getBody();
return JSON.parseObject(in, this.fastJsonConfig.getCharset(), type, this.fastJsonConfig.getFeatures());
}
@Override
public void write(Object t, Type type, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
if (headers.getContentType() == null) {
if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
contentType = this.getDefaultContentType(t);
}
if (contentType != null) {
headers.setContentType(contentType);
}
}
if (headers.getContentLength() == -1L) {
Long contentLength = this.getContentLength(t, headers.getContentType());
if (contentLength != null) {
headers.setContentLength(contentLength.longValue());
}
}
this.writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
}
Spring中的使用
因为是代码改造,配置与fastjson的配置完全一样。
<!-- 配置与FastJsonHttpMessageConverter完全相同 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="xxxxxxxxxxx.FastJsonConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
SpringBoot的使用
import cn.seaboot.common.core.Resource;
import cn.seaboot.plugin.config.ArgumentResolver;
import cn.seaboot.plugin.config.FastJsonConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @author Created by 12614 on 2018/5/11.
*/
@Configuration
public class ApplicationConfigurer implements WebMvcConfigurer {
private Logger logger = LoggerFactory.getLogger(ApplicationConfigurer.class);
/**
* JSON解析
* @param converters -
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//去除不需要的配置
String[] exclude = {
"org.springframework.http.converter.xml"
, "org.springframework.http.converter.json"};
Iterator<HttpMessageConverter<?>> ite = converters.iterator();
while (ite.hasNext()){
HttpMessageConverter<?> converter = ite.next();
String name = converter.getClass().getName();
for (String str: exclude) {
if(name.startsWith(str)){
ite.remove();
break;
}
}
}
FastJsonConverter fastConverter = new FastJsonConverter();
// 处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 提高优先级,放到集合开头
converters.set(0, fastConverter);
}
}