SpringMVC内容协商-扩展参数解析

问题

在使用SpringMVC时,调用接口传送数据时,大多情况使用的是json格式传输数据,这也是这个框架里面默认选项,但是如果我们要自己定义传输格式,并且写自己的解析接口,定义自己的媒体类型作为扩展,需要怎么做?

过程

SpringMVC传输数据的阶段,我们请求在DispatchServlet中处理时,当我们找到了对应的方法HandlerMapping之后,执行方法前会对参数进行解析,当然对应的执行完方法之后我们也会处理它的返回值。

举个例子,比如一个方法使用了@ResponseBody@RequestBody注解。如下

	//其中User为一个对象 包含name 和 age 两个属性
	@PostMapping(value = "/add/user")
	@ResponseBody
	public User user(@RequestBody User user) {
        return user;
    }

在执行方法前需要把请求里的数据转化成对象,在执行方法后需要把对象重新放到响应体里。这个过程有两个接口参与。

  • HandlerMethodArgumentResolver 负责处理入参
  • HandlerMethodReturnValueHandler 负责处理返参

SpringMVC中提供了他们两个共同的实现类RequestResponseBodyMethodProcessor 下面是它的继承关系

也就是说SpringMVC为我们提供了实现,在经过上面函数时会到RequestResponseBodyMethodProcessor里进行处理。当然这是对应的@ResponseBody@RequestBody两个注解,不同的注解处理函数时不一样的,SpringMVC使用组合模式设计,通过遍历符合条件的子类选择对应的解析器。

在RequestResponseBodyMethodProcessor里有两个函数也有说明

@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

实现

如果我们现在要实现传输properties形式的数据,需要如何编写自己的解析器。

SpringBootWebMvcConfigurer中提供了一个方法,所以我们可以配置自己的数据解析器。

	/**
	 * A hook for extending or modifying the list of converters after it has been
	 * configured. This may be useful for example to allow default converters to
	 * be registered and then insert a custom converter through this method.
	 * @param converters the list of configured converters to extend.
	 * @since 4.1.3
	 */
	default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
	}
  • 编写解析类
package com.hwk.springmvc.http.converter.properties;
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.AbstractGenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Properties;

public class PropertiesHttpMessageConverter extends AbstractGenericHttpMessageConverter<Properties> {

    //设置支持的媒体类型
    public PropertiesHttpMessageConverter() {
        super(new MediaType("text", "properties"));
    }

    @Override
    protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        HttpHeaders httpHeaders = outputMessage.getHeaders();
        MediaType mediaType = httpHeaders.getContentType();

        //获取字符编码
        Charset charset = mediaType.getCharset();

        //当charset 为空时   默认使用utf-8
        charset = charset == null ? Charset.forName("UTF-8") : charset;

        //Properties -> String
        //OutputStream -> Writer
        //字节输出流
        OutputStream outputStream = outputMessage.getBody();
        //字符输出流
        Writer writer = new OutputStreamWriter(outputStream, charset);

        //Properties 写入到字符输出流
        properties.store(writer, "success");
    }

    @Override
    protected Properties readInternal(Class<? extends Properties> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        //inputMessage 为Http消息请求对象  传输的内容可以从里面取
        //从请求头 Content-Type解析编码
        HttpHeaders httpHeaders = inputMessage.getHeaders();
        MediaType mediaType = httpHeaders.getContentType();

        //获取字符编码
        Charset charset = mediaType.getCharset();

        //当charset 为空时   默认使用utf-8
        charset = charset == null ? Charset.forName("UTF-8") : charset;

        //字节流转化为字符流
        InputStream inputStream = inputMessage.getBody();
        InputStreamReader reader = new InputStreamReader(inputStream, charset);

        Properties properties = new Properties();
        //加载字符流
        properties.load(reader);

        return properties;
    }

    @Override
    public Properties read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return readInternal(null, inputMessage);
    }
}
  • 添加到SpringMVC配置里
package com.hwk.springmvc.config;
import com.hwk.springmvc.http.converter.properties.PropertiesHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    	//注意  这里需要往前插入,不然文本信息会被Json格式的先处理
        converters.add(0, new PropertiesHttpMessageConverter());
    }
}
  • 编写Controller
package com.hwk.springmvc.controller;
import com.hwk.springmvc.model.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Properties;

@RestController
public class PropertiesRestController {
    @PostMapping(value = "/add/properties",
            consumes = "text/properties;charset=UTF-8"//过滤媒体类型
    )
    public Properties user(@RequestBody Properties properties) {
        return properties;
    }
}
  • 使用postman测试

Headers需要指明自定义的媒体类型
在这里插入图片描述
使用文本传输方式,当然内容需要时满足Properties格式
在这里插入图片描述
返回相同的结果就代表成功了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值