8

Javaを使用して、フラグメント識別子を取り除き、さまざまなURIセットの単純な正規化(小文字のスキーム、ホストなど)を実行したいと思います。入力URIと出力URIは、一般的なHTTPの意味で同等である必要があります。

通常、これは簡単なはずです。http://blah.org/A_%28Secret%29.xml#blahただし、パーセントでエンコードされるのようなURIの場合(Secret)、の動作java.util.URIは生活を困難にします。

正規化メソッドはhttp://blah.org/A_%28Secret%29.xmlURIから戻る必要があり、解釈において同等ではhttp://blah.org/A_%28Secret%29.xmlありhttp://blah.org/A_(Secret).xmlません[§2.2; RFC3968 ]

したがって、次の2つの正規化方法があります。

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はダブルエンコーディングを追加することを決定します。

これは、中国のフィンガートラップのように感じます。

したがって、2つの主な質問:

  • なぜjava.util.URIエンコーディングで遊ぶ必要性を感じるのですか?
  • 元のパーセントエンコーディングをいじることなく、この正規化メソッドをどのように実装できますか?

(私はむしろ、java.util.URI自明ではないの解析/連結メソッドを実装する必要はありません。)


編集:ここにURIjavadocからのいくつかのさらなる情報があります。

  • 単一引数のコンストラクターでは、引数に不正な文字を引用符で囲む必要があり、エスケープされたオクテットやその他の存在する文字を保持します。

  • 複数引数のコンストラクターは、それらが表示されるコンポーネントで必要とされるように、不正な文字を引用します。パーセント文字('%')は、これらのコンストラクターによって常に引用符で囲まれます。その他の文字はすべて保持されます。

  • getRawUserInfo、getRawPath、getRawQuery、getRawFragment、getRawAuthority、およびgetRawSchemeSpecificPartメソッドは、エスケープされたオクテットを解釈せずに、対応するコンポーネントの値を生の形式で返します。これらのメソッドによって返される文字列には、エスケープされたオクテットと他の文字の両方が含まれている可能性があり、不正な文字は含まれていません。

  • getUserInfo、getPath、getQuery、getFragment、getAuthority、およびgetSchemeSpecificPartメソッドは、対応するコンポーネント内のエスケープされたオクテットをデコードします。これらのメソッドによって返される文字列には、他の文字と不正な文字の両方が含まれている可能性があり、エスケープされたオクテットは含まれません。

  • toStringメソッドは、必要なすべての引用符を含むURI文字列を返しますが、他の文字が含まれている場合があります。

  • toASCIIStringメソッドは、他の文字を含まない、完全に引用符で囲まれ、エンコードされたURI文字列を返します。

URIそのため、クラスによって内部的にURLエンコードが混乱することなく、マルチ引数コンストラクターを使用することはできません。パー!

4

2 に答える 2

10

Java 1.4 (2002年に登場) で導入され、'(' と ')' をエスケープを必要としない文字として扱い、エスケープされても意味が変わらないjava.net.URIRFC2396 に基づいているため、さらに必要でない限り、それをエスケープすべきではないとさえ言っています(§2.3、RFC2396)。

java.net.URIしかし、RFC3986 (2005 年に公開) はこれを変更し、JDK の開発者は、既存のコードの互換性のためにの動作を変更しないことにしたと思います。

ランダムにグーグルで検索したところ、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]
}
}
于 2012-03-03T16:53:08.977 に答える
4

[§2.2; の最後にあるこの一節に注意してください。RFC3968]

URI 生成アプリケーションは、予約済みセット内の文字に対応するデータ オクテットをパーセント エンコードする必要があります。ただし、これらの文字が URI スキームによってそのコンポーネント内のデータを表すことが明確に許可されている場合を除きます。URI コンポーネントで予約文字が見つかり、その文字の区切りの役割が不明な場合は、その文字の US-ASCII でのエンコードに対応するデータ オクテットを表すものとして解釈する必要があります。

したがって、スキームが http または https である限り、エンコーディングは正しい動作です。

URI を表示する代わりに、 toASCIIStringメソッドを使用してみてくださいtoString。例えば:

System.put.println(norm1.toASCIIString());
于 2012-02-23T19:37:15.240 に答える