关于java:HttpServletRequest.getParameterValues()中的值排序

Ordering of values in HttpServletRequest.getParameterValues()

HttpServletRequest.getParameterValues()返回包含给定HTTP请求参数的所有值的String[]。有谁知道规范中是否保证此数组中值的顺序与请求中这些值的传递顺序相同?

例如,如果我有GET查询字符串x=1&x=2&x=3,当我调用getParameterValues()时是否可以保证收到String[] {"1","2","3"}吗?这似乎在实践中可行,但是我找不到任何可以说明必须是这种情况的东西,因此我不愿意依赖它。


用于ServletRequest的Javadoc(v2.5 javadoc)没有提及有关该方法的值顺序的任何内容。因此,我不会依赖保留的顺序。

更新:还检查了规格文档2.5,其中包含与getParameterValues()有关的以下信息。它没有提及有关查询字符串排序的任何内容,因此我认为您所看到的行为是实现细节,而不是定义为接口的一部分。

The parameters are stored as a set of
name-value pairs. Multiple parameter
values can exist for any given
parameter name. The following methods
of the ServletRequest interface are
available to access parameters:

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

The
getParameterValues method returns an
array of String objects containing all
the parameter values associated with a
parameter name. The value returned
from the getParameter method must be
the first value in the array of String
objects returned by
getParameterValues. The
getParameterMap method returns a
java.util.Map of the parameter of the
request, which contains names as keys
and parameter values as map values.

供将来参考,可以从Sun(我的意思是Oracle的网站)下载Java Servlet规范。您可以在此处再次检查您感兴趣的特定servlet版本。


是的,Servlet规范可以保证getParamterValues(String)返回的值和getParameterMap()中的条目的顺序。这是段落:

Data from the query string and the
post body are aggregated into the
request parameter set. Query string
data is presented before post body
data. For example, if a request is
made with a query string of a=hello
and a post body of a=goodbye&a=world,
the resulting parameter set would be
ordered a=(hello, goodbye, world).

(这来自Servlet规范的"请求"一章中的" HTTP协议参数"部分(2.4版中的SRV.4.1,2.5版中的SRV.3.1)。)

似乎没有一种干净的方法来按顺序获取名称(getParameterNames()不会按浏览器给出的顺序返回名称)。我想我可以解析getQueryString()中的原始GET字符串或解析getInputStream()中的原始POST字符串,但是我想我将添加另一个隐藏参数,然后使用getParameterValues(String)来获取其顺序。

如果您想知道为什么要按顺序排列参数,那是因为我拥有用户可以使用jQuery重新排列控件的控件,并且我想知道用户选择的顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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 src="jquery-ui.js">

  jQuery('#variables').sortable().disableSelection();


它确实没有在Servlet规范中明确定义,但是至少HTML表单规范在application / x-www-form-urlencoded部分中明确定义了它:

2.The control names/values are listed in the order they appear in the document.

所以,那部分是安全的。现在,从逻辑上讲,一个servlet容器,一个体面而高效的实现,将在输入HTTP输入流时立即对其进行处理,因此,将按照出现在请求URI(GET)或请求正文(POST)中的顺序处理参数。将它们收集在String[]中是最直接的选择,因为它在Servlet API中也按原样使用,因此我真的看不到有什么理由先将其收集在类似HashSet的结构中或进行Collections#shuffle()或其他内容,然后将其转换为String[]

我至少可以从经验中看出,Tomcat的做法是正确的,因此所有在Tomcat / Catalina之上构建的主要容器/应用服务器(IBM Websphere,JBoss AS,Sun Glassfish等)也将如此运行。我只没有接触过Weblogic的经验,但是如果它以不同的方式处理(阅读:效率较低),我会感到惊讶。

仅保证参数名称的顺序是不保证的,因为从逻辑上讲,它由HashMap支持。

总结:参数收集在HashMap<String, String[]>中。由于HashMap的性质,名称被隔离而不按顺序排列。这些值(一个参数名称可以具有多个值,例如foo=bar1&foo=bar2&foo=bar3)会根据String[]的性质进行排序,尽管这在Servlet API中未明确指定。

为了安全起见,您想使用其他方法,例如

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

with

1
2
3
4
int foos = Integer.valueOf(request.getParameter("foos"));
for (int i = 0; i < foos; i++) {
    int foo = Integer.valueOf(request.getParameter("foo[" + i +"]"));
}


我有一个问题,要按照JSP上的元素从HttpServletRequest中提取参数值映射。我写了一个OrderedRequestMap来解析一个application / x-www-form-urlencoded POST请求主体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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 ="";
}

这样称呼它

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

希望如此,对您有帮助。


这取决于HttpServletRequest接口的实现。