3

我有以下 Uri:http://host/path?param1=1¶m2[]=2¶m3[1]=3

uri.getQueryParameterNames() 返回一个数组,其值为预期的 ["param1", "param2[]", "param3[1]"]。

但是当将此值传递给 getQueryParameter 我得到: uri.getQueryParameter("param1") == "1" uri.getQueryParameter("param2[]") == null uri.getQueryParameter("param3[1]") == null

我尝试在没有括号部分的情况下传递参数的名称,它也不起作用。也调用 getQueryParameters() 不起作用。

谢谢你的帮助!

4

2 回答 2

14

文档中所述,Uri构建和解析符合RFC 2396的 URI 引用。

如果您向下滚动到规范中的第 2.4.3 节,您会发现以下内容:

其他字符被排除在外,因为已知网关和其他传输
代理有时会修改这些字符,或者它们被
用作分隔符。

unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | ""`

对应于排除字符的数据必须转义才能在 URI 中正确表示。

从代码的角度来看,Uri有一个私有方法来确定一个字符是否被允许——如果不允许,它将被编码。理论上,这应该返回false括号和其他特殊字符。

/**
 * Returns true if the given character is allowed.
 *
 * @param c character to check
 * @param allow characters to allow
 * @return true if the character is allowed or false if it should be
 *  encoded
 */
private static boolean isAllowed(char c, String allow) {
    return (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z')
            || (c >= '0' && c <= '9')
            || "_-!.~'()*".indexOf(c) != NOT_FOUND
            || (allow != null && allow.indexOf(c) != NOT_FOUND);
}

话虽如此,现在我们进入了有趣的部分。它看起来Uri.parse()相当“懒惰”,因为它实际上并没有对你给它的 uri 进行编码。在您的场景中,您可能正在Uri通过调用构建:

Uri uri = Uri.parse("http://host/path?param1=1&param2[]=2&param3[1]=3");

然后,当您开始调用getQueryParameter()结果Uri时,会发生支持数据 (a String) 是“未编码”的情况,但您提供给这些方法的参数名称确实会被编码。例如:

uri.getQueryParameter("param2[]")

事实上是:

uri.getQueryParameter("param2%5B%5D")

带键的查询参数param2%5B%5D显然不存在于Uri.

解决方案是首先确保括号被转义,或者使用Uri.Builder. 例如:

Uri uri = Uri.parse("http://host/path").buildUpon()
    .appendQueryParameter("param1", "1")
    .appendQueryParameter("param2[]", "2")
    .appendQueryParameter("param3[1]", "3")
    .build();

这将导致以下底层 uri:

http://host/path?param1=1&param2%5B%5D=2&param3%5B1%5D=3

然后您可以uri.getQueryParameter("param3[1]")毫无问题地调用:它会返回3.


编辑:我收回我所说Uri.parse()的懒惰。本来就是这样的。正如Javadoc中明确提到的:

创建一个解析给定编码URI 字符串的 Uri。

换句话说:您为解析提供的字符串应该已经正确编码。

于 2013-08-16T22:09:19.653 回答
2

我面临着同样的问题。感谢 MH. 的回答,我找到了一个快速的解决方案。

代替使用Uri.getQueryParameter(),使用UrlQuerySanitizer.getParameterList() 此方法,对必要的参数进行编码并正确获取值。

这是一个简单的例子:

HashMap<String,String> queryMap = new HashMap<>();
UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(urlString);
List<UrlQuerySanitizer.ParameterValuePair> sanitizerParameterList = sanitizer.getParameterList();

for (UrlQuerySanitizer.ParameterValuePair valuePair : sanitizerParameterList) {
    queryMap.put(valuePair.mParameter, valuePair.mValue);
}
于 2016-07-14T20:30:32.850 回答