Spring RestTemplate GET com parâmetros

267

Preciso fazer uma RESTchamada que inclua cabeçalhos personalizados e parâmetros de consulta. Defino meu HttpEntitycom apenas os cabeçalhos (sem corpo) e uso o RestTemplate.exchange()método da seguinte maneira:

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

Isso falha no final do cliente, pois dispatcher servletnão é possível resolver a solicitação para um manipulador. Após a depuração, parece que os parâmetros de solicitação não estão sendo enviados.

Quando eu faço uma troca com um POSTcorpo de solicitação usando e sem parâmetros de consulta, ele funciona muito bem.

Alguém tem alguma idéia?

Elwood
fonte

Respostas:

481

Para manipular facilmente URLs / caminho / parâmetros / etc., você pode usar a classe UriComponentsBuilder do Spring . É mais limpo que concatenar manualmente as strings e cuida da codificação de URL para você:

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
fonte
10
Ótima dica. Apenas mudei exchangepara getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class);por simplicidade.
Fernando M. Pinheiro
12
@ FernandoM.Pinheiro: Você está certo, mas se está esperando um tipo genérico na resposta, precisa usar exchangee fornecer um ParameterizedTypeReference. O exemplo pode ser ainda mais simplificado, substituindo builder.build().encode().toUri()por builder.toUriString().
mirzmaster
@Christophe L Você pode mostrar como eu poderia receber esses parâmetros de string no lado do servidor?
KJEjava48
3
Existe um atalho para obter o URI: apenas chamadabuilder.toUriString()
Michael Piefel
Documentos da primavera para UriComponentsBuilder . Guia explicando vários casos de uso de UriComponentsBuilder
Chacko Mathew
180

As uriVariables também são expandidas na string de consulta. Por exemplo, a seguinte chamada expandirá os valores para conta e nome:

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

portanto, o URL da solicitação real será

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

Veja HierarchicalUriComponents.expandInternal (UriTemplateVariables) para obter mais detalhes. A versão do Spring é 3.1.3.

pavel
fonte
Obrigado - Solução muito simples
Angshuman Agarwal
2
E ao criar a instância RestTemplate, você pode especificar como esses valores de parâmetros de consulta serão expandidos especificando o DefaultUriTemplateHandler (anterior ao Spring 5) ou DefaultUriBuilderFactory (Spring 5+). Isso é útil quando você deseja codificar caracteres adicionais como!, (,) Etc.
Stephen Rudolph
Meu URL tem mais de 10 parâmetros, alguma maneira de conseguir o mesmo com um objeto / mapa em vez de listar todas as variáveis? Eu não pode usar UriComponentsBuildertanto como ele está causando-lo para gerar uma métrica diferente para cada pedido, comMicrometer
Doug
@Doug - RestTemplatepossui métodos paralelos para especificar uma matriz posicional de valores ( Object... uriVariables) ou um mapa de valores nomeados ( Map<String, ?> uriVariables). Parece que a versão do mapa é o que você quer: restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
M. Justin
42

Como pelo menos o Spring 3, em vez de usar UriComponentsBuilderpara criar a URL (que é um pouco detalhada), muitos dos RestTemplatemétodos aceitam espaços reservados no caminho para parâmetros (não apenas exchange).

A partir da documentação:

Muitos dos RestTemplatemétodos aceitam um modelo de URI e variáveis ​​de modelo de URI, como Stringvararg ou como Map<String,String>.

Por exemplo, com um Stringvararg:

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

Ou com um 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);

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

Se você olhar para o JavaDoc para RestTemplatee procure por "Template URI", você pode ver quais métodos você pode usar espaços reservados com.

dustin.schultz
fonte
35

OK, estou sendo um idiota e confundindo parâmetros de consulta com parâmetros de URL. Eu meio que esperava que houvesse uma maneira melhor de preencher meus parâmetros de consulta em vez de uma String concatenada feia, mas aqui estamos. É simplesmente um caso de criar a URL com os parâmetros corretos. Se você o passar como String Spring, também cuidará da codificação.

Elwood
fonte
Isso funcionou para você ? Eu segui a mesma abordagem de usar o UriComponentsBuilder, mas, no URL de destino, quando faço um request.getAttribute (), fico nulo.
precisa saber é o seguinte
47
Eu realmente não entendo por que essa resposta tem um sinal verde.
Pradeep
7
porque ele é o OP
Kalpesh Soni 29/11
Então, qual é a sua solução? Obrigado!
Raymond Chen
18

Eu estava tentando algo semelhante, e o exemplo do RoboSpice me ajudou a resolver isso :

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
fonte
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: criar URI dinâmico usando UriComponents (variável URI e parâmetros de solicitação)

Kripesh Bista
fonte
6

Convertendo um mapa de hash em uma sequência 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
fonte
3

Eu adoto uma abordagem diferente, você pode concordar ou não, mas quero controlar o arquivo .properties em vez do código Java compilado

Arquivo interno application.properties

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

O código Java está aqui, você pode escrever se ou alternar a condição para descobrir se o URL do terminal no arquivo .properties possui @PathVariable (contém {}) ou @RequestParam (yourURL? Key = value) etc ... e, em seguida, invoque o método de acordo. dessa forma, é dinâmico e não precisa codificar alterações no futuro balcão único ...

Estou tentando dar mais ideias do que o código real aqui ... tente escrever um método genérico para @RequestParam e @PathVariable etc ... e chame de acordo quando necessário

  @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
fonte
3

No Spring Web 4.3.6, também vejo

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

Isso significa que você não precisa criar um mapa feio

Então, se você tem esse URL

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

Você pode fazer

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

ou

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

Se você passar parâmetros não parametrizados para o RestTemplate, terá uma métrica para todos os URLs diferentes que passar, considerando os parâmetros. Você gostaria de usar URLs parametrizados:

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

ao invés de

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

O segundo caso é o que você obtém usando a classe UriComponentsBuilder.

Uma maneira de implementar o primeiro comportamento é o seguinte:

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
fonte
0

Se o seu URL for http://localhost:8080/context path?msisdn={msisdn}&email={email}

então

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

funciona para o método de troca resttemplate conforme descrito por você

desconhecido
fonte