クエリ文字列パラメータのJava URLエンコーディング


710

私がURLを持っていると言います

http://example.com/query?q=

そして、私はユーザーによって入力されたクエリを持っています:

ランダムワード£500バンク$

結果を適切にエンコードされたURLにしたい:

http://example.com/query?q=random%20word%20%A3500%20bank%20%24

これを達成するための最良の方法は何ですか?URLEncoderURI / URLオブジェクトを作成してみましたが、どれも正しく機能しません。


25
「どれもうまく出てこない」とはどういう意味ですか?
Mark Elliot

2
URI.createを使用し、クエリ文字列でスペースを+に置き換えました。クライアントサイトでは、クエリ文字列を選択すると+からスペースに変換されました。それでうまくいきました。
ND27 2014年


なぜ$がパーセントでエンコードされていると思いますか?
jschnasse

回答:


1151

URLEncoder行く方法です。URL全体ではなく、個々のクエリ文字列パラメーターの名前または値、あるいはその両方のみをエンコードすることだけを念頭に置く必要があります。クエリ文字列パラメーターの区切り文字&やパラメーターの名前と値の区切り文字は含めないでください=

String q = "random word £500 bank $";
String url = "https://example.com?q=" + URLEncoder.encode(q, StandardCharsets.UTF_8);

クエリパラメータのスペース+%20、ではなくで表されることに注意してください。%20URI自体(URIクエリ文字列の区切り文字の前の部分にスペース表すために使用される通常?)、ないクエリ文字列(後の部分では?)。

また、3つのencode()方法があることに注意してください。1つCharsetは2番目の引数なしで、もう1つは2番目の引数Stringとしてあり、チェックされた例外をスローします。Charset引数のないものは非推奨です。絶対に使用せず、常にCharset引数を指定してください。Javadocはさえ明確で義務付けとして、UTF-8エンコーディングを使用することをお勧めしますRFC3986およびW3C

他のすべての文字は安全ではなく、いくつかのエンコードスキームを使用して最初に1バイト以上に変換されます。次に、各バイトは3文字の文字列「%xy」で表されます。ここで、xyはバイトの2桁の16進数表現です。使用が推奨されるコード化スキームはUTF-8です。ただし、互換性の理由から、エンコーディングが指定されていない場合は、プラットフォームのデフォルトのエンコーディングが使用されます。

以下も参照してください。


URLには2種類のパラメータを含めることができます。クエリ文字列(その後に?)とパスパラメータ(通常はURL自体の一部)。では、パスパラメータについてはどうでしょう。URLEncoderは、パスパラメーターの場合でもスペース用の+を生成します。実際には、クエリ文字列以外は何も処理しません。また、この動作はノードのjsサーバーと同期していません。したがって、私にとってこのクラスは無駄であり、非常に特定の/特別なシナリオ以外には使用できません。
sharadendu sinha 2017

2
@sharadendusinha:文書化および回答されているとおりURLEncoder、URLエンコードされたクエリパラメータはapplication/x-www-form-urlencodedルールに準拠しています。パスパラメータはこのカテゴリに適合しません。代わりにURIエンコーダーが必要です。
BalusC 2017

私が予測するように起こるでしょう...明らかに問題は人々が単なるパラメータ値以上のものをエンコードする必要があることなのでユーザーは混乱します。パラメータ値をエンコードするだけでよいという非常にまれなケースです。@sharadendusinhaのような人々を助けるために、私が「混乱した」wiki回答を提供したのはそのためです。
アダム・ゲント

1
@WijaySharma:URL固有の文字もエンコードされるため。URL全体を別のURLのクエリパラメータとして渡す場合にのみ、これを行う必要があります。
BalusC 2018年

1
「%20ではなく+」が私が聞く必要があったものです。どうもありがとうございます。
wetjosh

173

私は使用しませんURLEncoder。誤って名前が付けられる(URLEncoderURLとは関係ありません)以外にも、非効率的です(StringBufferビルダーの代わりに使用し、他にいくつかの遅い処理を行います)。

代わりにURIBuilderSpring org.springframework.web.util.UriUtils.encodeQueryまたはCommons Apacheを使用しHttpClientます。その理由qは、パラメーター値とは異なる方法でクエリパラメーター名(つまりBalusCの回答)をエスケープする必要があるためです。

上記の唯一の欠点(私が痛々しいことに気付いた)は、URLがURIの真のサブセットではないことです

サンプルコード:

import org.apache.http.client.utils.URIBuilder;

URIBuilder ub = new URIBuilder("http://example.com/query");
ub.addParameter("q", "random word £500 bank \$");
String url = ub.toString();

// Result: http://example.com/query?q=random+word+%C2%A3500+bank+%24

