栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot核心技术-Web开发-数据响应与内容协商

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot核心技术-Web开发-数据响应与内容协商

 1、响应JSON

返回值处理:将Controller中的方法在执行完成后,返回值处理器要将返回的类型转为前端需要的类型,如方法上加了@ResponseBody,就需要将Person转为JSON。

    @ResponseBody
    @GetMapping("/test/person")
    public Person getPerson(){
        Person person = new Person();
        person.setAge(11);
        person.setBirth(new Date());
        person.setUserName("zhangsan");
        return person;
    }

1.1、jackson.jar+@ResponseBody
    
      org.springframework.boot
      spring-boot-starter-web
    
    上面的web场景自动引入了json场景
    
      org.springframework.boot
      spring-boot-starter-json
      2.3.4.RELEASE
      compile
    

底层具体的依赖如下,框架已经帮忙引入, 自己只需要导入spring-boot-starter-web即可。

 返回值处理器:

之前的流程在请求参数解析中,返回值在invokeAndHandle中

 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        this.setResponseStatus(webRequest);
        if (returnValue == null) {
            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
                this.disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");

        try {
            //执行完目标方法之后,进行返回值处理
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception var6) {
            if (logger.isTraceEnabled()) {
                logger.trace(this.formatErrorForReturnValue(returnValue), var6);
            }

            throw var6;
        }
    }

this.returnValueHandlers.handleReturnValue();中进行返回值处理。

returnValueHandlers是HandlerMethodReturnValueHandlerComposite类型,下图是所有的返回值处理器。

handleReturnValue方法的内部实现:

HandlerMethodReturnValueHandlerComposite类
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        } else {
            handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
        }
    }

 选择好对应的返回值处理器之后,调用handler.handleReturnValue进入到下面方法:

以处理@ResponseBody返回值类型的RequestResponseBodyMethodProcessor类为例	
@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
        // 使用消息转换器进行写出操作
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

返回值解析器原理

  1. selectHandler中,返回值处理器判断是否支持这种类型返回值 supportsReturnType
  2. 返回值处理器调用 handleReturnValue 进行处理
  3. RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
    1. 利用MessageConverters将数据写为JSON
      1. 内容协商(浏览器默认会以请求头的方式告诉服务器他能接受的内容类型)

      2. 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,

      3. SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?

        1. 得到MappingJackson2HttpMessageConverter可以将对象写为json

        2. 利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

1.2、SpringMVC支持哪些返回值
ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;
1.3、HTTPMessageConverter原理 1.3.1 MessageConverter规范

HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。

例子:Person对象转为JSON。或者 JSON转为Person

1.3.2 默认的MessageConverter

0 - 只支持Byte类型的

1 - String

2 - String

3 - Resource

4 - ResourceRegion

5 - DOMSource.class SAXSource.class) StAXSource.class StreamSource.class Source.class

6 - MultiValueMap

7 - true

8 - true

9 - 支持注解方式xml处理的。

最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)

2、内容协商(writeWithMessageConverters的具体实现)

根据客户端接受能力,返回不同媒体类型的数据。

2.1 引入xml依赖

            com.fasterxml.jackson.dataformat
            jackson-dataformat-xml
2.2 postman分别测试返回json和xml

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

2.4 内容协商原理

1、判断当前响应头是否已经有确定的媒体类型。MediaType

2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】

List acceptableTypes;
try {
      acceptableTypes = this.getAcceptableMediaTypes(request);
}

以上图PostMan为例,此时的acceptableTypes为application/xml

3、遍历循环所有当前系统的MessageConverter,看哪些MessageConverter支持操作Controller方法的返回类型,如Person类型。

4、找到支持操作Person类型的converter,把converter支持转化的媒体类型统计出来

List producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);

5、客户端需要【application/xml】,服务端能处理的类型producibleTypes :如下

 6、进行内容协商的最佳匹配媒体类型,使用双层循环,找到服务器能实现客户端指定内容类型的类型。因为客户端可能允许接受多种类型,所有会选择最佳匹配媒体类型。

List mediaTypesToUse = new ArrayList();
            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));
                    }
                }
            }

7、用支持 将对象转为 最佳匹配媒体类型 的converter。调用它将他转化。

if (selectedMediaType != null) {
                //在上一步的双层遍历结果中,选择一个最终确定的类型
                selectedMediaType = selectedMediaType.removeQualityValue();
                Iterator var23 = this.messageConverters.iterator();
                
                //遍历所有messageConverters,看谁可以转化此类型
                while(var23.hasNext()) {
                    converter = (HttpMessageConverter)var23.next();
                    genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;
                    if (genericConverter != null) {
                        if (((GenericHttpMessageConverter)converter).canWrite((Type)targetType, valueType, selectedMediaType)) {
                            break label183;
                        }
                    } else if (converter.canWrite(valueType, selectedMediaType)) {
                        break label183;
                    }
                }
            }

总结:先看客户端可以接受哪些类型,再看服务器端哪些MessageConverter可以处理Controller中方法的返回值类型(如上述方法的Person)。根据上一步的MessageConverter结果,看他们可以转化为哪些类型。再使用双层循环,循环遍历客户端接受的类型和服务器端能处理的类型,得到最终服务器能处理的客户端接受的哪些类型。最后,找到能处理最佳匹配类型(上一步的类型集合中根据权重排序选出最佳)的MessageConverter,进行转化。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/396724.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号