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?

Elwood
fuente

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);
Christophe L
fuente
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.

pavel
fuente
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.

dustin.schultz
fuente
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.

Elwood
fuente
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);
elBradford
fuente
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)

Kripesh Bista
fuente
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);
Ilya Lysenko
fuente
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(); }
Java_Fire_Within
fuente
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 [])
Kalpesh Soni
fuente
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
fuente
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);
Dalton
fuente
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

desconocido
fuente