响应处理——内容协商底层原理
在前边学习返回值解析器时,里边使用了各种 MessageConverter 寻找哪个解析器可以处理我们的返回值,在寻找过程中,非常关键的一步就是内容协商,它通过遍历所有的 MessageConverter 一个一个寻找,接下来,就来看一下整个内容协商的流程。
内容协商:根据客户端接收能力不同,返回不同媒体类型的数据。
接下来,我们使用postman发起请求,分别测试返回json和xml的情况
先以application/xml来测试
前边的请求处理已经很熟悉了,直接下一步来到 ServletInvocableHandlerMethod.class ,
先拿到返回值
我们的请求标注了@ResponseBody,所以下一步就是找到能处理@ResponseBody的Handler,进过寻找,最终找到了 RequestResponseBodyMethodProcessor
step into 进入 writeWithMessageConverters,来到我们的内容协商关键环节
//......代码太多没有全部拿MediaType selectedMediaType =null;//首先判断当前响应头中是否已经有确定的媒体类型MediaType,我们没有前置拦截,所以没有MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType !=null&& contentType.isConcrete();if(isContentTypePreset){if(this.logger.isDebugEnabled()){this.logger.debug("Found 'Content-Type:"+ contentType +"' in response");}//如果有的话,就用之前的,我们没有走下边
selectedMediaType = contentType;}else{HttpServletRequest request = inputMessage.getServletRequest();//关键来啦,第一步,获取客户端可接收的媒体类型(accept请求头字段)List<MediaType> acceptableTypes =this.getAcceptableMediaTypes(request);
这里step into进入看一下是如何获取 acceptableTypes
到这里,就获取到了客户端能接收的值 acceptableTypes 是我们传的 application/xml
//然后获取能产生的媒体类型(写出类型) producibleTypesList<MediaType> producibleTypes =this.getProducibleMediaTypes(request, valueType,(Type)targetType);
这里step into进入看一下是如何获取 producibleTypes
if(body !=null&& producibleTypes.isEmpty()){thrownewHttpMessageNotWritableException("No converter found for return value of type: "+ valueType);}List<MediaType> mediaTypesToUse =newArrayList();Iterator var15 = acceptableTypes.iterator();//得到客户端需要类型和服务端能处理的类型后,进行最佳匹配,双重循环MediaType mediaType;while(var15.hasNext()){
mediaType =(MediaType)var15.next();Iterator var17 = producibleTypes.iterator();while(var17.hasNext()){MediaType producibleType =(MediaType)var17.next();if(mediaType.isCompatibleWith(producibleType)){
mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));}}}//.................中间省略处理代码.....................//拿到我们的对象
body =this.getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, converter.getClass(), inputMessage, outputMessage);if(body !=null){LogFormatUtils.traceDebug(this.logger,(traceOn)->{return"Writing ["+LogFormatUtils.formatValue(body,!traceOn)+"]";});this.addContentDispositionHeader(inputMessage, outputMessage);if(genericConverter !=null){//用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化写出
genericConverter.write(body,(Type)targetType, selectedMediaType, outputMessage);}else{
converter.write(body, selectedMediaType, outputMessage);}}elseif(this.logger.isDebugEnabled()){this.logger.debug("Nothing to write: null body");}
用最佳converter转化写出
进入 AbstractJackson2HttpMessageConverter.class 的 writeValue 方法
完成之后来到了 write 方法最后一行,看 outputMessage
到这里,我们的内容协商环节就结束了,可以将 Accept 改成 application/json 调试练习。
OVER(∩_∩)O~
版权归原作者 安东子丶 所有, 如有侵权,请联系我们删除。