Spring RestTemplate GET с параметрами


268

Я должен сделать RESTзвонок, который включает в себя пользовательские заголовки и параметры запроса. Я установил свой HttpEntityтолько с заголовками (без тела), и я использую RestTemplate.exchange()метод следующим образом:

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

Это терпит неудачу на стороне клиента с dispatcher servletнеспособностью разрешить запрос к обработчику. После его отладки похоже, что параметры запроса не отправляются.

Когда я делаю обмен с POSTиспользованием тела запроса и без параметров запроса, он работает просто отлично.

У кого-нибудь есть идеи?

Ответы:


481

Чтобы легко манипулировать URL / path / params / и т.д., вы можете использовать класс Spring UriComponentsBuilder . Он чище, что вручную объединяет строки и заботится о кодировке URL для вас:

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
Отличный совет. Просто изменилось exchangeна getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class);для простоты.
Фернандо М. Пиньейру

12
@ FernandoM.Pinheiro: Вы правы, но если вы ожидаете универсальный тип в ответе, то вам нужно использовать exchangeи предоставить ParameterizedTypeReference. Этот пример может быть еще более упрощен, если заменить builder.build().encode().toUri()на builder.toUriString().
mirzmaster

@Christophe L Можете ли вы показать, как я могу получить эти строковые параметры на стороне сервера ??
KJEjava48

3
Существует ярлык для получения URI: просто позвонитеbuilder.toUriString()
Michael Piefel

Spring документы для UriComponentsBuilder . Руководство, объясняющее различные варианты использования UriComponentsBuilder
Чако Мэтью

180

UriVariables также раскрываются в строке запроса. Например, следующий вызов расширит значения для учетной записи и имени:

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

поэтому фактический URL запроса будет

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

Посмотрите на HierarchicalUriComponents.expandInternal (UriTemplateVariables) для более подробной информации. Версия Spring 3.1.3.


Спасибо - Очень простое решение
Angshuman Agarwal

2
И при создании экземпляра RestTemplate вы можете указать, как будут расширяться значения этих параметров запроса, указав DefaultUriTemplateHandler (до Spring 5) или DefaultUriBuilderFactory (Spring 5+). Это полезно, когда вы хотите закодировать дополнительные символы, такие как!, (,) И т. Д.
Стивен Рудольф

Мой URL имеет более 10 параметров, любой способ достичь того же с помощью объекта / карты вместо перечисления каждой переменной? Я не могу использовать UriComponentsBuilderни того, ни другого, так как это приводит к тому, что он генерирует разные метрики для каждого запроса с помощьюMicrometer
Даг

@Doug - RestTemplateимеет параллельные методы для определения либо позиционного массива значений ( Object... uriVariables), либо карты именованных значений ( Map<String, ?> uriVariables). Звуки как версия карты является то , что вы хотите: restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap).
М. Джастин

42

Поскольку по крайней мере Spring 3, вместо того , чтобы использовать UriComponentsBuilderдля создания URL (который немного многословный), многие из RestTemplateметодов принимают заполнители в пути для параметров (не только exchange).

Из документации:

Многие из RestTemplateметодов принимают переменные шаблона URI и шаблона URI, либо как Stringпеременные, либо как Map<String,String>.

Например с Stringварагом:

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

Или с 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);

Ссылка: https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#rest-resttemplate-uri

Если вы посмотрите на JavaDoc для RestTemplateи поиска «URI шаблона», вы можете увидеть , какие методы можно использовать заполнители.


35

Итак, я идиот и путаю параметры запроса с параметрами url. Я надеялся, что был бы более хороший способ заполнить параметры моего запроса, а не безобразно сцепленную строку, но мы здесь. Это просто случай построения URL с правильными параметрами. Если вы передадите его как String Spring, он также позаботится о кодировке для вас.


у тебя это сработало? я следовал тому же подходу использования UriComponentsBuilder, но по целевому URL, когда я делаю request.getAttribute (), я получаю ноль.
Йатириган

47
Я серьезно не понимаю, почему этот ответ имеет зеленую галочку.
Прадип

7
потому что он ОП
Kalpesh Soni

Так какое у вас решение? Спасибо!
Раймонд Чен

18

Я пытался что-то подобное, и пример RoboSpice помог мне разобраться :

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: создание динамического URI с использованием UriComponents (переменная URI и параметры запроса)


6

Преобразование хэш-карты в строку параметров запроса:

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

Я использую другой подход, вы можете согласиться или нет, но я хочу управлять из файла .properties вместо скомпилированного кода Java

Внутри файла application.properties

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

Здесь идет код Java, вы можете написать условие if или switch, чтобы узнать, имеет ли URL-адрес конечной точки в файле .properties значение @PathVariable (содержит {}) или @RequestParam (yourURL? Key = value) и т. Д., А затем вызвать метод соответствующим образом. Таким образом, его динамика и не нужно менять код в будущем один стоп магазин ...

Я пытаюсь дать больше идеи, чем фактический код здесь ... попробуйте написать универсальный метод для @RequestParam, @PathVariable и т. Д. ... и затем вызывать соответственно при необходимости

  @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

В Spring Web 4.3.6 я тоже вижу

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

Это означает, что вам не нужно создавать уродливую карту

Так что если у вас есть этот URL

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

Вы можете сделать

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

или

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

Если вы передадите непараметризованные параметры для RestTemplate, у вас будет один показатель для каждого отдельного URL, который вы передаете, с учетом параметров. Вы хотели бы использовать параметризованные URL:

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

вместо того

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

Второй случай - это то, что вы получаете, используя класс UriComponentsBuilder.

Одним из способов реализации первого поведения является следующее:

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

Если ваш URL http://localhost:8080/context path?msisdn={msisdn}&email={email}

затем

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

работает по методу замены resttemplate, как описано вами

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.