使用Spring RestTemplate时如何避免[]的双重编码?

How to avoid double-encoding of [] when using Spring RestTemplate?

我有一个 Spring Boot 2.2.5 应用程序调用此 REST 端点

@RequestMapping(path = "/usable/search")
public List<Provider> findUsable(
      @RequestParam(name = "country-id", required = false) Integer countryId,
      @RequestParam(name = "network-ids[]", required = false) List<Integer> networkIds,
      @RequestParam(name = "usages[]") Set<Usage> usages)

我的来电者确实

val url = clientUri.toString() + configuration.getUrl().getUseableProvidersForNetworks(); % 'providers/usable/search?'
val builder = UriComponentsBuilder.fromHttpUrl(url)
      .queryParam("usages[]", USAGES)
      .queryParam("network-ids[]", networkIds);
val response =
      restTemplate.exchange(
          builder.toUriString(),
          HttpMethod.GET,
          entity,
          new ParameterizedTypeReference<List<Provider>>() {});

其中 USAGESSet<Usage>networkIdsList<Integer>

RestTemplate 由

创建
RestTemplate eurekaRestTemplate() {
    val requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setReadTimeout(TIMEOUT);
    requestFactory.setConnectTimeout(TIMEOUT);
    return new RestTemplate(new BufferingClientHttpRequestFactory(requestFactory));
  }

问题:[] 被双重编码为​​ %255B%255D,我最终调用了

providers/usable/search?usages%255B%255D=MESSAGE_DELIVERY&network-ids%255B%255D=2145118475&network-ids%255B%255D=-1536358371

这显然行不通;服务器未命中,例如usages[]。它适用于 [] 而不是 %255B%255D.

的 curl

我怎样才能避免这种情况?

我知道 this issue and the docs,但这并没有帮助。

我认为你不应该处理使用 []。我的意思是,没有必要使用“[]”来命名您的参数。 你为什么要这么做?

val builder = UriComponentsBuilder.fromHttpUrl(url)
      .queryParam("usages", USAGES)
      .queryParam("network-ids", networkIds);

将完美适用于:

public List<Provider> findUsable(
      @RequestParam(name = "country-id", required = false) Integer countryId,
      @RequestParam(name = "network-ids", required = false) List<Integer> networkIds,
      @RequestParam(name = "usages") Set<Usage> usages)

希望对您有所帮助!

提示:在 URL 上使用 array/collection 参数时要小心,您必须考虑 URL 长度限制

解决方案是调用 UriComponentsBuilder.build(false) 指示 UriComponentsBuilder not 编码 url:

val response = restTemplate.exchange(
              builder.build(false).toUriString(),
              HttpMethod.GET,
              entity,
              new ParameterizedTypeReference<List<Provider>>() {});

这很棘手,因为使用 Mockito/JUnit 的测试会告诉您 url 是 而非 编码,而使用 [=13= 的测试] 将显示一级编码([] 变为 %5B%5D)。

如果您希望将编码后的 url 直接传递给 RestTemplate,您可以通过传递 java.net.URI 来指示 RestTemplate 不对 url 进行编码而不是字符串。 这是一个使用 org.springframework.web.util.UriComponents

的示例
String url = "https://someurl/this%2Fis%2Fencoded/";
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(url).build(true);
ResponseEntity<String[]> file = template.exchange(uriComponents.toUri(), 
HttpMethod.GET, entity,String[].class);

通过传递 UriComponentsBuilder.fromHttpUrl(url).build(true) 您实际上是让 UriComponents 知道您的 url 已经编码。