美文网首页
(五)HttpMessageConvert

(五)HttpMessageConvert

作者: guideEmotion | 来源:发表于2019-06-16 17:17 被阅读0次

    一 介绍

    HttpMessageConveter 是用来处理request 和response 里的数据的。Spring 为我们内置了大量的HttpMessageConverter,例如, MappingJackson2HttpMessageConverter 、StringHttpMessageConverter 等。例如:我们实现一个自定义的HttpMessageConverter,他将接收以“-”号连接的数据,将数据转换成自定义的对象。


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

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

    相关文章

      网友评论

          本文标题:(五)HttpMessageConvert

          本文链接:https://www.haomeiwen.com/subject/vougfctx.html