在Java中规范化可能编码的URI字符串

bad*_*oit 7 java uri normalization percent-encoding

使用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"
Run Code Online (Sandbox Code Playgroud)

如我们所见,在没有片段标识符的情况下解析和重建URI.

但是,对于方法1,u.getPath()返回未编码的URI,这会更改最终的URI.

对于方法2,u.getRawPath()返回原始路径,但是当传递给URI构造函数时,Java决定添加双重编码.

这感觉像是一个中国手指陷阱.

所以有两个主要问题:

  • 为什么java.util.URI觉得需要使用编码?
  • 如何在不摆弄原始百分比编码的情况下实现这种规范化方法?

(我宁愿不必实现parse/concatenate方法java.util.URI,这些方法非常重要.)


编辑:这是URIjavadoc的一些进一步信息.

  • 单参数构造函数要求引用其参数中的任何非法字符,并保留任何转义的八位字节和其他存在的字符.

  • 多参数构造所要求的在它们出现的部件引用非法字符.百分比字符('%')始终由这些构造函数引用.保留任何其他字符.

  • getRawUserInfo,getRawPath,getRawQuery,getRawFragment,getRawAuthority和getRawSchemeSpecificPart方法以原始形式返回其相应组件的值,而不解释任何转义的八位字节.这些方法返回的字符串可能包含转义的八位字节和其他字符,并且不包含任何非法字符.

  • getUserInfo,getPath,getQuery,getFragment,getAuthority和getSchemeSpecificPart方法解码其相应组件中的任何转义八位字节.这些方法返回的字符串可能包含其他字符和非法字符,并且不包含任何转义的八位字节.

  • toString方法返回一个带有所有必要引号但可能包含其他字符的URI字符串.

  • toASCIIString方法返回一个完全引用并编码的URI字符串,该字符串不包含任何其他字符.

所以我不能使用多参数构造函数,而不会让URI类内部的URL编码混乱.呸!

Chi*_*kei 10

因为java.net.URI是用Java 1.4(其中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]
}
}
Run Code Online (Sandbox Code Playgroud)