Spring RestTemplate GET z parametrami

266

Muszę wykonać RESTpołączenie, które zawiera niestandardowe nagłówki i parametry zapytania. Ustawiam HttpEntitytylko z nagłówkami (bez treści) i używam RestTemplate.exchange()metody w następujący sposób:

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

Nie udaje się to po stronie klienta, dispatcher servletponieważ nie można rozwiązać żądania do programu obsługi. Po debugowaniu wygląda na to, że parametry żądania nie są wysyłane.

Kiedy dokonuję wymiany za POSTpomocą treści żądania i bez parametrów zapytania, działa to dobrze.

Czy ktoś ma jakieś pomysły?

Elwood
źródło

Odpowiedzi:

480

Aby łatwo manipulować adresami URL / ścieżką / parametrami / itp., Możesz użyć klasy UriComponentsBuilder firmy Spring . Jest czystszy, że ręcznie łączy ciągi i dba o kodowanie adresu URL dla Ciebie:

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.
źródło
10
Świetna wskazówka. Właśnie zmieniono exchangena getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class);dla uproszczenia.
Fernando M. Pinheiro
12
@ FernandoM.Pinheiro: Masz rację, ale jeśli oczekujesz ogólnego rodzaju odpowiedzi, musisz użyć exchangei podać ParameterizedTypeReference. Przykład można jednak jeszcze uprościć, zastępując builder.build().encode().toUri()go builder.toUriString().
mirzmaster,
@Christophe L Czy możesz pokazać, w jaki sposób mogę otrzymać te parametry ciągu po stronie serwera?
KJEjava48
3
Istnieje skrót do uzyskania identyfikatora URI: wystarczy zadzwonićbuilder.toUriString()
Michael Piefel
Wiosenne dokumenty dla UriComponentsBuilder . Przewodnik wyjaśniający różne przypadki użycia UriComponentsBuilder
Chacko Mathew
179

Zmienne uriVariables są również rozwijane w ciągu zapytania. Na przykład następujące wywołanie rozszerzy wartości zarówno konta, jak i nazwy:

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

więc rzeczywisty adres URL żądania będzie

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

Spójrz na HierarchicalUriComponents.expandInternal (UriTemplateVariables), aby uzyskać więcej informacji. Wersja Spring to 3.1.3.

pavel
źródło
Dzięki - Bardzo proste rozwiązanie
Angshuman Agarwal
2
Podczas tworzenia instancji RestTemplate można określić, w jaki sposób wartości parametrów zapytania będą rozszerzane, określając DefaultUriTemplateHandler (przed Spring 5) lub DefaultUriBuilderFactory (Spring 5+). Jest to przydatne, gdy chcesz zakodować dodatkowe znaki, takie jak!, (,) Itp.
Stephen Rudolph
Mój adres URL ma ponad 10 parametrów, czy jest jakiś sposób, aby osiągnąć to samo z obiektem / mapą zamiast listy każdej zmiennej? Nie mogę również użyć UriComponentsBuilder, ponieważ powoduje to generowanie innej metryki dla każdego żądania zMicrometer
Doug
@Doug - RestTemplatema równoległe metody określania albo tablicy pozycyjnej wartości ( Object... uriVariables), albo mapy nazwanych wartości ( Map<String, ?> uriVariables). Brzmi jak wersja mapy jest to, co chcesz: restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
M. Justin,
42

Co najmniej od wiosny 3, zamiast używać UriComponentsBuilderdo budowania URL (co jest nieco rozwlekły), wiele z RestTemplatemetod zaakceptować zastępcze w ścieżce dla parametrów (nie tylko exchange).

Z dokumentacji:

Wiele RestTemplatemetod akceptuje szablon URI i zmienne szablonu URI, jako Stringvararg lub as Map<String,String>.

Na przykład z Stringvararg:

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

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

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

Jeśli spojrzeć na JavaDoc dla RestTemplatei wyszukać „URI Szablon”, można zobaczyć, jakie metody można korzystać z zastępczych.

dustin.schultz
źródło
35

OK, więc jestem idiotą i mylę parametry zapytania z parametrami adresu URL. Miałem trochę nadziei, że będzie lepszy sposób na wypełnienie moich parametrów zapytania niż brzydki połączony ciąg, ale tak jest. Jest to po prostu przypadek zbudowania adresu URL z poprawnymi parametrami. Jeśli przekażesz go jako String String, zajmie się także kodowaniem.

Elwood
źródło
czy to zadziałało dla ciebie? zastosowałem to samo podejście, używając UriComponentsBuilder, ale pod docelowym adresem URL, gdy wykonuję request.getAttribute (), otrzymuję wartość null.
yathirigan
47
Naprawdę nie rozumiem, dlaczego ta odpowiedź ma zielony tyk.
Pradeep
7
ponieważ on jest OP
Kalpesh Soni
Więc jakie jest twoje rozwiązanie? Dzięki!
Raymond Chen
18

Próbowałem czegoś podobnego, a przykład RoboSpice pomógł mi to wypracować :

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
źródło
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: Zbuduj dynamiczny URI przy użyciu UriComponents (zmienna URI i parametry żądania)

Kripesh Bista
źródło
6

Konwersja mapy skrótu na ciąg parametrów zapytania:

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
źródło
3

Mam inne podejście, możesz się zgodzić lub nie, ale chcę kontrolować z pliku .properties zamiast skompilowanego kodu Java

Wewnątrz pliku application.properties

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

Tutaj jest kod Java, możesz napisać if lub zmienić warunek, aby dowiedzieć się, czy adres URL punktu końcowego w pliku .properties ma @PathVariable (zawiera {}) lub @RequestParam (twójURL? Klucz = wartość) itp., A następnie odpowiednio wywołaj metodę .. , w ten sposób jego dynamika i nie trzeba zmieniać kodu w przyszłości one stop shop ...

Próbuję podać więcej pomysłów niż rzeczywisty kod tutaj ... spróbuj napisać metodę ogólną dla @RequestParam i @PathVariable itp. ... a następnie odpowiednio zadzwoń, gdy zajdzie taka potrzeba

  @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
źródło
3

W Spring Web 4.3.6 również widzę

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

Oznacza to, że nie musisz tworzyć brzydkiej mapy

Więc jeśli masz ten adres URL

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

Możesz albo

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

lub

restTemplate.getForObject(url, Response.class, param [])
Kalpesh Soni
źródło
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
źródło
2

Jeśli przekażesz parametry nie sparametryzowane dla RestTemplate, będziesz mieć jedną Metrykę dla każdego z osobna podając inny adres URL, biorąc pod uwagę parametry. Chcesz użyć sparametryzowanych adresów URL:

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

zamiast

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

Drugi przypadek to to, co otrzymujesz za pomocą klasy UriComponentsBuilder.

Jednym ze sposobów wdrożenia pierwszego zachowania jest:

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
źródło
0

Jeśli masz adres URL http://localhost:8080/context path?msisdn={msisdn}&email={email}

następnie

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

działa w przypadku metody wymiany szablonów zgodnie z opisem użytkownika

nieznany
źródło