他の回答にリンクしているだけなので、これをコミュニティウィキとしてマークしました。自由に編集してください。


2
なぜURLとは関係がないのですか?
Luis Sep

15
@Luis:URLEncoderそのjavadocが言うapplication/x-www-form-urlencodedように、HTML仕様で記述されているようにクエリ文字列パラメーターをエンコードすることを意図して適合します:w3.org/TR/html4/interact/…。一部のユーザーは、現在の回答者が明らかにしたように、URI全体をエンコードするために実際に混乱/乱用しています。
BalusC、2015

8
短いURLEncoderの@LuisSepは、フォーム送信のエンコード用です。脱出用ではありません。これは、Webページに配置するURLを作成するために使用するのとまったく同じエスケープではありませんが、人々がそれを悪用するほど似ています。URLEncoderを使用する必要があるのは、HTTPクライアントを作成している場合のみです(それでも、エンコーディングにははるかに優れたオプションがあります)。
アダム・ゲント

1
@BalusC「一部のユーザーは、現在の回答者が明らかにしたように、URI全体をエンコードするために実際に混乱/乱用しています。」あなたは間違っていると思いました。私はそれを台無しにしたとは決して言いませんでした。私はそれを行った他の人たちを見たことがあります。私がめちゃくちゃにした部分は、Java URLクラスがエスケープされていない角括弧を受け入れますが、URIクラスは受け入れないということです。URLの作成を失敗させる方法はたくさんありますが、誰もがあなたのように素晴らしいわけではありません。URLEncodingのSOを探しているほとんどのユーザーは、おそらく「ユーザーは確かに混乱/乱用」URIエスケープです。
アダム・ゲント

1
質問はまだそれについてではありませんでしたあなたの答えはそれを意味します。
BalusC、2015

99

まず、次のようなURIを作成する必要があります。

String urlStr = "http://www.example.com/CEREC® Materials & Accessories/IPS Empress® CAD.pdf"
URL url= new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());

次に、そのUriをASCII文字列に変換します。

urlStr=uri.toASCIIString();

これで、URL文字列が完全にエンコードされました。最初に単純なURLエンコードを行い、次にそれをASCII文字列に変換して、US-ASCII以外の文字が文字列に残っていないことを確認します。これはまさにブラウザが行う方法です。


7
ありがとう!ソリューションが機能するのは愚かですが、組み込みは機能しURL.toURI()ません。
user11153 2015年

2
残念ながら、これは「file:///」では機能しないようです(例:「file:/// some / directory / a space.htmlを含むファイル」)。「新しいURL()」でMalformedURLExceptionを使用します。これを修正する方法はありますか?
ZioByte

次のようなことをする必要があります:String urlStr = " some / directory / a含有スペース.html"; URL url = new URL(urlStr); URI uri = new URI(url.getProtocol()、url.getUserInfo()、url.getHost()、url.getPort()、url.getPath()、url.getQuery()、url.getRef()); urlStr = uri.toASCIIString(); urlStr.replace( "http://"、 "file:///"); 私はそれをテストしていませんが、それはうまくいくと思います。...:)
M Abdul Sami

1
@tibi単純にuri.toString()メソッドを使用して、Ascii文字列ではなく文字列に変換できます。
Mアブドゥルサミ

1
私が使用していたAPI +はスペースの置換を受け入れませんでしたが、%20を受け入れたので、このソリューションはBalusCよりもうまく機能しました、ありがとう!
Julian

35

1
これらはと同じ間抜けな脱出ルールに悩まされていURLEncoderます。
2rs2ts 2014

3
彼らが問題を抱えているかどうかはわかりません。たとえば、「」または「%20」を区別して、エスケープしない「」(フォームパラメータまたはパスパラメータ)をエスケープURLEncoderします。
Emmanuel Touzery 2015

1
これは私にとってはうまくいきましたが、URLEncoder()の呼び出しをUrlEscapers.urlFragmentEscaper()の呼び出しに置き換えるだけでうまくいきました。代わりにUrlEscapers.urlPathSegmentEscaper()を使用する必要があるかどうかはわかりません。
Paul Taylor

2
私のために実際にそれdidntの仕事のURLEncoderそれdoesntのエンコード「+」とは違って、それだけでは、それを残しているので、私はのURLEncoderを使用する場合は「+」sは+に%2Bと正しく復号バックに変換されているのに対し、サーバーはスペースとして「+」デコード
ポール・テイラー

2
リンクの更新:UrlEscapers
mgaert

6

Apache Http Componentsライブラリは、クエリパラメータを構築およびエンコードするための優れたオプションを提供します-

- HttpComponents 4.xの使用により URLEncodedUtils

