SpringBoot自定义HttpMessageConverter

简介

我们使用**@RequestBody可以将请求体中的JSON字符串绑定到相应的bean,使用@ResponseBody**可以使返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中,而整个数据绑定的过程其实是HttpMessageConverter在起作用。

MediaType

MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。

@RequestBody的简单实用

@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。

  1. 解析json
  • Content-Type: application/json
  • 请求数据格式
{
	"question": "aaa",
	"fromUser": "bbb"
}
  1. 解析xml
  • Content-Type: application/xml
  • 请求数据格式
<?xml version='1.0' encoding="utf-8"?> 
<Request>
	<question>aaa</question>
	<fromUser>bbb</fromUser>
</Request>

上面两种方式都是可以把数据映射到Bean中的。

  1. 原理

Spring会根据MediaType查找合适的HttpMessageConverter的实现类进行序列化的操作

public interface HttpMessageConverter<T> {

	boolean canRead(Class<?> clazz, MediaType mediaType);

	boolean canWrite(Class<?> clazz, MediaType mediaType);

	List<MediaType> getSupportedMediaTypes();

	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}
方法作用
getSupportedMediaTypes获取支持的MediaType
read读取request的body
write把数据写到response的body中

@ResponseBody

ResponseBody中的使用和RequestBody类似

自定义HttpMessageConverter

  1. 目的
    SpringBoot提供一系列的HttpMessageConverter,满足了我们的绝大部分需求,如果有特性需求,我们可以编写自定义的转换器
  2. 步骤
  • 编写Converter类,需要实现HttpMessageConverter,或者继承已经存在的实现类,并重写上文中的关键方法
  • 编写WebConfig(extends WebMvcConfigurerAdapter)
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    /**
     * 自定义message_convert
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
       // 把converter添加到converters的最后(SpringBoot会使用第一个匹配到的Converter)
       converters.add(new XxxConverter());
       // 把converter添加到converters的最前面
       // converters.add(0, new XxxConverter());
    }
}

到此为止,我们自定义的Converter已经生效了
3. 自定义MediaType

虽然我们已经编写Converter,但是我们一般会为自定义的Converter指定可以处理的媒体类型,可以指定自定义的媒体类型

  • 在自定义的Converter中新增自定义的MediaType,并且根据需要修改canReadcanWrite
public class XxxConverter implements HttpMessageConverter<Serializable> {

    public static final String CUSTOM_MEDIA = "application/custom-media";

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return true;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return true;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Lists.newArrayList(MediaType.parseMediaType(CUSTOM_MEDIA));
    }

    @Override
    public Serializable read(Class<? extends Serializable> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Serializable serializable, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

    }
}

  这里一定要修改getSupportedMediaTypes方法,SpringBoot是根据这个方法的返回,以及Controller—@RequestMapping中指定的MediaType,判断是否可用于当前请求/返回。

  • Controller的@RequestMapping中指定consumes或者produces
@RestController
@RequestMapping(produces = CUSTOM_MEDIA, consumes = CUSTOM_MEDIA)
@Validated
public class HomeController {

    @GetMapping(HOME)
    JsonResult info(@RequestHeader("userId") Long userId) {
        return JsonResult.ok();
    }
}

  consumes是指定请求的MediaType,需要调用方设置成我们提供的application/custom-media

  produces是指定返回的MediaType,如果我们设置成application/custom-media,那么方法返回的数据就会通过自定义的XxxConverter进行转换。

问题

注意:如果我们修改了produces的MediaType,那么HTTP返回中的MediaType也会是我们自定义的类型,除非和调用方约定好,否则调用方是没有办法解析的。

解决办法:

public class XxxConverter implements HttpMessageConverter<Serializable> {

	......

    @Override
    public void write(Serializable serializable, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // 最后把Content-Type改成APPLICATION_JSON_UTF8_VALUE,要不然请求方会无法解析
        ((ServletServerHttpResponse) outputMessage)
        .getServletResponse().setHeader("Content-Type",APPLICATION_JSON_UTF8_VALUE);
    }
}

总结

一般情况下,SpringBoot提供的默认转换器已经足够我们使用,但是在一些接口的参数需要加解密,调整返回体的结构等情况下会用到

我的个人博客,有空来坐坐

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值