【问题标题】:Spring RestTemplate GET with parametersSpring RestTemplate GET 带参数
【发布时间】: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 进行交换时,它工作得很好。

有人有什么想法吗?

【问题讨论】:

    标签: java spring rest


    【解决方案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 交换方法

                      【讨论】:

                        猜你喜欢
                        • 2021-08-12
                        • 1970-01-01
                        • 2016-11-09
                        • 1970-01-01
                        • 2017-09-14
                        • 2019-10-25
                        • 2014-09-22
                        • 1970-01-01
                        相关资源
                        最近更新 更多