web-dev-qa-db-ja.com

HttpServletRequest.getParameterValues()での値の順序

HttpServletRequest.getParameterValues()は、指定されたHTTPリクエストパラメータのすべての値を含む_String[]_を返します。この配列の値の順序が、要求でそれらの値が渡された順序と同じように仕様によって保証されているかどうかを誰かが知っていますか?

たとえば、GETクエリ文字列_x=1&x=2&x=3_がある場合、getParameterValues()を呼び出したときに_String[] {"1", "2", "3"}_を受け取るのは保証ですか?実際にはうまくいくようですが、そうであるに違いないことを明記したものが見つからないので、頼りにしたくありません。

22
skaffman

ServletRequestのjavadoc( v2.5 javadoc )は、そのメソッドの値の順序については何も言及していません。そのため、順序が保持されていることに依存しません。


更新:2.5の仕様ドキュメントも確認しました。getParameterValues()に関連する次の情報が含まれています。クエリ文字列に関する順序については何も言及されていないため、表示されている動作は実装の詳細であり、インターフェイスの一部として定義されていないと思います。

パラメータは、名前と値のペアのセットとして保存されます。任意のパラメーター名に対して複数のパラメーター値が存在する可能性があります。パラメータにアクセスするには、ServletRequestインターフェイスの次のメソッドを使用できます。

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

GetParameterValuesメソッドは、パラメーター名に関連付けられたすべてのパラメーター値を含むStringオブジェクトの配列を返します。 getParameterメソッドから返される値は、getParameterValuesによって返されるStringオブジェクトの配列の最初の値である必要があります。 getParameterMapメソッドは、リクエストのパラメータのJava.util.Mapを返します。これには、キーとしての名前とマップ値としてのパラメータ値が含まれます。

将来の参考のために、Javaサーブレットの仕様は Sun、つまりOracleのWebサイト からダウンロードできます。興味のある特定のサーブレットのバージョンを再確認できます。

15
brabster

はい、getParamterValues(String)によって返される値の順序とgetParameterMap()isのエントリはサーブレット仕様によって保証されています。パッセージは次のとおりです。

クエリ文字列と投稿本文のデータは、リクエストパラメータセットに集約されます。クエリ文字列データは、投稿本文データの前に表示されます。たとえば、クエリ文字列a = helloと投稿本文a = goodbye&a = worldを使用してリクエストが行われた場合、結果のパラメータセットはa =(hello、goodbye、world)の順序になります。

(これは、サーブレット仕様(バージョン2.4のSRV.4.1、バージョン2.5のSRV.3.1)の「要求」の章にある「HTTPプロトコルパラメータ」セクションからのものです。)

そこにはありませんは名前を順番に取得するためのクリーンな方法のようです(getParameterNames()notは、ブラウザが指定した順序で名前を返します)。 getQueryString()から生のGET文字列を解析するかgetInputStream()から生のPOST stringを解析することができると思いますが、代わりに追加すると思います別の非表示パラメーターを選択し、getParameterValues(String)を使用してその順序を取得します。

パラメータを順番に並べたい理由がわからない場合は、ユーザーがjQueryを使用して再配置できるコントロールがあり、ユーザーが選択した順序を知りたいからです。

<form>
  <ul id=variables>
    <li>
      <input name=hello>hello
      <input type=hidden name=variableOrder value=hello>
    <li>
      <input name=world>world
      <input type=hidden name=variableOrder value=world>
  </ul>
</form>
<script src="jquery.js"></script>
<script src="jquery-ui.js"></script>
<script>
  jQuery('#variables').sortable().disableSelection();
</script>
12
yonran

実際、サーブレット仕様では明示的に定義されていませんが、少なくともHTMLフォーム仕様では、 application/x-www-form-urlencoded セクションで明示的に定義されています。

2.コントロールの名前/値は、ドキュメントに表示されている順序でリストされています。

したがって、その部分は安全です。これで、servletcontainerは、最も論理的に適切で効率的な実装で、HTTP入力ストリームが着信するとすぐに処理されるため、パラメーターは要求URI(GET)または要求本文(POST)に表示される順序で処理されます。それらを_String[]_に収集することは、サーブレットAPIでもそのまま使用されるため、最も簡単な選択です。したがって、最初にHashSetのような構造に収集する理由はまったくありません。 、またはCollections#shuffle()などを実行し、後で_String[]_に変換します。

少なくとも経験からわかるように、Tomcatはそれを正しい方法で実行するため、Tomcat/Catalina(IBM Websphere、JBoss AS、Sun Glassfishな​​ど)上に構築されたすべての主要なコンテナー/アプリサーバーもそのように動作します。私はWeblogicを実際に使用した経験がないだけですが、Weblogicの処理方法が異なると驚きます(読み取り:効率が低下します)。

パラメータnamesの順序のみが保証されていません。これは、論理的にはHashMapに支えられているためです。


要約:パラメーターは_HashMap<String, String[]>_に収集されます。 HashMapの性質上、名前は順番に並べられずに隔離されます。値(1つのパラメータ名は複数の値を持つことができます(例:_foo=bar1&foo=bar2&foo=bar3_))は、サーブレットAPIで明示的に指定されていませんが、_String[]_の性質上順番に並べられています。

安全を期すために、別のアプローチを使用したいと思います。

_foos=3&foo[0]=bar1&foo[1]=bar2&foo[2]=bar3
_

_int foos = Integer.valueOf(request.getParameter("foos"));
for (int i = 0; i < foos; i++) {
    int foo = Integer.valueOf(request.getParameter("foo[" + i + "]"));
}
_
8
BalusC

JSPの要素に従って、HttpServletRequestからparam-valueマップを順番に抽出する際に問題が発生しました。 application/x-www-form-urlencoded POSTリクエスト本文をパーサーするOrderedRequestMapを作成しました。

import Java.io.IOException;
import Java.io.InputStream;
import Java.io.InputStreamReader;
import Java.io.UnsupportedEncodingException;
import Java.net.URLDecoder;
import Java.util.Arrays;
import Java.util.LinkedHashMap;
import Java.util.Map;


public class OrderedRequestMap extends LinkedHashMap<String,String[]> {

private final String encoding;

public OrderedRequestMap(InputStream httpBody, String encoding) throws IOException {
    this.encoding = encoding;
    fill(httpBody);
}

private void fill(InputStream is) throws IOException {
    final InputStreamReader reader = new InputStreamReader(is, "ASCII");
    int c;
    StringBuilder sb = new StringBuilder(1000);
    while ((c = reader.read()) != -1) {
        char ch = (char)c;
        if (ch == '&') {
            put(sb.toString());
            sb = new StringBuilder(1000);
        } else {
            sb.append(ch);
        }
    }
    put(sb.toString());
}

private void put(String parameter) throws UnsupportedEncodingException {
    String[] pair = parameter.split("=");
    if (pair.length == 0 )
        return;
    String key = URLDecoder.decode(pair[0], encoding);
    String val = EMPTY_STR;
    if (pair.length > 1)
        val = URLDecoder.decode(pair[1], encoding);
    String[] values = get(key);
    if (values == null)
        values = new String[]{val};
    else {
        values = Arrays.copyOf(values, values.length+1);
        values[values.length - 1] = val;
    }
    put(key, values);
}


private static final String EMPTY_STR = "";
}

そしてそれをこのように呼ぶ

new OrderedRequestMap(request.getInputStream(), request.getCharacterEncoding());

それが役に立てば幸い。

2
Bragnikita

これは、HttpServletRequestインターフェースの実装に依存します。

0
giri