Spring MVC(5)HttpMessageConvert

一 介绍

HttpMessageConveter 是用来处理request 和response 里的数据的。Spring 为我们内置了大量的HttpMessageConverter,例如, MappingJackson2HttpMessageConverter 、StringHttpMessageConverter 等。主要是为了配合@RequestBody和@ResponsetBody。


是 Spring3.0 新添加的一个接 口,负责1:将请求信息转换为一个对象(类型为 T),2:将对象( 类型为 T)输出为响应信息

  1. Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器 可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对 象,同时指定支持 MIME 类型(text/html,applaiction/json等) – Boolean
  2. canWrite(Class<?> clazz,MediaType mediaType):指定转换器 是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型 在MediaType 中定义。 – LIst getSupportMediaTypes():该转换器支持的媒体类 型。
  3. T read(Class<? extends T> clazz,HttpInputMessage inputMessage): 将请求信息流转换为 T 类型的对象。
  4. 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;
	}

}

参考

  1. https://blog.csdn.net/DERRANTCM/article/details/77104957
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值