【转载】Tomcat如何解析URL的请求参数(追踪HttpServletRequest对于请求参数的解析过程)

来源:互联网 发布:seo和网络推广的区别 编辑:程序博客网 时间:2024/06/02 03:43

想起了前两个月同事问我:我发出的请求里如果有”a=f&a=g”,那么在Servlet里获取到的a的值是一个字符串”f,g”,这是怎么回事儿?
当时我就猜测是SpringMVC做的处理,然后启动了一个测试工程,并进行了Debug追踪,最终查询到了Tomcat的源码里,发现居然是它做的解析。
现在想写个博客记录一下,却发现自己已经找不到当时追踪的痕迹了。。。于是查找了一下资料,再次发现原来有两套方案。
参考资料:
tomcat中解析url中的参数或者post中的请求内容
解决URL参数中的%问题

方案一:

Servlet API 2.3之前,Tomcat使用avax.servlet.http.HttpUtils对参数进行解析。代码如下:

/***本方法使用Hashtable<String,String[]>数据结构来存储请求参数的键值,对于同名键值对,采用值数组来存储,即一个key对应一个value数组。Hashtable相对于HashMap具有线程安全的优势。**/public static Hashtable<String,String[]> parseQueryString(String s) {        String valArray[] = null;        if (s == null) {            throw new IllegalArgumentException();        }        Hashtable<String,String[]> ht = new Hashtable<String,String[]>();        StringBuilder sb = new StringBuilder();        StringTokenizer st = new StringTokenizer(s, "&");        while (st.hasMoreTokens()) {            String pair = st.nextToken();            int pos = pair.indexOf('=');            if (pos == -1) {                // XXX                // should give more detail about the illegal argument                throw new IllegalArgumentException();            }            String key = parseName(pair.substring(0, pos), sb);            String val = parseName(pair.substring(pos+1, pair.length()), sb);            if (ht.containsKey(key)) {                String oldVals[] = ht.get(key);                valArray = new String[oldVals.length + 1];                for (int i = 0; i < oldVals.length; i++)                     valArray[i] = oldVals[i];                valArray[oldVals.length] = val;            } else {                valArray = new String[1];                valArray[0] = val;            }            ht.put(key, valArray);        }        return ht;    }

方案二:

将解析方法放在org.apache.catalina.connector.Request里(Tomcat的实现类),下面我们来分析一下Tomcat对请求参数的解析过程:

设立一个标识位:    /**     * Request parameters parsed flag.     */    protected boolean parametersParsed = false;

其标识了当前请求Request的参数是否已经解析了,如果已经解析过了,则可以使用Request的真实对象 “org.apache.coyote.Request”的Parameters属性,否则 需要解析参数并初始化Parameters属性。
如下:

    @Override    public String getParameter(String name) {        if (!parametersParsed) {            parseParameters();        }        return coyoteRequest.getParameters().getParameter(name);    }

第一次解析请求参数,并初始化Parameters属性:

/*** Parse request parameters.*/protected void parseParameters() {    parametersParsed = true;    //拿到真实对象的Parameters属性,然后初始化之    Parameters parameters = coyoteRequest.getParameters();    boolean success = false;    try {            //省略前置操作代码            byte[] formData = null;            if (len < CACHED_POST_LEN) {                if (postData == null) {                    postData = new byte[CACHED_POST_LEN];                }                formData = postData;            } else {                formData = new byte[len];            }            //拿到formData以后 调用Parameters的方法进行参数解析            parameters.processParameters(formData, 0, len);        } else if ("chunked".equalsIgnoreCase(            coyoteRequest.getHeader("transfer-encoding"))) {            byte[] formData = null;            try {                formData = readChunkedPostBody();            }             if (formData != null) {                parameters.processParameters(formData, 0, formData.length);            }        }        //省略异常处理代码        success = true;    }}

Parameters类的processParameters方法:

    public void processParameters( byte bytes[], int start, int len ) {        processParameters(bytes, start, len, getCharset(encoding));    }    private void processParameters(byte bytes[], int start, int len, Charset charset) {    //省略前置操作代码,主要是将字节转换为字符串,并分割&、=等字符    try {        addParameter(name, value);    } catch (IllegalStateException ise) {        //省略异常处理代码                }    /**    *Parameters类使用LinkedHashMap<String,ArrayList<String>>()来存储键值对    */    public void addParameter( String key, String value )throws IllegalStateException {        if( key==null ) {            return;        }        parameterCount ++;        if (limit > -1 && parameterCount > limit) {            // Processing this parameter will push us over the limit. ISE is            // what Request.parseParts() uses for requests that are too big            parseFailed = true;            throw new IllegalStateException(sm.getString(                    "parameters.maxCountFail", Integer.valueOf(limit)));        }        ArrayList<String> values = paramHashValues.get(key);        if (values == null) {            values = new ArrayList<String>(1);            paramHashValues.put(key, values);        }        values.add(value);    }

至此,请求的参数就已经被解析并存储在Request的Parameters对象中去了,以后再次调用Request的getPathParameter(String name)或者getParameterValues(String name)等方法时就会直接从Parameters类中获取了。

本文转载于http://blog.csdn.net/panyongcsd/article/details/54948160

阅读全文
0 0
原创粉丝点击