Spring RestTemplate GET mit Parametern

266

Ich muss einen RESTAnruf tätigen , der benutzerdefinierte Header und Abfrageparameter enthält. Ich setze meine HttpEntitynur mit den Headern (kein Body) und verwende die RestTemplate.exchange()Methode wie folgt:

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json");

Map<String, String> params = new HashMap<String, String>();
params.put("msisdn", msisdn);
params.put("email", email);
params.put("clientVersion", clientVersion);
params.put("clientType", clientType);
params.put("issuerName", issuerName);
params.put("applicationName", applicationName);

HttpEntity entity = new HttpEntity(headers);

HttpEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class, params);

Dies schlägt auf Client-Seite fehl dispatcher servlet, da die Anforderung an einen Handler nicht aufgelöst werden kann. Nach dem Debuggen werden die Anforderungsparameter anscheinend nicht gesendet.

Wenn ich einen Austausch mit einem POSTAnforderungshauptteil und ohne Abfrageparameter durchführe, funktioniert dies einwandfrei.

Hat jemand irgendwelche Ideen?

Elwood
quelle

Antworten:

479

Um URLs / path / params / etc. einfach zu bearbeiten, können Sie die UriComponentsBuilder- Klasse von Spring verwenden . Es ist sauberer, Zeichenfolgen manuell zu verketten, und es kümmert sich um die URL-Codierung für Sie:

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
        .queryParam("msisdn", msisdn)
        .queryParam("email", email)
        .queryParam("clientVersion", clientVersion)
        .queryParam("clientType", clientType)
        .queryParam("issuerName", issuerName)
        .queryParam("applicationName", applicationName);

HttpEntity<?> entity = new HttpEntity<>(headers);

HttpEntity<String> response = restTemplate.exchange(
        builder.toUriString(), 
        HttpMethod.GET, 
        entity, 
        String.class);
Christophe L.
quelle
10
Toller Tipp. Einfach geändert exchangezu getForEntity: der restTemplate.getForEntity(builder.build().encode().toUri(), String.class);Einfachheit halber.
Fernando M. Pinheiro
12
@ FernandoM.Pinheiro: Sie haben Recht, aber wenn Sie einen generischen Typ in der Antwort erwarten, müssen Sie einen verwenden exchangeund angeben ParameterizedTypeReference. Das Beispiel kann jedoch weiter vereinfacht werden, indem es builder.build().encode().toUri()durch ersetzt wird builder.toUriString().
Mirzmaster
@Christophe L Kannst du zeigen, wie ich diese String-Parameter auf der Serverseite empfangen kann?
KJEjava48
3
Es gibt eine Abkürzung, um die URI zu erhalten: Rufen Sie einfach anbuilder.toUriString()
Michael Piefel
Frühlingsdokumente für UriComponentsBuilder . Handbuch zur Erläuterung verschiedener Anwendungsfälle von UriComponentsBuilder
Chacko Mathew
178

Die uriVariables werden auch in der Abfragezeichenfolge erweitert. Der folgende Aufruf erweitert beispielsweise die Werte für Konto und Name:

restTemplate.exchange("http://my-rest-url.org/rest/account/{account}?name={name}",
    HttpMethod.GET,
    httpEntity,
    clazz,
    "my-account",
    "my-name"
);

Die tatsächliche Anforderungs-URL lautet also

http://my-rest-url.org/rest/account/my-account?name=my-name

Weitere Informationen finden Sie unter HierarchicalUriComponents.expandInternal (UriTemplateVariables). Die Version von Spring ist 3.1.3.

pavel
quelle
Danke - Sehr einfache Lösung
Angshuman Agarwal
2
Beim Erstellen der RestTemplate-Instanz können Sie angeben, wie diese Abfrageparameterwerte erweitert werden sollen, indem Sie den DefaultUriTemplateHandler (vor Spring 5) oder DefaultUriBuilderFactory (Spring 5+) angeben. Dies ist nützlich, wenn Sie zusätzliche Zeichen wie!, (,)
Stephen Rudolph
Meine URL hat mehr als 10 Parameter. Gibt es eine Möglichkeit, dasselbe mit einem Objekt / einer Karte zu erreichen, anstatt jede Variable aufzulisten? Ich kann es auch nicht verwenden, UriComponentsBuilderda es dazu führt, dass für jede Anfrage mitMicrometer
Doug
@Doug - RestTemplateverfügt über parallele Methoden zum Angeben eines Positionsarrays von Werten ( Object... uriVariables) oder einer Karte mit benannten Werten ( Map<String, ?> uriVariables). Klingt so, als ob die Kartenversion genau das ist, was Sie wollen : restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
M. Justin
42

