spring boot可以响应页面,也可以响应数据。
如想响应数据,只需在方法上加@ResposeBody注解。
但是此数据以何种方式被传输出去或传输进来,需要浏览器和服务器进行协商,此处以传输出去为例。
@ResponseBody
@GetMapping("/aaa")
public User Aaa(){
User user = new User();
user.setUserName("aaa");
user.setPassword("111");
return user;
}
当浏览器发起aaa请求时,user对象会被传输到浏览器。
浏览器在请求头中会写明浏览器需要什么类型的数据。
其中q指权重,权重高的类型优先。可以看到xml权重是0.9,*/*是0.8。意味着如果服务器同时能提供json类型和xml类型的数据格式的话,会优先以xml格式传输。
这样可以自定义响应的数据格式,比如我想让数据以aaa-111方式响应出去,就得自定义。
spring.mvc.contentnegotiation.favor-parameter=true
自定义的消息转换器需要实现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());
}
}
在配置类中实现webMVCConfiger接口,所有springMVC的自定义方法都在这个匿名内部类中,此接口中有两个方法和消息转换器有关。
第一个方法是配置消息转换器,第二个方法是拓展消息转换器,此处用第二个方法。直接在方法里面添加一个自定义的消息转换器即可。
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new AaaConverter());
}
};
现在只能用postman之类的应用来测试,浏览器还无法接收到bbb类型的数据,因为springmvc的参数内容协商策略里面默认就只有json和xml类型的数据,无法映射为application/bbb,直接在确定可接收类型时就失败了。会返回406。
可以看到,在获取浏览器可接收的类型处就会抛出异常。
getAcceptableMediaTypes()方法里面直接调用的的下图的方法
获取媒体类型
直接从这个mediaTtpe:map里面获取bbb键所对应的值,但是这个map里面就只有json和xml两个键。所以获取不到。
所以虽然现在服务器可以提供bbb格式的数据,但是由于在获取浏览器可接收的数据类型就抛出异常了,所以浏览器是获取不到数据的。
参数策略和消息转换器的区别是:
消息转换器只是提供一个数据格式,表示服务器有处理这个格式的能力
参数策略是将消息转换器和某个参数进行映射(通俗来说就是进行绑定)。
所以只有消息转换器的话,请求头能访问但是参数不能访问
要想浏览器也能通过format参数来制定数据类型,需要以下操作
在自定义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.mvc.contentnegotiation.favor-parameter=true
②自定义消息转换器,自定义数据格式
③自定义参数内容协商策略,并将其添加到系统策略中。