优雅的使用spring boot: 消息转换器的介绍与使用

2,670 阅读4分钟
原文链接: mp.weixin.qq.com

1、介绍

HttpMessageConver 是SpringBoot中对Http请求数据与返回数据的转换接口,当我们需要定制化数据的转化时,就可以通过实现此接口来达成。

2、使用场景介绍

我们可以举一个例子,如下图:

当我们后台做好统一的API,当面对不同的客户端需要返回不同的格式的数据是,就可以很好的利用此功能定制 HttpMessageConver,从而在改动代码最小的情况下来完成需求。如:

当需要AES加密时,我们可以定制 AesHttpMessageConver,并设置支持的 MediaTypejson/aes,则:

  1. 当客户端设置的请求头为 Content-type:json/aes时,将会匹配到  AesHttpMessageConver,并使用其  readInternal方法来处理客户端发送来的数据。

  2. 当客户端设置的请求头为 Accept:json/aes时,也会匹配到  AesHttpMessageConver,并使用其  writeInternal来对数据进行处理后返回给客户端。

3、HttpMessageConver使用介绍

在实现的时候,我们主要是继承 AbstractHttpMessageConverter抽象类,其对 HttpMessageConverter接口进行了部分的实现, AbstractHttpMessageConverter抽象类如下所示:

                                
  1. public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T>{}

当我们继承时,需要指定 T的类型,表明此 MessageConver是对什么类型起效。

在实现类中,也需要调用父类的构造函数,说明此 MessageConverter支持的 MediaType类型,简单实现如下:

                                        
  1. public JsonMessageConver() {

  2.        super(MediaType.valueOf("json/aes;charset=UTF-8"));

  3.        setDefaultCharset(Charset.forName("UTF-8"));

  4.  }

上述代码主要做了两个动作:

  1. 设置此 MessageConverter支持的类型为  json /aes ;charset =UTF -8

  2. 设置默认的编码为 UTF-8

还需要实现下面3个抽象方法,如下:

  1.    protected abstract boolean supports(Class<?> clazz);

  2.    protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)

  3.            throws IOException, HttpMessageNotReadableException;

  4.    protected abstract void writeInternal(T t, HttpOutputMessage outputMessage)

  5.            throws IOException, HttpMessageNotWritableException;

  1. protectedabstractbooleansupports(Class<?>clazz);方法主要是进行类型支持的判断,判断类型是否适合此  converter处理,支持则返回  true,不支持返回  false

  2. protectedabstractT readInternal(Class<?extendsT>clazz,HttpInputMessageinputMessage)方法是对用户请求的数据请求处理与转换,然后再返回到  Controller层。如:

  1. protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

  2.        String read = IoUtil.read(inputMessage.getBody(), getDefaultCharset());

  3.        JSONObject personJson = JSONUtil.parseObj(AESUtils.decryptData(read));

  4.        return JSONUtil.toBean(personJson,clazz);

  5.    }

方法说明:

  • Class<?extendsObject>clazz:具体应该为  Class <? extendsT >clazz,这个与我们实现类设置的类型有关。

  • HttpInputMessageinputMessage: 这个input为用户的请求的信息流,我们可以通过 inputMessage.getBody()获取输入流,来获取用户的输入。

  • 返回值具体也是为 T,这个也与我们实现类设置的类型有关。

返回值需要一定为 T类型,或者是其子类。

  1. protectedabstractvoidwriteInternal(T t,HttpOutputMessageoutputMessage):此方法是对后台返回给客户的数据进行处理,消息是通过输出流的方式返回给客户,实现例子如下:

  1. protected void writeInternal(Object person, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

  2.        OutputStreamWriter writer = IoUtil.getWriter(outputMessage.getBody(), getDefaultCharset());

  3.        JSONObject jsonObject = JSONUtil.parseObj(person);

  4.        writer.write(AESUtils.encryptData(JSONUtil.toJsonStr(jsonObject)));

  5.        writer.flush();

  6.        writer.close();

  7.    }

方法说明:

  • Objectperson:这个参数为后台返回给客户的数据。

  • HttpOutputMessageoutputMessage:这个参数为数据返回的输出流。

因此,我们可以对返回的数据 person进行处理后,通过获取输出流,然后写入,来进行数据的返回。

3、实现AesHttpMessageConver

首先,下面代码展示在没用使用AesHttpMessageConver时,正常的SpringBoot接收与处理代码逻辑。

  1. 定义一个Person类负责接收Json数据

  1. public class Person {

  2.    private String id;

  3.    private Integer name;

  4. }

  1. 定义Controller层,实现数据的接收与返回

  1.    @PostMapping(value = "/test")

  2.    @ResponseBody

  3.    public Person testPerson(@RequestBody Person person) {

  4.        return person;

  5.    }

上面是一个正常的逻辑,下面,我们需要对接收和返回的数据都进行加密,在很多人一般的做法如下图代码所示:

  1.    @PostMapping(value = "/test")

  2.    public String testPerson(@RequestParam String key) {

  3.        String decryptData = AESUtils.decryptData(key);

  4.        Person person = JSONUtil.toBean(decryptData, Person.class);

  5.        //person业务处理

  6.        return AESUtils.encryptData(JSONUtil.toJsonStr(person));

  7.    }

可以看到,上述代码将会比较冗余,我们可以在SpringBoot中使用更优雅的方式来进行处理,即自定义 HttpMessageConver,具体实现如下。

  1. 第一步,定制实现 AesMessageConver

  1. public class AesMessageConver extends AbstractHttpMessageConverter<Object> {

  2.    public AesMessageConver() {

  3.        super(MediaType.valueOf("json/aes;charset=UTF-8"));

  4.        setDefaultCharset(Charset.forName("UTF-8"));

  5.    }

  6.    @Override

  7.    protected boolean supports(Class<?> aClass) {

  8.        return true;

  9.    }

  10.    @Override

  11.    protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

  12.        String read = IoUtil.read(inputMessage.getBody(), getDefaultCharset());

  13.        JSONObject personJson = JSONUtil.parseObj(AESUtils.decryptData(read));

  14.        return JSONUtil.toBean(personJson,clazz);

  15.    }

  16.    @Override

  17.    protected void writeInternal(Object person, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

  18.        OutputStreamWriter writer = IoUtil.getWriter(outputMessage.getBody(), getDefaultCharset());

  19.        JSONObject jsonObject = JSONUtil.parseObj(person);

  20.        writer.write(AESUtils.encryptData(JSONUtil.toJsonStr(jsonObject)));

  21.        writer.flush();

  22.        writer.close();

  23.    }

  24. }

  1. 第二步,注册 MessageConver到SpringBoot中

  1. @Configuration

  2. public class MvcConfig implements WebMvcConfigurer {

  3.    @Override

  4.    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

  5.        converters.add(new AesMessageConver());

  6.    }

  7. }

只要通过上述的两个步骤,我们就可以在不改动原有 Controller层的代码情况下,来满足AES加密的需求。