使用Java,我想剥离片段标识符并对一组不同的URI进行一些简单的规范化(例如,小写方案,主机)。输入和输出URI在一般的HTTP意义上应该是等效的。
通常,这应该是直截了当的。但是,对于http://blah.org/A_%28Secret%29.xml#blah
这样的URI,其中%(Secret)
编码,java.util.URI
的行为会让生活变得困难。
规范化方法应返回http://blah.org/A_%28Secret%29.xml
,因为URI http://blah.org/A_%28Secret%29.xml
和http://blah.org/A_(Secret).xml
在解释中不等同[§2.2; RFC3968]
所以我们有以下两种规范化方法:
URI u = new URI("http://blah.org/A_%28Secret%29.xml#blah");
System.out.println(u);
// prints "http://blah.org/A_%28Secret%29.xml#blah"
String path1 = u.getPath(); //gives "A_(Secret).xml"
String path2 = u.getRawPath(); //gives "A_%28Secret%29.xml"
//NORMALISE METHOD 1
URI norm1 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(),
u.getHost().toLowerCase(), u.getPort(), path1,
u.getQuery(), null);
System.out.println(norm1);
// prints "http://blah.org/A_(Secret).xml"
//NORMALISE METHOD 2
URI norm2 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(),
u.getHost().toLowerCase(), u.getPort(), path2,
u.getQuery(), null);
System.out.println(norm2);
// prints "http://blah.org/A_%2528Secret%2529.xml"
如我们所见,在没有片段标识符的情况下解析和重建URI。
但是,对于方法1,u.getPath()
返回一个未编码的URI,它会更改最终的URI。
对于方法2,u.getRawPath()
返回原始路径,但是当传递给URI
构造函数时,Java决定添加双重编码。
这感觉像是一个中国手指陷阱。
所以有两个主要问题:
java.util.URI
感觉需要使用编码? (我宁愿不必实现java.util.URI
的解析/连接方法,这些方法非常重要。)
编辑:以下是URI
javadoc的更多信息。
单参数构造函数要求引用其参数中的任何非法字符,保留任何转义的八位字节和其他存在的字符。
多参数构造函数引用它们出现的组件所需的非法字符。 这些构造函数始终引用百分号('%')。保留任何其他字符。
getRawUserInfo, getRawPath ,getRawQuery,getRawFragment,getRawAuthority和getRawSchemeSpecificPart方法以原始格式返回其相应组件的值,而不解释任何转义的八位字节 。这些方法返回的字符串可能包含转义的八位字节和其他字符,并且不包含任何非法字符。
getUserInfo, getPath ,getQuery,getFragment,getAuthority和getSchemeSpecificPart方法解码其相应组件中的任何转义八位字节。这些方法返回的字符串可能包含其他字符和非法字符,并且不包含任何转义的八位字节。
toString方法返回一个带有所有必要引号但可能包含其他字符的URI字符串。
toASCIIString方法返回一个完全引用并编码的URI字符串,该字符串不包含任何其他字符。
所以我不能使用多参数构造函数而不使URI
类在内部搞乱URL编码。 PAH!
答案 0 :(得分:10)
因为{1.4}引入了java.net.URI
(2002年出版),它基于RFC2396,它将'('和')'视为不需要转义的字符即使它被转义,语义也不会改变,而且它甚至说除非有必要,否则不应该逃避它(§2.3,RFC2396)。
但RFC3986(2005年出版)改变了这一点,我想JDK的开发人员决定不改变java.net.URI
的行为以兼容现有代码。
通过随机谷歌搜索,我发现Jena IRI看起来不错。
public class IRITest {
public static void main(String[] args) {
IRIFactory factory = IRIFactory.uriImplementation();
IRI iri = factory.construct("http://blah.org/A_%28Secret%29.xml#blah");
ArrayList<String> a = new ArrayList<String>();
a.add(iri.getScheme());
a.add(iri.getRawUserinfo());
a.add(iri.getRawHost());
a.add(iri.getRawPath());
a.add(iri.getRawQuery());
a.add(iri.getRawFragment());
IRI iri2 = factory.construct("http://blah.org/A_(Secret).xml#blah");
ArrayList<String> b = new ArrayList<String>();
b.add(iri2.getScheme());
b.add(iri2.getRawUserinfo());
b.add(iri2.getRawHost());
b.add(iri2.getRawPath());
b.add(iri2.getRawQuery());
b.add(iri2.getRawFragment());
System.out.println(a);
//[http, null, blah.org, /A_%28Secret%29.xml, null, blah]
System.out.println(b);
//[http, null, blah.org, /A_(Secret).xml, null, blah]
}
}
答案 1 :(得分:4)
请注意[§2.2; RFC3968]
URI生成应用程序应对数据八位字节进行百分比编码 除非这些字符对应于保留集中的字符 URI方案特别允许表示其中的数据 零件。如果在URI组件中找到保留字符 对于该角色没有分隔角色,那么它必须是 解释为表示与其对应的数据八位字节 字符在US-ASCII中的编码。
因此,只要方案是http或https,编码就是正确的行为。
尝试使用toASCIIString方法而不是toString
来打印URI。 E.g:
System.put.println(norm1.toASCIIString());