有没有办法用java.net.URI在查询arg中发送%2b(编码加号)?

时间:2013-01-25 16:42:18

标签: java

我似乎无法明确地提到这一点,但是如果你使用的是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 classes呢?

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

3 个答案:

答案 0 :(得分:3)

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

URI uri = new URI(...);
String asciiUrl = uri.toASCIIString();
String plusReplaced = asciiUrl.replace("+", "%2b");

答案 1 :(得分:0)

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

此API期望文档ID中的空格在URL上以+的形式转义,而文字加号为%2B

不幸的是,HttpClient请求执行链上的ProtocolExec重写了所有URI,使得无法在URI的路径上发送原义的%2B

  • 文字+作为+发送。
  • %2B也以+的形式发送
  • %252B%2B转义的%)按字面意义发送为 %252B

我的解决方案不仅是黑客,而且很难避免,它是编写自定义HttpClientBuilder并为MainExec插入装饰器,该装饰器将在ProtocolExec之后执行重写。看起来像这样:

/*
 * 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,但是只要最终得到格式正确的java.net.URI,您就可以将其替换为任何其他URI构建类。

此外,它对路径值执行此操作,但对于查询args则相同。您只需要替换那些路径即可。

我真的希望没有人再失去一个星期了。

答案 2 :(得分:-2)

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