Spring UriTemplateHandler.expand未编码加号(+)符号问题及解决咨询
解决RestTemplate UriTemplateHandler处理加号(+)编码问题的方案
针对你遇到的RestTemplate中restTemplate.getUriTemplateHandler().expand()方法对查询参数里的+不编码、传入%2B又双重编码的问题,以下是无需上游修复的几种解决办法:
1. 全局自定义UriTemplateHandler(推荐全局生效)
Spring默认的DefaultUriBuilderFactory遵循application/x-www-form-urlencoded规则,将查询参数中的+视为空格等价物,因此不会编码。我们可以自定义Handler强制将+编码为%2B:
import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriUtils; import java.nio.charset.StandardCharsets; public class PlusEncodingUriTemplateHandler extends DefaultUriBuilderFactory { @Override protected UriComponentsBuilder createUriComponentsBuilder(String uriTemplate) { return super.createUriComponentsBuilder(uriTemplate) .queryParamEncoder((paramName, paramValue) -> { // 先按标准规则编码,再替换+为%2B String encoded = UriUtils.encodeQueryParam(paramValue, StandardCharsets.UTF_8); return encoded.replace("+", "%2B"); }); } }
然后将自定义Handler设置到RestTemplate:
RestTemplate restTemplate = new RestTemplate(); restTemplate.setUriTemplateHandler(new PlusEncodingUriTemplateHandler());
之后所有通过该RestTemplate发起的请求,查询参数中的+都会被编码为%2B,且不会出现双重编码问题。
2. 单个请求手动控制编码(适合局部特殊场景)
如果只需要针对个别请求处理,可以手动预编码参数并避免双重编码:
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriUtils; import java.nio.charset.StandardCharsets; import java.net.URI; // 示例参数 String pathTemplate = "/api/data?key={key}"; String paramValue = "test+value"; // 预编码参数,替换+为%2B String encodedParam = UriUtils.encodeQueryParam(paramValue, StandardCharsets.UTF_8) .replace("+", "%2B"); // 构建URI时确保仅编码一次 URI finalUri = UriComponentsBuilder.fromUriString(pathTemplate) .buildAndExpand(encodedParam) .encode(StandardCharsets.UTF_8) .toUri(); // 发起请求 restTemplate.getForObject(finalUri, YourResponseClass.class);
3. 直接用UriComponentsBuilder构建URI(最灵活)
完全绕过expand()方法,直接构建URI并手动控制编码逻辑:
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriUtils; import java.nio.charset.StandardCharsets; import java.net.URI; String paramValue = "a+b+c"; URI uri = UriComponentsBuilder.fromHttpUrl("https://your-domain.com/api/resource") // 手动编码参数并替换+ .queryParam("param", UriUtils.encodeQueryParam(paramValue, StandardCharsets.UTF_8).replace("+", "%2B")) .build(true) // true表示使用已编码的参数,不再次编码 .toUri(); restTemplate.getForObject(uri, String.class);
原理说明
- 默认规则中,查询参数的+被解析为空格,这是HTTP表单编码的标准行为,但部分服务端会将+视为普通字符,因此需要强制编码。
- 自定义Handler通过覆盖编码逻辑,将+替换为%2B;手动编码则通过预处理参数,再关闭自动编码避免双重编码。
内容的提问来源于stack exchange,提问作者ystan-
相关产品推荐
相关产品推荐

