RestTemplate源码剖析:Encode

栏目: 编程工具 · 发布时间: 7年前

内容简介:业务方经过某些业务处理之后,带过来了一系列参数。其中,有个url参数供回调使用,但这个url经业务方处理后很特殊,带过来回调时频频出错,无法正常回调给业务方。于是,通过分析部分源码,剖析一下回调的流程。说到url的回调,很容易联想到decode、encode,即编码解码操作。按照如上所说,业务方带过来了处理后的url,另附上说明,url无需解码,直接回调即可,但事情往往没那么顺利。不管是使用spring5 webclient,还是restTemplate以get请求回调业务方,均会出错。由于最终使用了res

业务方经过某些业务处理之后,带过来了一系列参数。其中,有个url参数供回调使用,但这个url经业务方处理后很特殊,带过来回调时频频出错,无法正常回调给业务方。于是,通过分析部分源码,剖析一下回调的流程。

编码 解码

说到url的回调,很容易联想到decode、encode,即编码解码操作。

通常来说,需要编码操作即意味着不适合直接传输,某些字符可能有歧义,如中文、特殊字符等等。
而通过编码之后(如 utf-8),传输给服务端,服务端自行解码,便不会出现一系列乱码问题。
复制代码

说明

按照如上所说,业务方带过来了处理后的url,另附上说明,url无需解码,直接回调即可,但事情往往没那么顺利。不管是使用spring5 webclient,还是restTemplate以get请求回调业务方,均会出错。由于最终使用了restTemplate解决了问题,对此本次剖析一下restTemplate。

注:WebClient同样可以解决问题,剖析restTemplate的因由在于综合分析了项目的引用和耦合度,不适合改动webclient模块,故而拓展了restTemplate。

剖析

  1. 先写个极其简单的单测入口,进行RestTemplate的源码分析。

    RestTemplate源码剖析:Encode
  2. 紧接着集中分析RestTemplate类

  • 请求头、请求体是由RequestCallback来处理的,请求结果是由ResponseExtractor来处理。

    RestTemplate源码剖析:Encode
  • RestTemplate默认已经初始化添加了许多对象读写转换器。

    RestTemplate源码剖析:Encode
  1. 进入execute这个重要的方法继续分析。

    RestTemplate源码剖析:Encode
  2. 可以看出,先用UriTemplateHandler构建URI,再走关键逻辑。

  • UriTemplateHandler build URI

    RestTemplate源码剖析:Encode
  • 通过构造函数的初始化,可以看到使用的是默认builder工厂

    RestTemplate源码剖析:Encode
    RestTemplate源码剖析:Encode
  • 从类继承图可以看出DefaultUriBuilderFactory实现了UriBuilderFactory,继承了UriTemplateHandler

    RestTemplate源码剖析:Encode
  • expand(String var1, Object... var2)是UriTemplateHandler接口的方法

    RestTemplate源码剖析:Encode
  1. 继续分析UriTemplateHandler接口expand方法源码
    RestTemplate源码剖析:Encode
    RestTemplate源码剖析:Encode
  • 到这里我们可以发现DefaultUriBuilderFactory默认builder工厂存在一个内部类DefaultUriBuilder

    RestTemplate源码剖析:Encode
  • 接着进入分析一下UriComponentsBuilder类的源码,从类名可以猜想到这个UriComponentsBuilder构建器可能用于创建UriComponents实例

    RestTemplate源码剖析:Encode
  • 顾名思义,UriComponents包含了构成URI的组件,以及一系列的getter and setter方法

    RestTemplate源码剖析:Encode
  • 继续着刚才初始化UriComponentsBuilder的方法

    RestTemplate源码剖析:Encode
  • 符合了内置的url pattern之后继续构建UriComponentsBuilder,通过构造器可以看出默认字符集是utf-8

    RestTemplate源码剖析:Encode
    RestTemplate源码剖析:Encode
  • 可以清晰看出初始化之后的构建器

    RestTemplate源码剖析:Encode
  1. 接下来出来外层的包裹,分析build方法
    RestTemplate源码剖析:Encode
  • 分析DefaultUriBuilderFactory build的过程 划重点!!! 因为uriVariableValues为空,不关心这个expand方法,此时注意build方法。

    RestTemplate源码剖析:Encode
  • 可以看到,build UriComponents,有个encoded参数,默认为false先不透露具体细节,只需先记住构建这次请求的UriComponents,encoded参数为false!!!

    RestTemplate源码剖析:Encode
  • 接着看encoded参数为false,会影响到哪些参数值 由encodedfalse可得到UriComponentsBuilder.EncodingHint为NONE

