Spring Boot: How to Add Params to TestRestTemplate.postForEntity?

Viewed 182

I am trying to add parameters to a postForEntity request but it seems to never go through. Here is the minimum reproducible code:

@Test
public void test()
{
    String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/test")
            .queryParam("update")
            // .queryParam("update", "{update}") //This does not work either
            .encode()
            .toUriString();

    HashMap<String, String> paramValues = new HashMap<>();
    paramValues.put("update", "true");

    HttpEntity<AnimateRequest> httpEntity = new HttpEntity<>(null, new HttpHeaders());

    ResponseEntity<Boolean> response = this.template.postForEntity(
            urlTemplate,
            httpEntity,
            Boolean.class,
            paramValues);
    boolean bb = response.getBody();
}

In a controller:

@PostMapping(value = "/test")
public ResponseEntity<Boolean> tester(@RequestParam(name="update", required = false) boolean rr)
{
    return ResponseEntity
            .ok()
            .contentType(MediaType.TEXT_PLAIN)
            .body(rr);
}

Errors with:

org.springframework.web.client.RestClientException: Error while extracting response for type [class java.lang.Boolean] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token

at [Source: (PushbackInputStream); line: 1, column: 1]

2 Answers

I'm not exactly sure why, but the return contentType() in the controller return needed to be removed. For some reason Jackson was not parsing the return type properly. Then either a primitive boolean or the class Boolean works.

Main problem, is that your implementation tries to respond with text/plain without registering any Boolean to text/plain converter.

You have several options to solve that:

  1. Just return (response with) the "default (media) type":

    return ResponseEntity
     .ok()
     .body(rr);
    
  2. If you need to respond with text/plain, then

    a. ResponseEntity<String> would be the straight-forward solution:

    @PostMapping(value = "/test2")
    public ResponseEntity<String> // String!!!  not Boolean ... {
      return ResponseEntity
       .ok()
       .contentType(MediaType.TEXT_PLAIN) // explicit media type here or as @PostMapping.produces attribute
       .body(String.valueOf(rr)); // convert the boolean here
    }
    

    b. Or to really register a custom(ized) (Boolean<->text/plain) converter ...

Then we could test 1. (with TestRestTemplate) like:

    @Test
    public void test1() throws URISyntaxException {
      final String baseUrl = "http://localhost:" + randomServerPort + "/test/";
      URI uri = new URI(baseUrl);
      // true:
      ResponseEntity<Boolean> result = this.restTemplate.postForEntity(uri + "?update=true", null /*here goes normally the "post body"/entity*/, Boolean.class);

      assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
      assertThat(result.getBody()).isTrue();
    }

and 2. accordingly with string result:

    @Test
    public void test2() throws URISyntaxException {
      final String baseUrl = "http://localhost:" + randomServerPort + "/test2/";
      URI uri = new URI(baseUrl);
      ResponseEntity<String> result = this.restTemplate.postForEntity(uri + "?update=true", null, String.class);

      assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
      assertThat(Boolean.valueOf(result.getBody())).isTrue();
    }

Please consider, we have several (out-of-the-box) options regarding "content encoding" and "how to pass this update parameter".

For brevity, simplicity and lack of need I omit any post objects and headers (null, which would go as the second method argument), and passed the only parameter as "URI parameter".


Also consider the note on RestTemplate, which could also be applied to TerstRestTemplate:

NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

Related