(五)HttpMessageConvert

2019-06-16  本文已影响0人  guideEmotion

一 介绍

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
上一篇下一篇

猜你喜欢

热点阅读