26.内容协商原理
将数据以xml的方式返回给浏览器,在参数解析器解析时涉及到内容协商。
@ResponseBody
@RequestMapping("/test/person")
public Person getPerson(){
Person person = new Person();
person.setAge(20);
person.setBrith(new Date());
person.setUserName("张三");
return person;
}
1. 开启请求参数协商模式,可以随时修改浏览器接受的参数类型,xml、json格式的数据
spring:
contentnegotiation:
favor-parameter: true
2.得到请求参数的返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
返回值的参数类型如图所示:
3.调用返回值处理器对得到的参数值、请求进行处理
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
4,查找哪一种返回值处理器能够处理@Reponse注解的处理器,去处理相应的请求
返回值处理器的类型总共有15种
5.调用RequestResponseBodyMethodProcessor中的方法进行处理
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
6.内容协商处理时,首先获取到客户端(浏览器)的accept看浏览器能够处理哪些类型
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
7.step Into进去,利用一个for循环,得到所有浏览器支持的类型。基本请求头的协商策略,确定客户端可以接受的内容类型。HeaderContentNegotiationStrategy.还有根据配置基于参数的内容协商策略
for (ContentNegotiationStrategy strategy : this.strategies) {
List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
continue;
}
return mediaTypes;
}
return MEDIA_TYPE_ALL_LIST;
}
从accept获取数据
String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
mediaTypes的类型共有5种
8.后台服务器所支持的类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
判断哪些MessageConverters能够转换person类型,将结果封装到result结果集里边。
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
else if (converter.canWrite(valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
类型转换器如图共有11种,其中能够转换对象类型的有10种
9.将浏览器能够处理的类型与后台所能处理的类型进行对比,找出最适合的类型转换
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
可用的媒体类型共有14种
10.用消息转换器,将对象类型转换为xml/json类型的数据写入到响应体中
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
objectWriter.writeValue(generator, value);