SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

作者:Decade0712 时间:2023-11-28 11:35:54 

1、自定义消息转换器MessageConverter

在WebMvcAutoConfiguration类中有一个方法configureMessageConverters(),它会配置默认的MessageConverter

public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider.ifAvailable((customConverters) -> {
converters.addAll(customConverters.getConverters());
});
}

假设我们现在有一个新的需求

想要后端返回我们自己定义的格式的数据,就叫x-decade,格式为使用分号拼接Person对象属性值

那么就要新建一个MessageConverter了

package com.decade.converters;
import com.decade.pojo.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class DecadeConverter implements HttpMessageConverter<Person> {
// 只考虑写出,所以这里默认写false
   @Override
   public boolean canRead(Class<?> clazz, MediaType mediaType) {
       return false;
   }
   @Override
   public boolean canWrite(Class<?> clazz, MediaType mediaType) {
       return clazz.isAssignableFrom(Person.class);
   }
   /**
    * 统计当前converter能支持哪些类型
    * @return 返回支持的媒体类型
    */
   @Override
   public List<MediaType> getSupportedMediaTypes() {
       return MediaType.parseMediaTypes("application/x-decade");
   }
// 只考虑写出,所以这里默认写null
   @Override
   public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
       return null;
   }
   @Override
   public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
       // 自定义想要写出的数据格式
       String result = person.getName() + ";" + person.getAge() + ";" + person.getBirth();
       // 写出
       final OutputStream body = outputMessage.getBody();
       body.write(result.getBytes());
   }
}

我们发现,WebMvcConfigurer接口类下面有2个关于配置MessageConverter的方法

// 会覆盖默认的MessageConverter
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
// 不会覆盖,只会在默认的MessageConverter后面追加我们自定义的
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}

所以,我们选择在自定义配置类中重写extendMessageConverters()方法

package com.decade.config;
import com.decade.converters.DecadeConverter;
import org.springframework.context.annotation.Bean;
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(proxyBeanMethods = false)
public class MyMvcConfig implements WebMvcConfigurer {
   @Bean
   public WebMvcConfigurer createConvert() {
       return new WebMvcConfigurer() {
           @Override
           public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
               converters.add(new DecadeConverter());
           }
       };
   }
}

我们在请求头中设置Accept为我们自定义的格式application/x-decade

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

验证结果如下

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

2、自定义内容协商管理器contentNegotiationManager

问题:我们新建的x-decade格式是否只能通过postman调用才会生效呢?如果我们要使用浏览器参数方式,怎么才能生效呢?

因为我们之前的【Spring Boot】响应处理

它默认只能处理xml和json格式,所以为了解决这个问题,我们就要自定义内容协商管理器了

首先我们还是要在自定义配置类中重写相关方法

package com.decade.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration(proxyBeanMethods = false)
public class MyMvcConfig implements WebMvcConfigurer {
   @Bean
   public WebMvcConfigurer createConvert() {
       return new WebMvcConfigurer() {
           @Override
           public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
               // 设置支持的浏览器参数类型
               Map<String, MediaType> mediaTypes = new HashMap<>();
               mediaTypes.put("json", MediaType.APPLICATION_JSON);
               mediaTypes.put("xml", MediaType.APPLICATION_XML);
               mediaTypes.put("decade", MediaType.parseMediaType("application/x-decade"));
               final ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
               // 为了继续支持请求头参数类型,还需要往里面塞请求头内容协商管理器
               final HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
               configurer.strategies(Arrays.asList(strategy, headerContentNegotiationStrategy));
           }
       };
   }
}

可以看到,系统中的内容协商管理器下面还是原来的2种:获取请求头中的Accept和获取请求参数中的format

但是获取请求参数format,除了能识别原来的json和xml,还能识别我们自定义的application/x-decade,它使用decade与之对应

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

可以看到,我们自定义的媒体类型成功加入服务器能解析出来的类型

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

我们写一个接口进行验证

@GetMapping(value = "/testPerson")
@ResponseBody
public Person testPerson() {
Person person = new Person();
person.setName("decade");
person.setAge(24);
person.setBirth(new Date());
return person;
}

由验证结果可以知道,我们从postman和浏览器都可以获得我们指定格式的数据

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

SpringBoot自定义MessageConverter与内容协商管理器contentNegotiationManager详解

来源:https://blog.csdn.net/Decade0712/article/details/126690183

标签:SpringBoot,MessageConverter,contentNegotiationManager
0
投稿

猜你喜欢

  • Java并发编程之ConcurrentLinkedQueue源码详解

    2023-01-22 16:19:51
  • Java线程同步方法实例总结

    2022-08-20 20:35:08
  • Android Studio 3.6 layout文件text模式切换问题

    2022-10-06 11:28:32
  • android POST数据遇到的UTF-8编码(乱码)问题解决办法

    2022-09-12 22:31:46
  • JNI语言基本知识

    2022-01-20 20:02:46
  • java项目构建Gradle的使用教程

    2023-06-07 19:17:41
  • No ‘Access-Control-Allow-Origin‘ header is present跨域及解决

    2022-07-12 13:33:08
  • Android中隐藏状态栏和标题栏的方法汇总(隐藏状态栏、标题栏的五种方法)

    2022-05-18 07:31:03
  • Android系统永不锁屏永不休眠的方法

    2023-03-11 00:06:21
  • java创建子类对象设置并调用父类的变量操作

    2023-10-13 18:16:13
  • Java代码实现简单酒店管理系统

    2023-11-21 03:38:57
  • 搭建MyBatis-Plus框架并进行数据库增删改查功能

    2023-11-09 04:33:43
  • Java spring AOP基础

    2022-08-10 12:39:25
  • SpringBoot整合之SpringBoot整合MongoDB的详细步骤

    2023-11-25 09:55:37
  • C#之继承实现

    2023-04-10 16:51:41
  • Java 入门图形用户界面设计之复选框

    2022-06-19 15:22:53
  • Android使用IntentService进行apk更新示例代码

    2022-01-19 12:12:07
  • Android View事件机制 21问21答

    2022-10-14 04:27:10
  • 手动模拟JDK动态代理的方法

    2023-03-23 00:49:03
  • SpringSecurity Jwt Token 自动刷新的实现

    2022-04-28 18:49:45
  • asp之家 软件编程 m.aspxhome.com