一 介绍
HttpMessageConveter 是用来处理request 和response 里的数据的。Spring 为我们内置了大量的HttpMessageConverter
,例如, MappingJackson2HttpMessageConverter 、StringHttpMessageConverter 等。例如:我们实现一个自定义的HttpMessageConverter,他将接收以“-”号连接的数据,将数据转换成自定义的对象。
是 Spring3.0 新添加的一个接 口,负责将请求信息转换为一个对象(类型为 T),将对象( 类型为 T)输出为响应信息
- Boolean
canRead(
Class<?> clazz,MediaType mediaType): 指定转换器 可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对 象,同时指定支持 MIME 类型(text/html,applaiction/json等) – Boolean -
canWrite
(Class<?> clazz,MediaType mediaType):指定转换器 是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型 在MediaType 中定义。 – LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类 型。 - T
read
(Class<? extends T> clazz,HttpInputMessage inputMessage): 将请求信息流转换为 T 类型的对象。 - void
write
(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类 型为 contentType。
二 实践
实现自定义HttpMessageConvert
这个类得作用是将前端通过表单(如x-www-form-urlencoded
)提交得key-value
的数据;我们后台可以通过@ResponseBody
来获取。
这里的key是固定的,query
或-model
.
比如用户提交了xx?query={'name':"xw","age":"12"}
,我们后台可以处理这样的请求。
转换class类型或者ParameterizedType类型
package net.zjsos.core.common.web.config;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.context.request.ServletWebRequest;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import net.zjsos.core.common.base.util.SessionUtils;
import net.zjsos.core.common.util.Jackson;
import net.zjsos.core.common.util.StringUtils;
public class FormHttpMessageConvertor implements GenericHttpMessageConverter<Object> {
private List<MediaType> list = new ArrayList<>();
{
//在程序初始化时spring mvc 会查询所有消息转换支持的MediaType,如果其他的消息转换器已经有了下面MediaType,可以不写
list.add(MediaType.APPLICATION_FORM_URLENCODED);
list.add(MediaType.APPLICATION_JSON);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return list;
}
@Override
public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
}
@Override
public boolean canRead(Type type, Class contextClass, MediaType mediaType) {
// if(!(type instanceof Class)) {//泛型不是class
// return false;
// }
if(!(type instanceof ParameterizedType || type instanceof Class)) {//非class类型或者ParameterizedType类型
return false;
}
ServletWebRequest request = SessionUtils.getServletWebRequest();
String content = request.getParameter("_model");
if(!StringUtils.empty(content))
return true;
content = request.getParameter("query");
if(!StringUtils.empty(content))
return true;
return false;
}
@Override
public Object read(Type type, Class contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
ServletWebRequest request = SessionUtils.getServletWebRequest();
String content = request.getParameter("_model");
if(!StringUtils.empty(content)) {
// return Json.fromJson(content, type);
JavaType javaType = getJavaType(type,contextClass);
return Jackson.toObject(content, javaType);
}
content = request.getParameter("query");
if(!StringUtils.empty(content)) {
// return Json.fromJson(content, type);
JavaType javaType = getJavaType(type,contextClass);
return Jackson.toObject(content, javaType);
}
return null;
}
@Override
public boolean canWrite(Type type, Class clazz, MediaType mediaType) {
return false;
}
@Override
public void write(Object t, Type type, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
}
@Override
public boolean canRead(Class clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType) {
return false;
}
@Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
protected JavaType getJavaType(Type type, Class<?> contextClass) {
TypeFactory typeFactory = Jackson.getTypeFactory();
if (contextClass != null) {
ResolvableType resolvedType = ResolvableType.forType(type);
if (type instanceof TypeVariable) {
ResolvableType resolvedTypeVariable = resolveVariable(
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
if (resolvedTypeVariable != ResolvableType.NONE) {
return typeFactory.constructType(resolvedTypeVariable.resolve());
}
}
else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve();
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
return typeFactory.constructType(ResolvableType.
forClassWithGenerics(resolvedType.getRawClass(), generics).getType());
}
}
return typeFactory.constructType(type);
}
private ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) {
ResolvableType resolvedType;
if (contextType.hasGenerics()) {
resolvedType = ResolvableType.forType(typeVariable, contextType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
ResolvableType superType = contextType.getSuperType();
if (superType != ResolvableType.NONE) {
resolvedType = resolveVariable(typeVariable, superType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
for (ResolvableType ifc : contextType.getInterfaces()) {
resolvedType = resolveVariable(typeVariable, ifc);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
return ResolvableType.NONE;
}
}
转换TypeVariable
/**
* 该转换器应该放在FormHttpMessageConvertor后面,不然ParameterizedType类型的参数会序列化失败
*
* @author zyc
*
*/
public class TypeVariableConverter implements HttpMessageConverter<Object> {
private List<MediaType> list = new ArrayList<>();
{
// 在程序初始化时spring mvc 会查询所有消息转换支持的MediaType,如果其他的消息转换器已经有了下面MediaType,可以不写
list.add(MediaType.APPLICATION_FORM_URLENCODED);
list.add(MediaType.APPLICATION_JSON);
}
// 经过FormHttpMessageConvertor筛选后,仍然返回true的参数的targettype肯定不是class类型
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
ServletWebRequest request = SessionUtils.getServletWebRequest();
String content = request.getParameter("_model");
if (!StringUtils.empty(content))
return true;
content = request.getParameter("query");
if (!StringUtils.empty(content))
return true;
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return list;
}
@Override
public Object read(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
ServletWebRequest request = SessionUtils.getServletWebRequest();
String content = request.getParameter("_model");
if (!StringUtils.empty(content)) {
return Json.fromJson(content, clazz);
}
content = request.getParameter("query");
if (!StringUtils.empty(content)) {
return Json.fromJson(content, clazz);
}
return null;
}
@Override
public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
}
}
配置
@Configuration
@EnableWebMvc
@ComponentScan("com.example.spring.framework.converter")
public class MyMvcConfig extends WebMvcConfigurerAdapter {
...
/**
* 配置自定义的HttpMessageConverter 的Bean ,在Spring MVC 里注册HttpMessageConverter有两个方法:
* 1、configureMessageConverters :重载会覆盖掉Spring MVC 默认注册的多个HttpMessageConverter
* 2、extendMessageConverters :仅添加一个自定义的HttpMessageConverter ,不覆盖默认注册的HttpMessageConverter
* 在这里重写extendMessageConverters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
}
三 RequestBodyAdvice
上面两个HttpMesageConvert在请求的content-type不支持的情况下,需要一个统一处理;转换失败后的统一处理,只需要重写handleEmptyBody
.不影响其他正常的@RequestBody注解使用
package net.zjsos.core.common.web.advice;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import net.zjsos.core.common.base.util.SessionUtils;
import net.zjsos.core.common.util.Jackson;
import net.zjsos.core.common.util.StringUtils;
import net.zjsos.core.common.web.config.FormHttpMessageConvertor;
import net.zjsos.core.common.web.config.TypeVariableConverter;
@ControllerAdvice
public class NullBodyAdvice implements RequestBodyAdvice{
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
if(FormHttpMessageConvertor.class == converterType || TypeVariableConverter.class == converterType)
return true;
return false;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
try {
ServletWebRequest request = SessionUtils.getServletWebRequest();
Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
JavaType javatype = getJavaType(targetType,contextClass);
String content = request.getParameter("_model");
if(!StringUtils.empty(content)) {
return Jackson.toObject(content, javatype);
}
content = request.getParameter("query");
if(!StringUtils.empty(content)) {
return Jackson.toObject(content, javatype);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
// TODO 原样返回
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// TODO 不修改原值
return body;
}
protected JavaType getJavaType(Type type, Class<?> contextClass) {
TypeFactory typeFactory = Jackson.getTypeFactory();
if (contextClass != null) {
ResolvableType resolvedType = ResolvableType.forType(type);
if (type instanceof TypeVariable) {
ResolvableType resolvedTypeVariable = resolveVariable(
(TypeVariable<?>) type, ResolvableType.forClass(contextClass));
if (resolvedTypeVariable != ResolvableType.NONE) {
return typeFactory.constructType(resolvedTypeVariable.resolve());
}
}
else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve();
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
else {
generics[i] = ResolvableType.forType(typeArgument).resolve();
}
}
return typeFactory.constructType(ResolvableType.
forClassWithGenerics(resolvedType.getRawClass(), generics).getType());
}
}
return typeFactory.constructType(type);
}
private ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) {
ResolvableType resolvedType;
if (contextType.hasGenerics()) {
resolvedType = ResolvableType.forType(typeVariable, contextType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
ResolvableType superType = contextType.getSuperType();
if (superType != ResolvableType.NONE) {
resolvedType = resolveVariable(typeVariable, superType);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
for (ResolvableType ifc : contextType.getInterfaces()) {
resolvedType = resolveVariable(typeVariable, ifc);
if (resolvedType.resolve() != null) {
return resolvedType;
}
}
return ResolvableType.NONE;
}
}
网友评论