Spring RestTemplate GET con parámetros


268

Tengo que hacer una RESTllamada que incluya encabezados personalizados y parámetros de consulta. Configuré mi HttpEntitysolo con los encabezados (sin cuerpo), y uso el RestTemplate.exchange()método de la siguiente manera:

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

Esto falla en el extremo del cliente al dispatcher servletno poder resolver la solicitud a un controlador. Después de depurarlo, parece que los parámetros de solicitud no se están enviando.

Cuando hago un intercambio con el POSTuso de un cuerpo de solicitud y sin parámetros de consulta, funciona bien.

¿Alguien tiene alguna idea?

Respuestas:


481

Para manipular fácilmente las URL / ruta / parámetros / etc., puede utilizar la clase UriComponentsBuilder de Spring . Es más limpio que concatena cadenas manualmente y se encarga de la codificación de URL por usted:

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

10
Gran consejo Acaba de cambiar exchangea getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class);por simplicidad.
Fernando M. Pinheiro

12
@ FernandoM.Pinheiro: Tienes razón, pero si esperas un tipo genérico en la respuesta, entonces debes usar exchangey proporcionar un ParameterizedTypeReference. Sin embargo, el ejemplo se puede simplificar aún más, reemplazando builder.build().encode().toUri()con builder.toUriString().
mirzmaster

@Christophe L ¿Puede mostrar cómo podría recibir estos parámetros de cadena en el lado del servidor?
KJEjava48

3
Hay un atajo para obtener el URI: solo llamebuilder.toUriString()
Michael Piefel el

Documentos de primavera para UriComponentsBuilder . Guía que explica varios casos de uso de UriComponentsBuilder
Chacko Mathew

180

Las uriVariables también se expanden en la cadena de consulta. Por ejemplo, la siguiente llamada expandirá los valores para ambos, cuenta y nombre:

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

entonces la URL de solicitud real será

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

Mire HierarchicalUriComponents.expandInternal (UriTemplateVariables) para más detalles. La versión de Spring es 3.1.3.


Gracias - Solución muy simple
Angshuman Agarwal

2
Y al crear la instancia de RestTemplate, puede especificar cómo se expandirán esos valores de parámetros de consulta especificando DefaultUriTemplateHandler (antes de Spring 5) o DefaultUriBuilderFactory (Spring 5+). Esto es útil cuando desea codificar caracteres adicionales como!, (,), Etc.
Stephen Rudolph

Mi URL tiene más de 10 parámetros, ¿alguna forma de lograr lo mismo con un objeto / mapa en lugar de enumerar todas las variables? No puedo usar UriComponentsBuilderninguno, ya que está causando que genere una métrica diferente para cada solicitud conMicrometer
Doug

@Doug: RestTemplatetiene métodos paralelos para especificar una matriz posicional de valores ( Object... uriVariables) o un mapa de valores con nombre ( Map<String, ?> uriVariables). Sonidos como la versión del mapa es lo que quiere: restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
M. Justin

42

Desde al menos Spring 3, en lugar de usar UriComponentsBuilderpara construir la URL (que es un poco detallado), muchos de los RestTemplatemétodos aceptan marcadores de posición en la ruta de los parámetros (no solo exchange).

De la documentación:

Muchos de los RestTemplatemétodos aceptan una plantilla de URI y variables de plantilla de URI, ya sea como Stringvararg o como Map<String,String>.

Por ejemplo con un Stringvararg:

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

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

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

Si nos fijamos en el JavaDoc para RestTemplatey la búsqueda de "Plantilla URI", se puede ver qué métodos se pueden utilizar marcadores de posición con.


35

Bien, estoy siendo un idiota y estoy confundiendo los parámetros de consulta con los parámetros de URL. Esperaba que hubiera una mejor manera de llenar los parámetros de mi consulta en lugar de una cadena concatenada fea, pero ahí estamos. Es simplemente un caso de construir la URL con los parámetros correctos. Si lo pasa como String Spring también se encargará de la codificación por usted.


funcionó para ti? Seguí el mismo enfoque de usar UriComponentsBuilder pero, en la URL de destino, cuando hago un request.getAttribute (), obtengo un valor nulo.
yathirigan

47
En serio, no entiendo por qué esta respuesta tiene un tic verde.
Pradeep

77
porque él es el OP
Kalpesh Soni

Entonces, ¿cuál es tu solución? ¡Gracias!
Raymond Chen el

18

Estaba intentando algo similar, y el ejemplo de RoboSpice me ayudó a resolverlo :

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

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: Construya URI dinámico usando UriComponents (variable URI y parámetros de solicitud)


6

Conversión de un mapa hash en una cadena de parámetros de consulta:

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

3

Tomo un enfoque diferente, puede estar de acuerdo o no, pero quiero controlar desde el archivo .properties en lugar del código compilado de Java

Dentro del archivo application.properties

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

El código Java va aquí, puede escribir if o cambiar la condición para averiguar si la URL del punto final en el archivo .properties tiene @PathVariable (contiene {}) o @RequestParam (yourURL? Key = value), etc. ... luego invoque el método en consecuencia. de esa manera es dinámico y no necesita cambiar el código en una ventanilla única en el futuro ...

Estoy tratando de dar más idea que el código real aquí ... intente escribir un método genérico para @RequestParam y @PathVariable, etc. ... luego llame en consecuencia cuando sea necesario

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

3

En Spring Web 4.3.6 también veo

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

Eso significa que no tienes que crear un mapa feo

Entonces, si tienes esta url

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

Puedes hacer

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

o

restTemplate.getForObject(url, Response.class, param [])

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

2

Si pasa parámetros no parametrizados para RestTemplate, tendrá una métrica para cada una de las diferentes URL que pase, teniendo en cuenta los parámetros. Te gustaría usar URL parametrizadas:

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

en vez de

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

El segundo caso es lo que obtienes al usar la clase UriComponentsBuilder.

Una forma de implementar el primer comportamiento es la siguiente:

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

0

Si tu url es http://localhost:8080/context path?msisdn={msisdn}&email={email}

luego

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

funciona para el método de cambio de plantilla como lo describió usted

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.