spring boot自定义数据输出格式

前言

spring boot可以响应页面,也可以响应数据。
如想响应数据,只需在方法上加@ResposeBody注解。
但是此数据以何种方式被传输出去或传输进来,需要浏览器和服务器进行协商,此处以传输出去为例。

    @ResponseBody
    @GetMapping("/aaa")
    public User Aaa(){
        User user = new User();
        user.setUserName("aaa");
        user.setPassword("111");
        return user;
    }

当浏览器发起aaa请求时,user对象会被传输到浏览器。
浏览器在请求头中会写明浏览器需要什么类型的数据。
spring boot自定义数据输出格式_第1张图片
其中q指权重,权重高的类型优先。可以看到xml权重是0.9,*/*是0.8。意味着如果服务器同时能提供json类型和xml类型的数据格式的话,会优先以xml格式传输。

而服务器能提供的类型为
spring boot自定义数据输出格式_第2张图片
其中7和8是处理json的

所以默认请求以json格式响应出去。
json数据

1.基于请求头的自定义messageConverter

这样可以自定义响应的数据格式,比如我想让数据以aaa-111方式响应出去,就得自定义。

1.1开启springmvc内容协商的参数偏好(不知道怎么翻译好)
spring.mvc.contentnegotiation.favor-parameter=true
1.2自定义messageConverter

自定义的消息转换器需要实现HttpMessageConverter接口

public class AaaConverter implements HttpMessageConverter<User> {
	//由于只需要往外写,所以没必要可读
    @Override
    public boolean canRead(Class<?> aClass, MediaType mediaType) {
        return false;
    }
	//如果传入的类是User类型,那么就可写
    @Override
    public boolean canWrite(Class<?> aClass, MediaType mediaType) {
        return aClass.isAssignableFrom(User.class);
    }
	//获取支持的媒体类型,就是配置这个消息转换器的请求头参数
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/bbb");
    }
	//读方法空实现
    @Override
    public User read(Class<? extends User> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }
	//写方法,先获取消息体,然后将消息写出去,此处可以自定义写出去的格式
    @Override
    public void write(User user, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        OutputStream body = httpOutputMessage.getBody();
        String message = user.getUserName() + "-" + user.getPassword();
        body.write(message.getBytes());
    }
}
1.2将自定义消息转换器添加到spring MVC

在配置类中实现webMVCConfiger接口,所有springMVC的自定义方法都在这个匿名内部类中,此接口中有两个方法和消息转换器有关。
spring boot自定义数据输出格式_第3张图片
第一个方法是配置消息转换器,第二个方法是拓展消息转换器,此处用第二个方法。直接在方法里面添加一个自定义的消息转换器即可。

@Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new AaaConverter());
            }
        };
1.3测试

用postman发送请求
spring boot自定义数据输出格式_第4张图片

1.4其他说明

现在只能用postman之类的应用来测试,浏览器还无法接收到bbb类型的数据,因为springmvc的参数内容协商策略里面默认就只有json和xml类型的数据,无法映射为application/bbb,直接在确定可接收类型时就失败了。会返回406。

可以看到,在获取浏览器可接收的类型处就会抛出异常。
spring boot自定义数据输出格式_第5张图片
getAcceptableMediaTypes()方法里面直接调用的的下图的方法
获取媒体类型
spring boot自定义数据输出格式_第6张图片
直接从这个mediaTtpe:map里面获取bbb键所对应的值,但是这个map里面就只有json和xml两个键。所以获取不到。
spring boot自定义数据输出格式_第7张图片
所以虽然现在服务器可以提供bbb格式的数据,但是由于在获取浏览器可接收的数据类型就抛出异常了,所以浏览器是获取不到数据的。
spring boot自定义数据输出格式_第8张图片

1.5 消息转换器和参数策略的关系

spring boot自定义数据输出格式_第9张图片

参数策略和消息转换器的区别是:
消息转换器只是提供一个数据格式,表示服务器有处理这个格式的能力
参数策略是将消息转换器和某个参数进行映射(通俗来说就是进行绑定)。
所以只有消息转换器的话,请求头能访问但是参数不能访问

2.基于参数的自定义messageConverter

要想浏览器也能通过format参数来制定数据类型,需要以下操作

2.1配置内容协商

在自定义webMvcConfiger中重写configureContentNegotiation()方法,此方法会覆盖springMvc的配置,所以需要配置所有的类型

@Configuration("proxyBeanMethods = false")
public class MyConfig {

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {

            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            	//由于参数类型内容协商策略的构造器需要传一个map
                HashMap<String, MediaType> map = new HashMap<>();
                //将参数与指定的类型进行关联
                map.put("json",MediaType.APPLICATION_JSON);
                map.put("xml",MediaType.APPLICATION_XML);
                //为了便于理解,将参数名设置为ccc
                map.put("ccc",MediaType.parseMediaType("application/bbb"));
                //创建一个请求头内容协商策略对象
                HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderStrategy();
                //创建一个参数内容协商策略对象
                ParameterContentNegotiationStrategy paraStrategy = new ParameterContentNegotiationStrategy(map);
                //将参数和请求头内容协商策略对象添加到系统策略中
				//注意此处必须先放参数后放请求头
                configurer.strategies(Arrays.asList(paraStrategy,HeaderStrategy));
            }

            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new AaaConverter());
            }
        };
    }
}

至于为什么需要先添加参数策略再添加请求头策略是因为
spring boot自定义数据输出格式_第10张图片
源码中迭代两个策略时,第一个策略执行完毕后,只有没有获取到值才会执行第二个策略,如果先放参数策略后放请求头策略,那么有参数就执行参数策略,没有参数就执行请求头策略。
如果先放请求头策略,那么请求头策略肯定有值,参数就没任何作用了。

浏览器访问
浏览器访问
postman访问

spring boot自定义数据输出格式_第11张图片

3. 总结

如果想通过浏览器参数和请求头都能制定数据格式的话。
①开启内容协商偏好,spring.mvc.contentnegotiation.favor-parameter=true
②自定义消息转换器,自定义数据格式
③自定义参数内容协商策略,并将其添加到系统策略中。

你可能感兴趣的:(java,java,spring,spring,boot)