Spring RestTemplate GET 带参数

Posted

技术标签:

【中文标题】Spring RestTemplate GET 带参数【英文标题】:Spring RestTemplate GET with parameters 【发布时间】:2012-01-07 23:50:04 【问题描述】:

我必须进行包含自定义标头和查询参数的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 进行交换时,它工作得很好。

有人有什么想法吗?

【问题讨论】:

【参考方案1】:

要轻松操作 URLs/path/params/等,您可以使用 Spring 的 UriComponentsBuilder 类创建一个带有参数占位符的 URL 模板,然后在 RestOperations.exchange(...) 调用中为这些参数提供值。它比手动连接字符串更干净,它会为您处理 URL 编码:

HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> entity = new HttpEntity<>(headers);

String urlTemplate = UriComponentsBuilder.fromHttpUrl(url)
        .queryParam("msisdn", "msisdn")
        .queryParam("email", "email")
        .queryParam("clientVersion", "clientVersion")
        .queryParam("clientType", "clientType")
        .queryParam("issuerName", "issuerName")
        .queryParam("applicationName", "applicationName")
        .encode()
        .toUriString();

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

HttpEntity<String> response = restOperations.exchange(
        urlTemplate,
        HttpMethod.GET,
        entity,
        String.class,
        params
);

【讨论】:

很棒的提示。为简单起见,将exchange 更改为getForEntity: restTemplate.getForEntity(builder.build().encode().toUri(), String.class); @FernandoM.Pinheiro:您说得对,但如果您希望响应中包含泛型类型,那么您需要使用exchange 并提供ParameterizedTypeReference。该示例可以进一步简化,将builder.build().encode().toUri() 替换为builder.toUriString() 有一个获取URI的快捷方式:调用builder.toUriString() 小心!如果你这样做,你会得到双重编码。 builder.queryParam("value", "some&value") 将在 builder.toUriString(), "some%26value" 期间进行编码,然后在 'exchange()', "some%2526value" 中再次编码。最好传入 uriVariables。 正如@steve 上面所说,如果您按照此答案进行操作,您可能会获得双重编码值,包括您的参数值中有空格。如果您使用.build().toUriString() 而不是简单的.toUriString(),则可以解决此问题。这会跳过调用.encode(),这可以解决问题。见docs.spring.io/spring-framework/docs/current/javadoc-api/org/…【参考方案2】:

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。

【讨论】:

谢谢 - 非常简单的解决方案 并且在创建 RestTemplate 实例时,您可以通过指定 DefaultUriTemplateHandler(Spring 5 之前)或 DefaultUriBuilderFactory(Spring 5+)来指定如何扩展这些查询参数值。当您希望编码其他字符(例如 !、(、) 等时,这很有用。 我的 URL 有 10 多个参数,有什么方法可以通过对象/地图实现相同的效果,而不是列出每个变量?我也不能使用UriComponentsBuilder,因为它会导致它为Micrometer的每个请求生成不同的指标 @Doug — RestTemplate 具有指定值的位置数组 (Object... uriVariables) 或命名值的映射 (Map&lt;String, ?&gt; uriVariables) 的并行方法。听起来地图版本就是您想要的:restTemplate.exchange(url, HttpMethod.GET, httpEntity, clazz, urlVariablesMap) 没错!你可以通过调用来检查 URL 结果:restTemplate.getUriTemplateHandler().expand(“/some/some/other”, some, other);见 org.springframework.web.util.UriTemplateHandler【参考方案3】:

至少从 Spring 3 开始,而不是使用 UriComponentsBuilder 来构建 URL(这有点冗长),RestTemplate 中的 许多 方法接受参数路径中的占位符(不是只是exchange)。

来自文档:

许多RestTemplate 方法接受URI 模板和URI 模板变量,可以是String vararg,也可以是 Map&lt;String,String&gt;.

例如使用String 可变参数:

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

或者Map&lt;String, String&gt;:

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 模板”,您可以看到可以使用占位符的方法。

【讨论】:

【参考方案4】:

好的,所以我是个白痴,我将查询参数与 url 参数混淆了。我有点希望有一种更好的方法来填充我的查询参数,而不是一个丑陋的串联字符串,但我们已经有了。这只是使用正确参数构建 URL 的一种情况。如果您将它作为字符串传递,Spring 也会为您处理编码。

【讨论】:

它对你有用吗?我遵循使用 UriComponentsBuilder 的相同方法,但是在目标 URL 处,当我执行 request.getAttribute() 时,我得到 null。 我真的不明白为什么这个答案有绿色勾号。 因为他是OP 那么您的解决方案是什么?谢谢!【参考方案5】:
    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:使用 UriComponents(URI 变量和请求参数)构建动态 URI

【讨论】:

【参考方案6】:

我正在尝试类似的事情,the RoboSpice example helped me work it out:

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

【讨论】:

【参考方案7】:

将哈希映射转换为查询参数字符串:

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

【讨论】:

【参考方案8】:

在 Spring Web 4.3.6 中我也看到了

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

这意味着您不必创建丑陋的地图

所以如果你有这个网址

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

你可以这样做

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

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

【讨论】:

【参考方案9】:

我采取不同的方法,你可能同意或不同意,但我想从 .properties 文件而不是编译的 Java 代码进行控制

在 application.properties 文件中

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

Java 代码在这里,您可以编写 if 或 switch 条件来确定 .properties 文件中的端点 URL 是否具有 @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(); 

【讨论】:

【参考方案10】:

如果您为 RestTemplate 传递非参数化参数,则考虑到参数,您将为每个传递的单个不同 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);

【讨论】:

【参考方案11】:
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);
    

【讨论】:

【参考方案12】:

我正在提供带有路径参数示例的 RestTemplate GET 方法的代码 sn-p

public ResponseEntity<String> getName(int id) 
    final String url = "http://localhost:8080/springrestexample/employee/name?id=id";
    Map<String, String> params = new HashMap<String, String>();
    params.put("id", id);
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity request = new HttpEntity(headers);
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, String.class, params);
    return response;

【讨论】:

【参考方案13】:

如果你的网址是http://localhost:8080/context path?msisdn=msisdn&amp;email=email

然后

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

适用于您描述的 resttemplate 交换方法

【讨论】:

以上是关于Spring RestTemplate GET 带参数的主要内容,如果未能解决你的问题,请参考以下文章

Spring之RestTemplate常用API实践

Spring之RestTemplate常用API实践

springboot中使用restTemplate发送带参数和请求头的post,get请求

Spring RestTemplate用法 Post Get Cookie

spring restTemplate 用法

如何使用 spring 记录 RestTemplate 请求和响应?