spring boot学习(7)— 自定义中的 HttpMessageConverter

2,133 阅读3分钟

在我们开发自己的应用时,有时候,我们可能需要自定义一些自己的数据格式来传输,这时,自定义的数据传输和类的实例之间进行转化就需要统一起来了, Spring MVC 中的 HttpMessageConverter 就派上用场了。

HttpMessageConverter 的声明:

public interface HttpMessageConverter<T> {

    /**
     * Indicates whether the given class can be read by this converter.
     * @param clazz the class to test for readability
     * @param mediaType the media type to read (can be {@code null} if not specified);
     * typically the value of a {@code Content-Type} header.
     * @return {@code true} if readable; {@code false} otherwise
     */
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

    /**
     * Indicates whether the given class can be written by this converter.
     * @param clazz the class to test for writability
     * @param mediaType the media type to write (can be {@code null} if not specified);
     * typically the value of an {@code Accept} header.
     * @return {@code true} if writable; {@code false} otherwise
     */
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

    /**
     * Return the list of {@link MediaType} objects supported by this converter.
     * @return the list of supported media types
     */
    List<MediaType> getSupportedMediaTypes();

    /**
     * Read an object of the given type from the given input message, and returns it.
     * @param clazz the type of object to return. This type must have previously been passed to the
     * {@link #canRead canRead} method of this interface, which must have returned {@code true}.
     * @param inputMessage the HTTP input message to read from
     * @return the converted object
     * @throws IOException in case of I/O errors
     * @throws HttpMessageNotReadableException in case of conversion errors
     */
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;

    /**
     * Write an given object to the given output message.
     * @param t the object to write to the output message. The type of this object must have previously been
     * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
     * @param contentType the content type to use when writing. May be {@code null} to indicate that the
     * default content type of the converter must be used. If not {@code null}, this media type must have
     * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
     * returned {@code true}.
     * @param outputMessage the message to write to
     * @throws IOException in case of I/O errors
     * @throws HttpMessageNotWritableException in case of conversion errors
     */
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;

}

里面有四个方法:

  1. canRead: 查看对应的类型和 content-type 头部类型是否支持被读取.
  2. canWrite: 查看对应的类型和 content-type 头部类型是否支持输出.
  3. getSupportedMediaTypes: 获取支持的类型
  4. read: 从 http 请求中,读取数据,并转化为指定类型。
  5. write: 将指定类型的实例写入到response中。

内置的常用HttpMessageConverter 类型:

  1. ByteArrayHttpMessageConverter – 转换 byte array 类型数据
  2. StringHttpMessageConverter – 根据编码格式,转化Strings
  3. ResourceHttpMessageConverter – converts org.springframework.core.io.Resource for any type of octet stream
  4. SourceHttpMessageConverter – 转换 javax.xml.transform.Source 类型的数据。
  5. FormHttpMessageConverter – 转换 application/x-www-form-urlencodedmultipart/form-data 类型的消息.
  6. Jaxb2RootElementHttpMessageConverter – 将对象和xml转换。
  7. MappingJackson2HttpMessageConverter – 使用 jackson2 转换 json 数据和 对象实例

自定义 HttpMessageConverter 并使用

  1. 首先,自定义 HttpMessageConverter: 需要转化的对象为:
public class TestRequestInfo {
    private String key;
    private int value;
    private MultipartFile file;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public MultipartFile getFile() {
        return file;
    }

    @Override
    public String toString() {
        return "TestRequestInfo{" +
                "key='" + key + '\'' +
                ", value=" + value +
                '}';
    }
}

Converter

public class TestMessageConverter extends AbstractHttpMessageConverter<TestRequestInfo> {

    public TestMessageConverter(){
        super(new MediaType("application", "test-converter", Charset.forName("UTF-8")));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    protected TestRequestInfo readInternal(Class<? extends TestRequestInfo> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        String temp = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
        TestRequestInfo test = new TestRequestInfo();
        test.setKey(temp);
        return test;
    }

    @Override
    protected void writeInternal(TestRequestInfo testRequestInfo, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        outputMessage.getBody().write(testRequestInfo.toString().getBytes());
    }
}

  1. 注册 HttpMessageConverter:
@EnableWebMvc
@Configuration
@ComponentScan
public class MVCConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new TestMessageConverter());
    }
}
  1. 定义 Controller:
@RestController
public class TestController {
    @RequestMapping(
        value="/reports",
        method = {RequestMethod.GET, RequestMethod.POST}
    )
    public @ResponseBody
    TestRequestInfo getReports(@RequestBody TestRequestInfo requestInfo){
        System.out.println(requestInfo);
        return requestInfo;
    }
}

请求结果为: {"key":"sdfasfasdf","value":0,"file":null}.