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つのパラメーターを受け入れます。
-
data –翻訳される文字列
-
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を見つけることができます。