问题
在使用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
形式的数据,需要如何编写自己的解析器。
在SpringBoot
的WebMvcConfigurer
中提供了一个方法,所以我们可以配置自己的数据解析器。
/**
* 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格式
返回相同的结果就代表成功了。