2

Is there a way to let Spring populate RestTemplate query parameters automatically from a DTO, similarly to how it instantiates the response DTO automatically?

I wish to write something like:

RequestDto request = new RequestDto();
request.setFoo("foo");
request.setBar("bar");

ResponseDto response = restTemplate.getForObject(
        "http://example.com/api",
        ResponseDto.class, 
        request
);

Instead of:

ResponseDto response = restTemplate.getForObject(
        "http://example.com/api?foo={foo}&bar={bar}",
        ResponseDto.class,
        "foo",
        "bar"
);

Because there are many large DTOs, requiring tons of boilerplate code, which must be kept in sync with any DTO changes.

Spring 4.3.25

Snackoverflow
  • 5,332
  • 7
  • 39
  • 69

3 Answers3

3

I don't think that is directly possible. The following is not exactly using the DTO, but it does let you build the request without having to form the URL string manually. You can use Spring's UriComponentsBuilder class.

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://example.com/api")
    .queryParam("foo", "bar")
    // etc. ...
    .queryParam("bar", "foo");

String result = restTemplate.getForObject(builder.toString(), String.class);

You could loop over the DTO and build the query as above. Or without the DTO, you could use a Map<String, String> and loop over it.

Map<String, String> params = new HashMap<>();
params.put("foo", "bar");

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://example.com/api");
for (Map.Entry<String, String> entry : params.entrySet()) {
    builder.queryParam(entry.getKey(), entry.getValue());
}
String result = restTemplate.getForObject(builder.toString(), String.class);

Edit:

As crizzis suggested below, you can use Spring Cloud OpenFeign's REST Client (from Feign @QueryMap support):

The OpenFeign @QueryMap annotation provides support for POJOs to be used as GET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is incompatible with Spring because it lacks a value property.

and

Spring Cloud OpenFeign provides an equivalent @SpringQueryMap annotation, which is used to annotate a POJO or Map parameter as a query parameter map.

From your question's example:

public class RequestDto {
  private string foo;
  private string bar;
}
@FeignClient(name = "client", url = "http://example.com")
public interface FooTemplate {

    @GetMapping(path = "/api")
    String endpoint(@SpringQueryMap RequestDto requestDto);
}

Sebastian Richner
  • 782
  • 1
  • 13
  • 21
  • Feign seems cool, but where do you specify the URL `http://example.com` to comply with the question? – Snackoverflow May 05 '21 at 11:41
  • @Snackoverflow You can do this by specifying the url in the annotation, i.e. `@FeignClient(name = "client", url = "https://example.com")`. I will update the answer accordingly. – Sebastian Richner May 05 '21 at 12:19
1

You can do something like this-

UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.com/api")
        .queryParam("foo", "foo")
        .queryParam("bar", "bar");

ResponseDto response = restTemplate.getForObject(
        builder.buildAndExpand(builder).toUriString(),
        ResponseDto.class);

A more detailed answer can be found here- RestTemplate: How to send URL and query parameters together

Mukul Bansal
  • 878
  • 8
  • 10
0

How about using Feign? It allows you to describe the remote endpoint just like a Spring Controller. This includes support for query parameter DTOs.

See an example here

crizzis
  • 9,978
  • 2
  • 28
  • 47
  • The solution is unclear from the answer. Please post some example code on how we would implement this considering the code in the question. Links alone are not viable, because they are prone to expiry (even SO links, because posts can be removed or changed). – Snackoverflow May 05 '21 at 07:26
  • @Snackoverflow I added an example using Feign to my answer. – Sebastian Richner May 05 '21 at 10:04