0

显然,在从 Spring Boot 1 迁移到 Spring Boot 2(Spring 5)的过程中,RestTemplates 的 URL 参数的编码行为发生了变化。在传递的其余模板上获取通用查询参数似乎异常困难,以便正确转义具有特殊含义的字符,例如“+”。似乎,由于“+”是一个有效字符,它不会被转义,即使它的含义被改变(见这里)。这似乎很奇怪,违反直觉,并且违反了所有其他平台上的所有其他约定。更重要的是,我不知道如何轻松绕过它。如果我先对字符串进行编码,它会被双重编码,因为“%”会被重新编码。无论如何,这似乎应该是框架所做的非常简单的事情,但我没有弄清楚。

这是我在 Spring Boot 1 中工作的代码:

  String url = "https://base/url/here";
  UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
  for (Map.Entry<String, String> entry : query.entrySet()) {
    builder.queryParam(entry.getKey(), entry.getValue());
  }
  HttpEntity<TheResponse> resp = myRestTemplate.exchange(builder.toUriString(), ...);

但是,现在它不会对“+”字符进行编码,因此另一端将其解释为空格。在 Java Spring Boot 2 中构建此 URL 的正确方法是什么?

注意 - 我也试过这个,但它实际上对所有内容进行了双重编码:

try {
  for (Map.Entry<String, String> entry : query.entrySet()) {
    builder.queryParam(entry.getKey(), URLEncoder.encode(entry.getValue(),"UTF-8" ));
  }
} catch(Exception e) {
  System.out.println("Encoding error");
}

在第一个中,如果我输入“q”=>“abc+1@efx.com”,那么,恰好在 URL 中,我得到“abc+1@efx.com”(即,根本没有编码) . 但是,在第二个中,如果我输入“abc+1@efx.com”,那么我会得到“abc%252B1%2540efx.com”,它是双编码的。

可以手写一个编码方法,但这似乎 (a) 有点矫枉过正,并且 (b) 自己编码是安全问题和奇怪的错误往往会蔓延的地方。但对我来说,你不能只添加似乎很疯狂Spring Boot 2 中的一个查询参数。这似乎是一项基本任务。我错过了什么?

4

1 回答 1

0

找到了我认为是一个不错的解决方案。事实证明,问题的很大一部分实际上是“交换”函数,它接受一个字符串作为 URL,但由于我无法理解的原因重新编码了该 URL。但是,可以改为向交换函数发送 java.net.URI。在这种情况下,它不会尝试插入任何内容,因为它已经是一个 URI。然后我使用 java.net.URLEncoder.encode() 对片段进行编码。我仍然不知道为什么这在 Spring 中不是标准的,但这应该可以。

    private String mapToQueryString(Map<String, String> query) {
        List<String> entries = new LinkedList<String>();
        for (Map.Entry<String, String> entry : query.entrySet()) {
            try {
                entries.add(URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8"));
            } catch(Exception e) {
                log.error("Unable to encode string for URL: " + entry.getKey() + " / " + entry.getValue(), e);
            }
        }
        return String.join("&", entries);
    }

    /* Later in the code */
    String endpoint = "https://baseurl.example.com/blah";
    String finalUrl = query.isEmpty() ? endpoint : endpoint + "?" + mapToQueryString(query);
    URI uri;
    try {
        uri = new URI(finalUrl);
    } catch(URISyntaxException e) {
        log.error("Bad URL // " + finalUrl, e);
            return null;
        }
    }
    /* ... */
    HttpEntity<TheResponse> resp = myRestTemplate.exchange(uri, ...)

于 2021-05-04T16:07:12.953 回答