• 【Spring Boot】自定义MessageConverter


    1、自定义消息转换器MessageConverter

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

    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    	this.messageConvertersProvider.ifAvailable((customConverters) -> {
    		converters.addAll(customConverters.getConverters());
    	});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假设我们现在有一个新的需求
    想要后端返回我们自己定义的格式的数据,就叫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());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

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

    // 会覆盖默认的MessageConverter
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    
    }
    
    // 不会覆盖,只会在默认的MessageConverter后面追加我们自定义的
    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    所以,我们选择在自定义配置类中重写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());
                }
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    我们在请求头中设置Accept为我们自定义的格式application/x-decade
    在这里插入图片描述

    验证结果如下
    在这里插入图片描述

    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));
                }
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    可以看到,系统中的内容协商管理器下面还是原来的2种:获取请求头中的Accept和获取请求参数中的format
    但是获取请求参数format,除了能识别原来的json和xml,还能识别我们自定义的application/x-decade,它使用decade与之对应
    在这里插入图片描述
    可以看到,我们自定义的媒体类型成功加入服务器能解析出来的类型
    在这里插入图片描述
    我们写一个接口进行验证

    @GetMapping(value = "/testPerson")
    @ResponseBody
    public Person testPerson() {
    	Person person = new Person();
    	person.setName("decade");
    	person.setAge(24);
    	person.setBirth(new Date());
    	return person;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    由验证结果可以知道,我们从postman和浏览器都可以获得我们指定格式的数据
    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    AIGC Midjourney 指令生成高清图像及参数提示词
    C# 进行 Starlink 仿真02:先搞个小型 Walker 星座 ===> 创建“十字形”星间链路(升轨、降轨采用不同颜色)
    21天学Python --- 打卡5:Python && Threading
    大数据产业酝酿巨变 元宇宙新格局呼之欲出
    OFDM基本原理
    Abbkine 细胞侵袭分析试剂盒,简单方便,快速检测
    HTML+CSS+JS鲜花商城网页设计期末课程大作业 web前端开发技术 web课程设计 网页规划与设计
    使用 Python 进行测试(6)Fake it...
    前端静态页面基本开发思路(一)
    c#开发和学习(c#编写windows服务)
  • 原文地址:https://blog.csdn.net/Decade0712/article/details/126690183