ResetTemplate针对url类型的参数处理问题

  今天在对UC接口的时候,被UC的同事狠狠的鄙视了一翻。先说下场景啊:

        我们需要把一个url类型参数传递给UC这边。之前的时候用的是我个人封装的Httpclient的工具类,一直没什么问题,现在做项目改造微服务化的时候,使用是我重新封装的ResetTemplate。关于ResetTemplate的封装可以参考

       RestTemplate封装

       因为UC这边对接口设定是GET请求,或者POST请求,请求类型为:application/x-www-form-urlencoded。如果正常的POST请求是:application/json的形式的话不会出现问题的。       

      当然我们传递URL类型的参数的时候一般都需要编码的

url = URLEncoder.encode(url,"UTF-8");

       理论上编码完成之后这个url应该就是一个普通的字符串参数,但是我在使用restTemplate发起一个请求的的时候出现了诡异的情况:

  url = URLEncoder.encode(url,"UTF-8");
  String pushUrl = MessageFormat.format(UcConstant.PUSH_URL+"?link={0}&event_type={1}&event_time={2}",url,eventType,String.valueOf(createDate.getTime()));
  String result = resetTemplateService.getWithNoParams(url,String.class);

  简单的请求测试,会发现一直报错,说解析link参数出错。当时整的我一脸懵逼,然后我去问UC的同事,给我的反馈

       这种时候,当然是先打断点跟下确认下,追踪之后就一脸懵逼了,框架在构造URI对象的时候居然,对url参数进行了二次编码,还针对的是%这个字符。好吧,这个时候我得承认我是真的被打脸了呗。

       分析下:


@Override
public URI build(Map<String, ?> uriVars) {
	if (!defaultUriVariables.isEmpty()) {
		Map<String, Object> map = new HashMap<>();
		map.putAll(defaultUriVariables);
		map.putAll(uriVars);
		uriVars = map;
	}
	if (encodingMode.equals(EncodingMode.VALUES_ONLY)) {
		uriVars = UriUtils.encodeUriVariables(uriVars);
	}
	UriComponents uriComponents = this.uriComponentsBuilder.build().expand(uriVars);
	if (encodingMode.equals(EncodingMode.URI_COMPONENT)) {
		uriComponents = uriComponents.encode();
	}
	return URI.create(uriComponents.toString());
}

@Override
public URI build(Object... uriVars) {
	if (ObjectUtils.isEmpty(uriVars) && !defaultUriVariables.isEmpty()) {
		return build(Collections.emptyMap());
	}
	if (encodingMode.equals(EncodingMode.VALUES_ONLY)) {
		uriVars = UriUtils.encodeUriVariables(uriVars);
	}
	UriComponents uriComponents = this.uriComponentsBuilder.build().expand(uriVars);
	if (encodingMode.equals(EncodingMode.URI_COMPONENT)) {
		uriComponents = uriComponents.encode();
	}
	return URI.create(uriComponents.toString());
}

两个builder方法提供关键URI生成逻辑,根据最后的返回可以知道,生成URI依然是使用URI.create,所以出问题的地方就应该是 uriComponents.encode() 实现url编码的地方了,对应的代码如下

@Override
public HierarchicalUriComponents encode(Charset charset) {
	if (this.encoded) {
		return this;
	}
	String scheme = getScheme();
	String fragment = getFragment();
	String schemeTo = (scheme != null ? encodeUriComponent(scheme, charset, Type.SCHEME) : null);
	String fragmentTo = (fragment != null ? encodeUriComponent(fragment, charset, Type.FRAGMENT) : null);
	String userInfoTo = (this.userInfo != null ? encodeUriComponent(this.userInfo, charset, Type.USER_INFO) : null);
	String hostTo = (this.host != null ? encodeUriComponent(this.host, charset, getHostType()) : null);
	PathComponent pathTo = this.path.encode(charset);
	MultiValueMap<String, String> paramsTo = encodeQueryParams(charset);
	return new HierarchicalUriComponents(
			schemeTo, fragmentTo, userInfoTo, hostTo, this.port, pathTo, paramsTo, true, false);
}


// org.springframework.web.util.HierarchicalUriComponents#encodeQueryParams
private MultiValueMap<String, String> encodeQueryParams(Charset charset) {
	int size = this.queryParams.size();
	MultiValueMap<String, String> result = new LinkedMultiValueMap<>(size);
	this.queryParams.forEach((key, values) -> {
		String name = encodeUriComponent(key, charset, Type.QUERY_PARAM);
		List<String> encodedValues = new ArrayList<>(values.size());
		for (String value : values) {
			encodedValues.add(encodeUriComponent(value, charset, Type.QUERY_PARAM));
		}
		result.put(name, encodedValues);
	});
	return result;
}

       结果,构建前后输出的结果集确实不一样。对比下上面的区别,发现这个参数编码,会将请求参数中的 % 编码为 %25, 所以问题就清楚了,我传进来本来就已经是编码之后的了,结果再编码一次,相当于修改了请求参数了。
那么怎么解决呢?

1、和UC的同事说的一样,改成Httpclient作为请求发起工具。

     但是这个不是又让我回到原点了嘛。

2、遇到问题解决问题嘛,居然是在构造URI对象的时候出问题的,那么我就自己构造URI对象就OK了嘛

 URI uri = null;
        try {
            url = URLEncoder.encode(url,"UTF-8");
            String pushUrl = MessageFormat.format(UcConstant.PUSH_URL+"?link={0}&event_type={1}&event_time={2}",url,eventType,String.valueOf(createDate.getTime()));
            uri = new URI(pushUrl);
        }catch (Exception e){
            log.info("连接编码失败");
        }
        String result = resetTemplateService.getWithNoParams(uri,String.class);

OK,开始测试,没问题。

学无止境,遇到问题能转化成自己就是最大的收获!加油!!!

不要为自己的无知找借口!!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值