public UriComponents build(boolean encoded) {
      return this.buildInternal(encoded ? UriComponentsBuilder.EncodingHint.FULLY_ENCODED : (this.encodeTemplate ? UriComponentsBuilder.EncodingHint.ENCODE_TEMPLATE : UriComponentsBuilder.EncodingHint.NONE));
}
复制代码
  • 再接着就是构造HierarchicalUriComponents
    RestTemplate源码剖析:Encode
HierarchicalUriComponents uric = new HierarchicalUriComponents(this.scheme, this.fragment, this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams, hint == UriComponentsBuilder.EncodingHint.FULLY_ENCODED);
复制代码
  • 最后可以看到HierarchicalUriComponents.EncodeState为RAW 这里划重点!!!
    RestTemplate源码剖析:Encode
  1. 继续看外层的包裹,构建完之后的UriComponents
    RestTemplate源码剖析:Encode
  • 继续分析createUri,由于匹配到是默认的URI_COMPONENT,则走encode方法

    RestTemplate源码剖析:Encode
  • 本次剖析很重要的一个方法,终于要接近问题的真相了!!!

    RestTemplate源码剖析:Encode
  • 从上个提示重要的剖析点得知,构建UriComponents,encoded参数默认为false,进而从源码可得知,HierarchicalUriComponents.EncodeState的值为RAW。那么就会走下面的encodeUriComponent的方法(至于怎么encode暂不在分析范围内,看源码貌似是用字节流操作来encode,有兴趣可以阅读)

    RestTemplate源码剖析:Encode
  • 接着对segment,queryParams等进行编码

    RestTemplate源码剖析:Encode
  • 参数编码的细节,勾出来的是 Java 8的Function,实现就不贴了,源码比较简单

    RestTemplate源码剖析:Encode
  • 最终,通过比对参数ext_info的值,便得知参数被主动encode了

    RestTemplate源码剖析:Encode

解决方案

ok~~~ 分析完后,知道问题的根源在不该被encode的URL却在restTemplate的深处被动encode了,因此导致了回调的失败。既然明确问题所在,也分析过源码,解决起来就很简单了。如下:

构建UriComponentsBuilder,builder构建时传入encoded值为true,并转URI形式入参,问题解决。

RestTemplate源码剖析:Encode

枚举状态总结

为了帮助大家记忆,简单理一下状态。

  1. UriComponentsBuilder.EncodingHint

UriComponentsBuilder构建器有个枚举EncodingHint,UriComponentsBuilder构建时encoded默认为false。

  • 若encoded为false,则看有没有提供另外的encodeTemplate; 若有则值为ENCODE_TEMPLATE,否则为NONE
  • 若encoded为true时,值为FULLY_ENCODED
  1. HierarchicalUriComponents.EncodeState

HierarchicalUriComponents组件有个枚举EncodeState,其值的影响在于以上的EncodingHint。 源码的encode过滤也在于这个组件的EncodeState值!!!

  • 若EncodingHint为FULLY_ENCODED,则EncodeState为FULLY_ENCODED
  • 若EncodingHint不为FULLY_ENCODED(ENCODE_TEMPLATE或NONE),则EncodeState为RAW

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Advanced Web Metrics with Google Analytics, 2nd Edition

Advanced Web Metrics with Google Analytics, 2nd Edition

Brian Clifton / Sybex / 2010-3-15 / USD 39.99

Valuable tips and tricks for using the latest version of Google Analytics Packed with insider tips and tricks, this how-to guide is fully revised to cover the latest version of Google Analytics and sh......一起来看看 《Advanced Web Metrics with Google Analytics, 2nd Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具