Da zumindest Feder 3, anstelle der Verwendung UriComponentsBuilderder URL aufzubauen (was ein Bit ausführlich ist), viele der RestTemplateVerfahren akzeptieren Platzhalter in den Pfad für die Parameter (nicht nur exchange).

Aus der Dokumentation:

Viele der RestTemplateMethoden akzeptieren eine URI-Vorlage und URI-Vorlagenvariablen, entweder als Stringvararg oder als Map<String,String>.

Zum Beispiel mit einem StringVararg:

restTemplate.getForObject(
   "http://example.com/hotels/{hotel}/rooms/{room}", String.class, "42", "21");

Oder mit einem Map<String, String>:

Map<String, String> vars = new HashMap<>();
vars.put("hotel", "42");
vars.put("room", "21");

restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{room}", 
    String.class, vars);

Referenz: https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#rest-resttemplate-uri

Wenn Sie im JavaDoc nach RestTemplate"URI-Vorlage" suchen, können Sie sehen, mit welchen Methoden Sie Platzhalter verwenden können.

Dustin.schultz
quelle
35

OK, ich bin ein Idiot und verwechsle Abfrageparameter mit URL-Parametern. Ich hatte gehofft, dass es eine bessere Möglichkeit gibt, meine Abfrageparameter zu füllen, als eine hässliche verkettete Zeichenfolge, aber da sind wir. Es geht einfach darum, die URL mit den richtigen Parametern zu erstellen. Wenn Sie es als String übergeben, übernimmt Spring Spring auch die Codierung für Sie.

Elwood
quelle
Hat es bei dir funktioniert? Ich habe den gleichen Ansatz wie bei der Verwendung des UriComponentsBuilder verfolgt, aber an der Ziel-URL erhalte ich null, wenn ich eine request.getAttribute () mache.
Yathirigan
47
Ich verstehe ernsthaft nicht, warum diese Antwort grünes Häkchen hat.
Pradeep
7
weil er der OP ist
Kalpesh Soni
Was ist Ihre Lösung? Vielen Dank!
Raymond Chen
18

Ich habe etwas Ähnliches versucht, und das RoboSpice-Beispiel hat mir dabei geholfen, es herauszufinden :

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json");

HttpEntity<String> request = new HttpEntity<>(input, createHeader());

String url = "http://awesomesite.org";
Uri.Builder uriBuilder = Uri.parse(url).buildUpon();
uriBuilder.appendQueryParameter(key, value);
uriBuilder.appendQueryParameter(key, value);
...

String url = uriBuilder.build().toString();

HttpEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request , String.class);
elBradford
quelle
15
    String uri = http://my-rest-url.org/rest/account/{account};

    Map<String, String> uriParam = new HashMap<>();
    uriParam.put("account", "my_account");

    UriComponents builder = UriComponentsBuilder.fromHttpUrl(uri)
                .queryParam("pageSize","2")
                        .queryParam("page","0")
                        .queryParam("name","my_name").build();

    HttpEntity<String> requestEntity = new HttpEntity<>(null, getHeaders());

    ResponseEntity<String> strResponse = restTemplate.exchange(builder.toUriString(),HttpMethod.GET, requestEntity,
                        String.class,uriParam);

    //final URL: http://my-rest-url.org/rest/account/my_account?pageSize=2&page=0&name=my_name

RestTemplate: Erstellen eines dynamischen URI mithilfe von UriComponents (URI-Variable und Anforderungsparameter)

Kripesh Bista
quelle
6

Konvertieren einer Hash-Map in eine Zeichenfolge von Abfrageparametern:

Map<String, String> params = new HashMap<>();
params.put("msisdn", msisdn);
params.put("email", email);
params.put("clientVersion", clientVersion);
params.put("clientType", clientType);
params.put("issuerName", issuerName);
params.put("applicationName", applicationName);

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
    builder.queryParam(entry.getKey(), entry.getValue());
}

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json");

HttpEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity(headers), String.class);
Ilya Lysenko
quelle
3

Ich verfolge einen anderen Ansatz, Sie können zustimmen oder nicht, aber ich möchte aus der .properties-Datei anstelle von kompiliertem Java-Code steuern

In der Datei application.properties

endpoint.url = https: // yourHost / resource? requestParam1 = {0} & requestParam2 = {1}

Hier finden Sie Java-Code. Sie können if- oder switch-Bedingungen schreiben, um herauszufinden, ob die Endpunkt-URL in der .properties-Datei @PathVariable (enthält {}) oder @RequestParam (yourURL? Key = value) usw. enthält. Rufen Sie dann die Methode entsprechend auf. Auf diese Weise ist es dynamisch und muss in Zukunft nicht mehr aus einer Hand geändert werden ...

Ich versuche hier mehr Ideen als tatsächlichen Code zu geben ... versuche, jeweils eine generische Methode für @RequestParam und @PathVariable usw. zu schreiben ... und rufe sie dann bei Bedarf entsprechend auf

  @Value("${endpoint.url}")
  private String endpointURL;
  // you can use variable args feature in Java
  public String requestParamMethodNameHere(String value1, String value2) {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate
           .getMessageConverters()
           .add(new MappingJackson2HttpMessageConverter());

    HttpHeaders headers = new HttpHeaders();
    headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
    HttpEntity<String> entity = new HttpEntity<>(headers);

    try {
      String formatted_URL = MessageFormat.format(endpointURL, value1, value2);
      ResponseEntity<String> response = restTemplate.exchange(
                    formatted_URL ,
                    HttpMethod.GET,
                    entity,
                    String.class);
     return response.getBody();
    } catch (Exception e) { e.printStackTrace(); }
Java_Fire_Within
quelle
3

In Spring Web 4.3.6 sehe ich auch

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)

Das heißt, Sie müssen keine hässliche Karte erstellen

Also, wenn Sie diese URL haben

http://my-url/action?param1={param1}&param2={param2}

Sie können entweder tun

restTemplate.getForObject(url, Response.class, param1, param2)

oder

restTemplate.getForObject(url, Response.class, param [])
Kalpesh Soni
quelle
2
public static void main(String[] args) {
         HttpHeaders httpHeaders = new HttpHeaders();
         httpHeaders.set("Accept", MediaType.APPLICATION_JSON_VALUE);
         final String url = "https://host:port/contract/{code}";
         Map<String, String> params = new HashMap<String, String>();
         params.put("code", "123456");
         HttpEntity<?> httpEntity  = new HttpEntity<>(httpHeaders); 
         RestTemplate restTemplate  = new RestTemplate();
         restTemplate.exchange(url, HttpMethod.GET, httpEntity,String.class, params);
    }
Neeraj Gahlawat
quelle
2

Wenn Sie nicht parametrisierte Parameter für RestTemplate übergeben, haben Sie eine Metrik für jede einzelne unterschiedliche URL, die Sie unter Berücksichtigung der Parameter übergeben. Sie möchten parametrisierte URLs verwenden:

http://my-url/action?param1={param1}&param2={param2}

anstatt

http://my-url/action?param1=XXXX&param2=YYYY

Der zweite Fall ist der, den Sie mit der UriComponentsBuilder-Klasse erhalten.

Eine Möglichkeit, das erste Verhalten zu implementieren, ist die folgende:

Map<String, Object> params = new HashMap<>();
params.put("param1", "XXXX");
params.put("param2", "YYYY");

String url = "http://my-url/action?%s";

String parametrizedArgs = params.keySet().stream().map(k ->
    String.format("%s={%s}", k, k)
).collect(Collectors.joining("&"));

HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> entity = new HttpEntity<>(headers);

restTemplate.exchange(String.format(url, parametrizedArgs), HttpMethod.GET, entity, String.class, params);
Dalton
quelle
0

Wenn deine URL ist http://localhost:8080/context path?msisdn={msisdn}&email={email}

dann

Map<String,Object> queryParams=new HashMap<>();
queryParams.put("msisdn",your value)
queryParams.put("email",your value)

funktioniert für die von Ihnen beschriebene Resttemplate-Austauschmethode

Unbekannt
quelle