在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的spec文档,包含有关getParameterValues()的以下信息。 它没有提到关于查询字符串的排序,所以我认为您看到的行为是实现细节,未定义为接口的一部分。

参数存储为一组名称 – 值对。 对于任何给定的参数名称,可以存在多个参数值。 ServletRequest接口的以下方法可用于访问参数:

  • 的getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

getParameterValues方法返回一个String对象数组,其中包含与参数名称关联的所有参数值。 getParameter方法返回的值必须是getParameterValues返回的String对象数组中的第一个值。 getParameterMap方法返回请求参数的java.util.Map,其中包含名称作为键,参数值包含映射值。

为了将来参考,Java Servlet规范可以从Sun下载,我的意思是Oracle的网站 。 您可以仔细检查您感兴趣的特定servlet版本。

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

来自查询字符串和post正文的数据被聚合到请求参数集中。 查询字符串数据在post正文数据之前呈现。 例如,如果使用a = hello的查询字符串和a = goodbye&a = world的post主体发出请求,则生成的参数集将被排序为a =(hello,goodbye,world)。

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

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

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

 
  • hello
  • world

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

2.控件名称/值按它们在文档中出现的顺序列出。

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

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

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


总结:参数收集在HashMap 。 由于HashMap的性质,保证名称不被排序。 由于String[]的性质,值(一个参数名称可以有多个值,例如foo=bar1&foo=bar2&foo=bar3 )依次排序,尽管这在Servlet API中没有明确指定。

为了安全起见,您希望使用不同的方法,例如

 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 + "]")); } 

我有一个问题是根据JSP上的元素按顺序从HttpServletRequest中提取param-value映射。 我写了一个OrderedRequestMap来解析一个application / x-www-form-urlencoded的POST请求体。

 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 { 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()); 

希望能帮助到你。

它取决于HttpServletRequest接口的实现。