HttpClientを3.xの使用のために- EncodingUtil


6

以下は、URL文字列とパラメーターのマップをクエリパラメーターを含む有効なエンコードされたURL文字列に変換するためにコードで使用できるメソッドです。

String addQueryStringToUrlString(String url, final Map<Object, Object> parameters) throws UnsupportedEncodingException {
    if (parameters == null) {
        return url;
    }

    for (Map.Entry<Object, Object> parameter : parameters.entrySet()) {

        final String encodedKey = URLEncoder.encode(parameter.getKey().toString(), "UTF-8");
        final String encodedValue = URLEncoder.encode(parameter.getValue().toString(), "UTF-8");

        if (!url.contains("?")) {
            url += "?" + encodedKey + "=" + encodedValue;
        } else {
            url += "&" + encodedKey + "=" + encodedValue;
        }
    }

    return url;
}

6
URL url= new URL("http://example.com/query?q=random word £500 bank $");
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
String correctEncodedURL=uri.toASCIIString(); 
System.out.println(correctEncodedURL);

プリント

http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$

ここで何が起きてるの?

1. URLを構造部分に分割します。それに使用java.net.URL します。

2. 各構造パーツを適切にエンコードします!

3.ホスト名IDN.toASCII(putDomainNameHere)Punycodeエンコードするために使用します!

4.java.net.URI.toASCIIString()パーセントエンコード、NFCエンコードユニコードに使用します(NFKCの方が良いでしょう)。詳細については、このURLを適切にエンコードする方法を参照してください。

場合によっては、URLがすでにエンコードされているかどうか確認することをお勧めします。また、「+」でエンコードされたスペースを「%20」でエンコードされたスペースに置き換えます。

これも適切に機能するいくつかの例です

{
      "in" : "http://نامه‌ای.com/",
     "out" : "http://xn--mgba3gch31f.com/"
},{
     "in" : "http://www.example.com/‥/foo",
     "out" : "http://www.example.com/%E2%80%A5/foo"
},{
     "in" : "http://search.barnesandnoble.com/booksearch/first book.pdf", 
     "out" : "http://search.barnesandnoble.com/booksearch/first%20book.pdf"
}, {
     "in" : "http://example.com/query?q=random word £500 bank $", 
     "out" : "http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$"
}

このソリューションは、Webプラットフォームテストによって提供される約100のテストケースに合格します。


1

Androidでは、このコードを使用します:

Uri myUI = Uri.parse ("http://example.com/query").buildUpon().appendQueryParameter("q","random word A3500 bank 24").build();

どこUriandroid.net.Uri


10
これは標準のJava APIを使用していません。使用するライブラリを指定してください。
rmuller 2016

1

私の場合、URL全体を渡して、各パラメータの値のみをエンコードする必要がありました。私はそれを行うための一般的なコードを見つけられませんでした(!!)だから、私は仕事をするためにこの小さなメソッドを作成しました:

public static String encodeUrl(String url) throws Exception {
    if (url == null || !url.contains("?")) {
        return url;
    }

    List<String> list = new ArrayList<>();
    String rootUrl = url.split("\\?")[0] + "?";
    String paramsUrl = url.replace(rootUrl, "");
    List<String> paramsUrlList = Arrays.asList(paramsUrl.split("&"));
    for (String param : paramsUrlList) {
        if (param.contains("=")) {
            String key = param.split("=")[0];
            String value = param.replace(key + "=", "");
            list.add(key + "=" +  URLEncoder.encode(value, "UTF-8"));
        }
        else {
            list.add(param);
        }
    }

    return rootUrl + StringUtils.join(list, "&");
}

public static String decodeUrl(String url) throws Exception {
    return URLDecoder.decode(url, "UTF-8");
}

org.apache.commons.lang3.StringUtilsを使用します


-2
  1. これを使用してください:URLEncoder.encode(query、StandardCharsets.UTF_8.displayName()); またはthis:URLEncoder.encode(query、 "UTF-8");
  2. 次のコードを使用できます。

    String encodedUrl1 = UriUtils.encodeQuery(query, "UTF-8");//not change 
    String encodedUrl2 = URLEncoder.encode(query, "UTF-8");//changed
    String encodedUrl3 = URLEncoder.encode(query, StandardCharsets.UTF_8.displayName());//changed
    
    System.out.println("url1 " + encodedUrl1 + "\n" + "url2=" + encodedUrl2 + "\n" + "url3=" + encodedUrl3);

4
正しくありません。パラメータの名前と値を個別にエンコードする必要があります。クエリ文字列全体をエンコードする=と、および&区切り文字もエンコードされますが、これは正しくありません。
ローン侯爵
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.