Java URLエンコーディング/デコーディングの手引き

Java URLエンコード/デコードのガイド

1. 前書き

簡単に言えば、URL encodingは、URLからの特殊文字を、仕様に準拠し、正しく理解および解釈できる表現に変換します。

この記事では、how to encode/decode the URL or form dataに焦点を当てて、仕様に準拠し、ネットワークを介して正しく送信できるようにします。

2. URLを分析します

基本的なURI構文は、次のように一般化できます。

scheme:[//[user:[email protected]]host[:port]][/]path[?query][#fragment]

URIをエンコードする最初のステップは、その部分を調べてから、関連する部分のみをエンコードすることです。

URIの例を見てみましょう。

String testUrl =
  "http://www.example.com?key1=value+1&key2=value%40%21%242&key3=value%253";

URIを分析する1つの方法は、文字列表現をjava.net.URIクラスにロードすることです。

@Test
public void givenURL_whenAnalyze_thenCorrect() throws Exception {
    URI uri = new URI(testUrl);

    assertThat(uri.getScheme(), is("http"));
    assertThat(uri.getHost(), is("www.example.com"));
    assertThat(uri.getRawQuery(),
      .is("key1=value+1&key2=value%40%21%242&key3=value%253"));
}

URIクラスは、文字列表現URLを解析し、単純なAPIを介してその部分を公開します(例:getXXX.

3. URLをエンコードする

URIをエンコードする場合、よくある落とし穴の1つは完全なURIのエンコードです。 通常、URIのクエリ部分のみをエンコードする必要があります。

URLEncoderクラスのencode(data, encodingScheme)メソッドを使用してデータをエンコードしましょう。

private String encodeValue(String value) {
    return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}

@Test
public void givenRequestParam_whenUTF8Scheme_thenEncode() throws Exception {
    Map requestParams = new HashMap<>();
    requestParams.put("key1", "value 1");
    requestParams.put("key2", "[email protected]!$2");
    requestParams.put("key3", "value%3");

    String encodedURL = requestParams.keySet().stream()
      .map(key -> key + "=" + encodeValue(requestParams.get(key)))
      .collect(joining("&", "http://www.example.com?", ""));

    assertThat(testUrl, is(encodedURL));

encodeメソッドは、次の2つのパラメーターを受け入れます。

  1. data –翻訳される文字列

  2. encodingScheme –文字エンコードの名前

このencodeメソッドは、文字列をapplication/x-www-form-urlencoded形式に変換します。

エンコード方式は、特殊文字を「%xy」の形式で表される8ビットの2桁の16進表現に変換します。 パスパラメータを処理する場合、または動的なパラメータを追加する場合は、データをエンコードしてサーバーに送信します。

Note:World Wide Web Consortium勧告では、UTF-8を使用する必要があると述べています。 そうしないと、非互換性が生じる可能性があります。 (参照:https://docs.oracle.com/javase/7/docs/api/java/net/URLEncoder.html

4. URLをデコードします

ここで、URLDecoderのdecodeメソッドを使用して前のURLをデコードしましょう。

private String decode(String value) {
    return URLDecoder.decode(value, StandardCharsets.UTF_8.toString());
}

@Test
public void givenRequestParam_whenUTF8Scheme_thenDecodeRequestParams() {
    URI uri = new URI(testUrl);

    String scheme = uri.getScheme();
    String host = uri.getHost();
    String query = uri.getRawQuery();

    String decodedQuery = Arrays.stream(query.split("&"))
      .map(param -> param.split("=")[0] + "=" + decode(param.split("=")[1]))
      .collect(Collectors.joining("&"));

    assertEquals(
      "http://www.example.com?key1=value 1&[email protected]!$2&key3=value%3",
      scheme + "://" + host + "?" + decodedQuery);
}

ここで重要な2つのビットは次のとおりです。

  • デコードする前にURLを分析します

  • エンコードとデコードに同じエンコード方式を使用する

解析するよりもデコードする場合、URL部分が正しく解析されない可能性があります。 別のエンコードスキームを使用してデータをデコードすると、ガベージデータになります。

5. パスセグメントをエンコードする

URLEncoderは、URLのパスセグメントのエンコードには使用できません。 パスコンポーネントは、ディレクトリパスを表す階層構造を参照するか、“/”で区切られたリソースを見つけるのに役立ちます。

パスセグメントの予約文字は、クエリパラメーター値とは異なります。 たとえば、「–」記号はパスセグメント内の有効な文字であるため、エンコードしないでください。

パスセグメントをエンコードするには、代わりにSpring FrameworkのUriUtilsクラスを使用します。 UriUtilsクラスは、パスとパスセグメントをそれぞれエンコードするためのencodePathメソッドとencodePathSegmentメソッドを提供します。

例を見てみましょう:

private String encodePath(String path) {
    try {
        path = UriUtils.encodePath(path, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        LOGGER.error("Error encoding parameter {}", e.getMessage(), e);
    }
    return path;
}
@Test
public void givenPathSegment_thenEncodeDecode()
  throws UnsupportedEncodingException {
    String pathSegment = "/Path 1/Path+2";
    String encodedPathSegment = encodePath(pathSegment);
    String decodedPathSegment = UriUtils.decode(encodedPathSegment, "UTF-8");

    assertEquals("/Path%201/Path+2", encodedPathSegment);
    assertEquals("/Path 1/Path+2", decodedPathSegment);
}

上記のコードスニペットでは、encodePathSegmentメソッドを使用すると、エンコードされた値が返され、+はパスコンポーネントの値文字であるためエンコードされていないことがわかります。

テストURLにパス変数を追加しましょう。

String testUrl
  = "/path+1?key1=value+1&key2=value%40%21%242&key3=value%253";

そして、適切にエンコードされたURLを組み立ててアサートするために、セクション2からテストを変更しましょう。

String path = "path+1";
String encodedURL = requestParams.keySet().stream()
  .map(k -> k + "=" + encodeValue(requestParams.get(k)))
  .collect(joining("&", "/" + encodePath(path) + "?", ""));
assertThat(testUrl, CoreMatchers.is(encodedURL));

6. 結論

このチュートリアルでは、データを正しく転送および解釈できるように、データをエンコードおよびデコードする方法を見てきました。 この記事では、URIクエリパラメーター値のエンコード/デコードに焦点を当てましたが、このアプローチはHTMLフォームパラメーターにも適用されます。

ソースコードover on GitHubを見つけることができます。