Spring RestTemplate GET avec paramètres

268

Je dois passer un RESTappel qui inclut des en-têtes et des paramètres de requête personnalisés. J'ai mis mon HttpEntityavec juste les en-têtes (pas de corps), et j'utilise la RestTemplate.exchange()méthode comme suit:

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

Cela échoue du côté client, car il dispatcher servletest impossible de résoudre la demande à un gestionnaire. Après l'avoir débogué, il semble que les paramètres de demande ne soient pas envoyés.

Lorsque je fais un échange avec un POSTutilisant un corps de requête et aucun paramètre de requête, cela fonctionne très bien.

Quelqu'un a-t-il une idée?

Elwood
la source

Réponses:

481

Pour manipuler facilement les URL / chemin / paramètres / etc., vous pouvez utiliser la classe UriComponentsBuilder de Spring . Il est plus propre que la concaténation manuelle des chaînes et il prend en charge l'encodage des URL pour vous:

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
la source
10
Bon conseil. Juste changé exchangeen getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class);pour plus de simplicité.
Fernando M. Pinheiro
12
@ FernandoM.Pinheiro: Vous avez raison, mais si vous attendez un type générique dans la réponse, vous devez utiliser exchangeet fournir un ParameterizedTypeReference. L'exemple peut être encore simplifié, en le remplaçant builder.build().encode().toUri()par builder.toUriString().
mirzmaster
@Christophe L Pouvez-vous montrer comment puis-je recevoir ces paramètres de chaîne côté serveur?
KJEjava48
3
Il y a un raccourci pour obtenir l'URI: il suffit d'appelerbuilder.toUriString()
Michael Piefel
Documents de printemps pour UriComponentsBuilder . Guide expliquant les différents cas d'utilisation d' UriComponentsBuilder
Chacko Mathew
180

Les uriVariables sont également développés dans la chaîne de requête. Par exemple, l'appel suivant développera les valeurs pour le compte et le nom:

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

de sorte que l'URL de demande réelle sera

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

Consultez HierarchicalUriComponents.expandInternal (UriTemplateVariables) pour plus de détails. La version de Spring est 3.1.3.

pavel
la source
Merci - Solution très simple
Angshuman Agarwal
2
Et lors de la création de l'instance RestTemplate, vous pouvez spécifier comment ces valeurs de paramètre de requête seront développées en spécifiant DefaultUriTemplateHandler (avant Spring 5) ou DefaultUriBuilderFactory (Spring 5+). Ceci est utile lorsque vous souhaitez encoder des caractères supplémentaires tels que!, (,), Etc.
Stephen Rudolph
Mon URL a plus de 10 paramètres, est-il possible d'y parvenir avec un objet / une carte au lieu de répertorier chaque variable? Je ne peux pas utiliser non UriComponentsBuilderplus car cela provoque la génération d'une métrique différente pour chaque demande avecMicrometer
Doug
@Doug - RestTemplatea des méthodes parallèles pour spécifier soit un tableau positionnel de valeurs ( Object... uriVariables) soit une carte de valeurs nommées ( Map<String, ?> uriVariables). Sons comme la version de la carte est ce que vous voulez: restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
M. Justin
42

Depuis au moins 3 Spring, au lieu d'utiliser UriComponentsBuilderpour construire l'URL (qui est un peu bavard), un grand nombre des RestTemplateméthodes acceptent des espaces réservés dans le chemin des paramètres (non seulement exchange).

De la documentation:

De nombreuses RestTemplateméthodes acceptent un modèle d'URI et des variables de modèle d'URI, soit en tant que Stringvararg, soit en tant que Map<String,String>.

Par exemple avec un Stringvararg:

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

Ou avec un 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);

Référence: https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#rest-resttemplate-uri

Si vous regardez la JavaDoc pour RestTemplateet recherchez "URI Template", vous pouvez voir avec quelles méthodes vous pouvez utiliser des espaces réservés.

dustin.schultz
la source
35

OK, donc je suis un idiot et je confond les paramètres de requête avec les paramètres d'URL. J'espérais un peu qu'il y aurait une meilleure façon de remplir mes paramètres de requête plutôt qu'une chaîne concaténée laide, mais nous y sommes. Il s'agit simplement de créer l'URL avec les paramètres corrects. Si vous le passez en tant que String Spring, vous vous occuperez également de l'encodage.

Elwood
la source
Cela a-t-il fonctionné pour vous ? j'ai suivi la même approche en utilisant UriComponentsBuilder mais, à l'URL cible, quand je fais une request.getAttribute (), j'obtiens null.
yathirigan
47
Je ne comprends vraiment pas pourquoi cette réponse a une coche verte.
Pradeep
7
parce qu'il est l'OP
Kalpesh Soni
Quelle est donc votre solution? Merci!
Raymond Chen
18

J'essayais quelque chose de similaire, et l'exemple RoboSpice m'a aidé à le résoudre :

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
la source
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: créer un URI dynamique à l'aide d'UriComponents (variable URI et paramètres Request)

Kripesh Bista
la source
6

Conversion d'une carte de hachage en une chaîne de paramètres de requête:

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
la source
3

J'utilise une approche différente, vous pouvez être d'accord ou non mais je veux contrôler à partir du fichier .properties au lieu du code Java compilé

Dans le fichier application.properties

endpoint.url = https: // votreHôte / ressource? requestParam1 = {0} & requestParam2 = {1}

Le code Java va ici, vous pouvez écrire if ou changer de condition pour savoir si l'URL du noeud final dans le fichier .properties a @PathVariable (contient {}) ou @RequestParam (votreURL? Clé = valeur) etc ... puis appelez la méthode en conséquence. .de cette façon sa dynamique et pas besoin de coder le changement dans le futur guichet unique ...

J'essaie de donner plus d'idées que de code réel ici ... essayez d'écrire chacune une méthode générique pour @RequestParam, et @PathVariable etc ... puis appelez en conséquence si nécessaire

  @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
la source
3

Dans Spring Web 4.3.6, je vois aussi

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

Cela signifie que vous n'avez pas à créer une moche carte

Donc, si vous avez cette URL

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

Vous pouvez soit faire

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

ou

restTemplate.getForObject(url, Response.class, param [])
Kalpesh Soni
la source
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
la source
2

Si vous transmettez des paramètres non paramétrés pour RestTemplate, vous aurez une métrique pour chaque URL unique que vous transmettez, en tenant compte des paramètres. Vous souhaitez utiliser des URL paramétrées:

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

au lieu de

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

Le deuxième cas est ce que vous obtenez en utilisant la classe UriComponentsBuilder.

Une façon d'implémenter le premier comportement est la suivante:

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
la source
0

Si votre URL est http://localhost:8080/context path?msisdn={msisdn}&email={email}

puis

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

fonctionne pour la méthode d'échange resttemplate comme décrit par vous

inconnue
la source