【发布时间】:2022-06-15 18:52:54
【问题描述】:
我刚刚在 java 中发现了一些与 URL 编码相关的错误,涉及 java.net.URLEncoder、java.net.URI 和 java.io.File,独立或一起取决于相同的底层事物。
问题在于 URL 编码中“+”的处理。许多人,包括 java.net.URLEncoder 的作者,认为在编码字符串中,+ 代表一个空格,相当于%20。以下是 java.net.URLEncoder 的规则
对字符串进行编码时,适用以下规则:
- 字母数字字符“a”到“z”、“A”到“Z”和“0”到“9”保持不变。
- 特殊字符“.”、“-”、“*”和“_”保持不变。
- 空格字符“”转换为加号“+”。
- 所有其他字符都是不安全的,首先转换为 [percent-encoding]
当我分别阅读关于 URL 和 URI 的 RFC 1738 和 RFC 3986 时,我发现使用“+”来编码空间实际上并没有被识别出来。这让我大吃一惊。
这是一个显示有问题的程序。但目前还不清楚谁有过错。
import java.net.*;
import java.io.*;
public class FileNameTest {
public static void main(String args[]) throws Exception {
System.out.println("1. " + URLEncoder.encode("name with+space"));
System.out.println("2. " + new URI("file:/tmp/"+URLEncoder.encode("name with+space")));
System.out.println("3. " + new File(new URI("file:/tmp/"+URLEncoder.encode("name with+space"))));
System.out.println("4. " + new URI("file:/tmp/"+URLEncoder.encode("name with+space")).getPath());
System.out.println("5. " + new File(new URI("file:/tmp/name%20with%2Bspace")));
System.out.println("6. " + new File(new URI("file:/tmp/name%20with%2Bspace")).toURI());
System.out.println("7. " + new URI("file", "", "/tmp/name with+space", "name with+space", null));
}
}
这个输出:
$ java -cp . FileNameTest
1. name+with%2Bspace
2. file:/tmp/name+with%2Bspace
3. \tmp\name+with+space
4. /tmp/name+with+space
5. \tmp\name with+space
6. file:/C:/tmp/name%20with+space
7. file:///tmp/name%20with+space?name%20with+space
解释一下,
- URL 编码的“name with+space”是“name+with%2Bspace”——这在 URLEncoder 自己的规范中是正确的,但是使用 + 来编码空格的权威性值得怀疑。
- 来自 URL 编码的“name with+space”的 URI 是“name+with%2Bspace”,URI 没有在 + 上阻塞,但它按原样接受它。
- 将最后一步的 URI 转换为文件时,我们会得到“name+with+space”,也就是说,第一个“+”本来应该对空格进行编码,现在是文字“+”
- 在获取 URI 的路径组件时,+ 不会被解释为转义空格
- 但当“+”变成百分比编码“$20”时,一切正常。
- 当文件随后转回 URI 时,文件和 URI 一起认为“+”不需要转义为“%2B”。
- 最后,由于 URI 承诺在使用多参数构造函数时负责编码,我想看看它是否正确编码了 URL 编码的查询参数,因为这些被认为是 URLEncoder 类的目的(形成 URL-编码),但同样,没有转义“+”表示不理解“+”是“%20”的缩写。
我的分析是 URLEncoder 是在一些非权威但非常习惯的规则下运行的,在这些规则下,“+”是空间编码,与“%20”同义,真正的“+”应该转义为“%2B” ”。也许有基于 URL 编码的表单编码,但实际上不是。
java.net.URI 根本不同意 URLEncoder。它接受“%2B”作为“+”但不生成它。并且它不将“+”理解为“%20”的简写。这似乎不是 URL 和 URI 之间的细微差别,但我看不到“+”被指定为编码空间。
这是一个坑坑洼洼的坑,Java 使用的内部规范相互冲突,它们不能一起工作。似乎也没有“正确的方法”来做到这一点,因为没有 URIEncoder 函数可以正常工作以与 URL 结合,或者有吗?
【问题讨论】:
-
来自
URLEncoderdocs:“这个类包含将String转换为application/x-www-form-urlencoded MIME格式的静态方法。”所以它的目的是不是对 URL 进行编码,而是对 URL 参数的名称/值进行编码,并且有将+替换为%2b并将` ` 编码为+的规则适用。