Search code examples
javaspring-booturlurlencodequery-string

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


I've a Spring Boot 2.2.5 application calling this REST endpoint

@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)

My caller does

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>>() {});

where USAGES is a Set<Usage> and networkIds is a List<Integer>.

The RestTemplate is created by

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

Problem: The [] get double encoded to %255B%255D and I end up with a call to

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

Which obviously doesn't work; the server misses e.g. usages[]. It works with curl with [] instead of %255B%255D.

How can I avoid that?

I'm aware of this issue and the docs, but that didn't help yet.


Solution

  • The solution was to call UriComponentsBuilder.build(false) to instruct the UriComponentsBuilder not to encode the url:

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

    This is tricky because a test with Mockito/JUnit of this will tell you that the url is not encoded while a test with MockRestServiceServer will show one level of encoding ([] become %5B%5D).