9

我似乎找不到明确提到这一点,但如果您使用 java.net.URI,您似乎无法发送转义的加号(“%2b”)作为查询 arg 值,因为查询 arg被逃脱。

// bad: http://example.com/foo?a=%252b
new URI("http", null, "example.com", 80, "/foo", "a=%2b", null);

尝试了一个实际的“+”字符,但它按原样发送,因此服务器会将其解释为空格。

// bad: http://example.com/foo?a=+
new URI("http", null, "example.com", 80, "/foo", "a=+", null);

所以我猜你只需要自己对查询 arg 键和值进行百分比编码,并使用不会转义的单参数 URI 构造函数?也许让 URI 转义“路径”,因为规则很棘手(例如,“+”字符在路径中表示加号,而不是空格):

// good: http://example.com/foo?a=%2b
new URI(new URI("http", null, "example.com", 80, "/foo", null, null).toASCIIString() + "?a=%2b");

此外,文档声称您可以创建这样的 URI,它将与源 URI 相同:

URI u = ...;
URI identical = new URI(u.getScheme(),
        u.getUserInfo(),
        u.getPath(), u.getQuery(),
        u.getFragment());

但当它包含 %2b 时,情况并非如此

URI u = new URI("http://example.com:80/foo?a=%2b");
URI identical = ...; // not identical! http://example.com:80/foo?a=+

令人沮丧,我想这就是为什么每个人都使用 apache commons 或 spring 类来代替?

PS:http ://docs.oracle.com/javase/6/docs/api/java/net/URI.html引用了“以下身份也持有”部分中不存在的 URI 构造函数。它需要删除“权限”参数。

4

3 回答 3

3

我遇到了同样的麻烦。经过一番研究,我认为如果您需要加号的 %2b 编码,最好不要使用 URI 类。我正在使用这样的代码:

URI uri = new URI(...);
String asciiUrl = uri.toASCIIString();
String plusReplaced = asciiUrl.replace("+", "%2b");
于 2013-04-29T17:11:50.513 回答
2

我使用 Spring 5 遇到了这个问题RestTemplate,它在下面使用了 apache HttpClient。无论我做什么,我都无法发送%2B,这对于 Couchbase 的非标准(呃……)REST API 来说是个问题。

此 API 期望文档 ID 中的空格在+URL 上被转义,并且文字加号为%2B.

不幸的是,ProtocolExec在 HttpClient 请求执行链上重写所有 URI 的方式使得无法%2B在 URI 的路径上发送文字。

  • 文字++.
  • %2B也被发送为+
  • %252B%2B%转义符一起)按字面意思发送 %252B

我的解决方案是编写一个自定义的 HttpClientBuilder 并MainExecProtocolExec. 它看起来像这样:

/*
 * This is a hack to circumvent Apache's HttpClient otherwise inevitable URI rewriting. This rewriting made it
 * impossible to send a literal %2B in a query path as it includes a forceful reencoding that doesn't reencode the
 * '+' character. This is necessary because Couchbase decided that they were too good for following standards and
 * decided to use + as meaning " " (whitespace) in URI paths instead of the typical %20.
 *
 * As an ugly solution we wrote an HttpClientBuilder that injects a ClientExecChain that will rewrite the full path
 * turning + to spaces. Maybe future versions will make this easier to accomplish.
 */
public class CustomHttpClientBuilder extends HttpClientBuilder {

    @Override
    protected ClientExecChain decorateMainExec(final ClientExecChain requestExecutor) {
        return (
                HttpRoute route,
                HttpRequestWrapper request,
                HttpClientContext clientContext,
                HttpExecutionAware execAware) -> {
            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(request.getURI());
            uriComponentsBuilder.replacePath(request.getURI().getRawPath().replace("+", "%2B"));
            request.setURI(uriComponentsBuilder.encode().build(true).toUri());

            return requestExecutor.execute(route, request, clientContext, execAware);
        };
    }
}

这使用 Spring 的UriComponentsBuilder,但您可以将其替换为任何其他 URI 构建类,只要您最终得到格式正确的java.net.URI.

此外,这对路径值也是如此,但对于查询参数也是如此。你只需要替换那些而不是路径。

我真的希望没有人再为此失去一周的时间。

于 2019-05-24T11:34:06.293 回答
-2

我会使用UriBuilder来处理所有需要编码的字符。

于 2013-04-29T17:16:33.397 回答