• 解决表单提交参数乱码问题【终极版】不看后悔


    解决表单提交参数乱码问题【终极版】不看后悔

    提交表单乱码问题,一直是困然网站开发人员的“吐血”问题,这问题虽说不痛不痒,但是绝对“恶心人”。之前自己遇到这个问题是,一直是能绕过就绕过,懒得理它,直到今天我又遇到它,我知道,我必须得治治它了。

    表单提交通常有两种方式,一种是GET方式,一种时POST方式,两种方式这里就不详细解释了;然后表单参数的传递,也有两种方式,一种是直接把参数加在URL上,以key=value的方式传递,一种是在表单内部添加带name属性的标签,例如inputselect标签等。那么它们组合在一起,就有4种方式:

     

    URL传参

    表单标签传参

    混合传参

    GET

    A

    B

    C

    POST

    D

    E

    F

    先说一下使用中会出现的问题。AC方式中,URL上的参数会被表单的参数冲掉,所以AC方式不要使用。

    在说说这几种方式的特点,在GET方式中,表单中所有的参数实际上都是被追加到URL上的(这也是get方式的url传参,url参数被冲掉的原因),表单最后提交给服务器的就是一个urlurl长度一般限制为255字符)。这种方式产生的乱码最难缠。

    POST方式中,如果参数位于表单中(等同于ajax提交数据时的data内容),参数是以非url形式提交的,所以这种通常不会出现乱码,而且也容易解决。如果参数位于url中,那参数的传递方式和get方式是一样的,这时产生乱码的原因和get方式是一样的。

    现在我们把问题抽象出来了,参数传递有两种,一种是通过url传参,一种是通过data传参。

    乱码之所以乱码,是因为编码和解码的格式不一致。

    说说我们通常解决乱码的方法。通常有两类解决办法,一类是对参数进行编码,然后后台进行解码,这种方式对于以上几种传参都适用,但是因为前台要编码,后台需要解码,所以增加了代码复杂性。另一种方式就是弄个filterspring自带一个,就是这货org.springframework.web.filter.CharacterEncodingFilter,可以直接把它配在web.xml里面),对所有请求都setCharactorEncoding()UTF-8,这种方式通常都行。之所以说通常都行,是因为这种方式之对通过data方式传递的参数有效,对于通过url传递的参数无效,这也是为什么get提交方式产生乱码概率大的原因。

    但是我们怎么通过url传参时的乱码呢?也许有人会说,不用url传参不就可以了,但是在许多情况下,我不得不使用url传参,比如一个超链接。

     

    其实只要找到问题所在,解决方案也就好办了。开始时,我的办法是写一个filter,对于通过get方式提交的参数,把所有的参数都进行一下编码转换:ISO-8859-1 à UTF-8。这种方式我使用了很长时间,直到有一次,我不得不使用post方式的混合传参时,才发现url上的参数居然被认为是post方式传递的,当然也没有被我的filter拦截,当然也就乱码了。

     

    不过既然要死磕,就一定要把这问题解决。

    思路倒是很清晰,虽然是post方式提交的,但是我们只需要把其中url方式传参的参数进行转码即可(data传参只需要设置CharactorEncoding即可,如果转码那就转成乱码了),可是怎么知道哪些参数是url传递呢?

    HttpServletRequest对象有getQueryString()这个方法,这个方法能够获得url传递的参数的字符串,当然了,参数也就包含在其中。所以我们只要把其中的参数名分离出来即可,这些就是我们需要进行转码的,别的不需要解码。

     

    在然后呢,我们通过request获取参数时,一般会通过这么几个方法:getParameter()getPrarmeterMap()getParameterValues()这三个方法。所以我们只要在这三个方法上“做手脚”即可。另外,如果某些参数是按照传引用(相对于传值而言,了解c的人,对这个应该比较了解。另外虽然java本质上都是传值,但是如果对象不是基本类型时,就会有传引用的效果)传递的,我们还要设置一些标志位,防止多次转码。

     

    思路已经清楚了,下面直接贴本人的成型代码。

    第一个是GetHttpServletRequestWrapper,这个类是“主角”,完成对参数的筛选和转码:

    1. /* 
    2.  * Copyright (c) 2014, ShiXiaoyong. All rights reserved. 
    3.  */  
    4. package com.common.filter;  
    5.   
    6. import java.io.UnsupportedEncodingException;  
    7. import java.util.Enumeration;  
    8. import java.util.HashMap;  
    9. import java.util.Map;  
    10.   
    11. import javax.servlet.http.HttpServletRequest;  
    12. import javax.servlet.http.HttpServletRequestWrapper;  
    13.   
    14. /** 
    15.  * 描述:GetHttpServletRequestWrapper 
    16.  *  
    17.  * <pre> 
    18.  * HISTORY  
    19.  * ****************************************************************  
    20.  *  ID   DATE           PERSON          REASON  
    21.  * 1     2015-3-6       Shixy           Create  
    22.  * 2     2015-3-23      Shixy           增加对post混合传参方式的支持  
    23.  * **************************************************************** 
    24.  * </pre> 
    25.  *  
    26.  * @author Shixy 
    27.  * @since 1.0 
    28.  */  
    29. public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {  
    30.   
    31.     private String charset = "UTF-8";  
    32.   
    33.     private static final String ENCODED = "__encoded";  
    34.   
    35.     private Map<String, String> urlParamNames = null;  
    36.   
    37.     /** 
    38.      * @param request 
    39.      */  
    40.     public GetHttpServletRequestWrapper(HttpServletRequest request) {  
    41.         super(request);  
    42.         initUrlParameterNames();  
    43.     }  
    44.   
    45.     /** 
    46.      * 获得被装饰对象的引用和采用的字符编码 
    47.      *  
    48.      * @param request 
    49.      * @param charset 
    50.      */  
    51.     public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) {  
    52.         super(request);  
    53.         this.charset = charset;  
    54.         initUrlParameterNames();  
    55.     }  
    56.   
    57.     @Override  
    58.     public Enumeration<String> getParameterNames() {  
    59.         return super.getParameterNames();  
    60.     }  
    61.   
    62.     /** 
    63.      * 实际上就是调用被包装的请求对象的getParameter方法获得参数,然后再进行编码转换 
    64.      */  
    65.     @Override  
    66.     public String getParameter(String name) {  
    67.         String value = super.getParameter(name);  
    68.         // 根据urlParamNames是否包含此值来判断是否需要对其进行get方式转码  
    69.         if (!urlParamNames.containsKey(name)) {  
    70.             return value;  
    71.         }  
    72.         if (null != value) {  
    73.             value = convert(value);  
    74.         }  
    75.         return value;  
    76.     }  
    77.   
    78.     @Override  
    79.     public String[] getParameterValues(String name) {  
    80.         // values也是传值  
    81.         String[] values = super.getParameterValues(name);  
    82.         if ((!urlParamNames.containsKey(name))) {  
    83.             return values;  
    84.         }  
    85.         for (int i = 0; i < values.length; i++) {  
    86.             values[i] = convert(values[i]);  
    87.         }  
    88.   
    89.         return values;  
    90.     }  
    91.   
    92.     @Override  
    93.     public Map<String, String[]> getParameterMap() {  
    94.         Map<String, String[]> map = super.getParameterMap();  
    95.         // 是否已经转码的标识位  
    96.         // 因为map是传引用的,因此多次调用时,原值会被转码转码在转码,因此要设置此标志位,防止多次转码  
    97.         if ("1".equals(this.getAttribute(ENCODED))) {  
    98.             return map;  
    99.         }  
    100.   
    101.         // 对map中所有的url传参进行编码  
    102.         // 遍历map中的参数,转换器编码  
    103.         for (String key : urlParamNames.keySet()) {  
    104.             String[] value = map.get(key);  
    105.             if (value != null) {  
    106.                 for (int i = 0; i < value.length; i++) {  
    107.                     value[i] = convert(value[i]);  
    108.                 }  
    109.             }  
    110.         }  
    111.   
    112.         this.setAttribute(ENCODED, "1");  
    113.         return map;  
    114.     }  
    115.   
    116.     /** 
    117.      * 将字符串转码 
    118.      * ISO-8859-1为国际通用url编码 
    119.      * @param target 
    120.      * @return 
    121.      */  
    122.     private String convert(String target) {  
    123.         try {  
    124.             return new String(target.trim().getBytes("ISO-8859-1"), charset);  
    125.         } catch (UnsupportedEncodingException e) {  
    126.             return target;  
    127.         }  
    128.     }  
    129.       
    130.     /** 
    131.      * 初始化设置url传值的参数名 
    132.      */  
    133.     private void initUrlParameterNames() {  
    134.         if (null != urlParamNames) {  
    135.             return;  
    136.         }  
    137.         // 获取所有的url传参的参数名  
    138.         urlParamNames = new HashMap<String, String>();  
    139.         String st = this.getQueryString();  
    140.         if (null == st || 0 == st.length()) {  
    141.             return;  
    142.         }  
    143.         String[] params = this.getQueryString().split("&");  
    144.         for (String p : params) {  
    145.             if (!p.contains("=")) {  
    146.                 continue;  
    147.             }  
    148.             urlParamNames.put(p.substring(0, p.indexOf("=")), null);  
    149.         }  
    150.     }  
    151.   
    152. }  



    第二个就是一个简单的filter,用于使用上面的RquestWrapper转码我们的参数:

    1. /*   
    2.  * Copyright (c) 2014, ShiXiaoyong. All rights reserved.  
    3.  */  
    4. package com.common.filter;  
    5.   
    6. import java.io.IOException;  
    7.   
    8. import javax.servlet.Filter;  
    9. import javax.servlet.FilterChain;  
    10. import javax.servlet.FilterConfig;  
    11. import javax.servlet.ServletException;  
    12. import javax.servlet.ServletRequest;  
    13. import javax.servlet.ServletResponse;  
    14. import javax.servlet.http.HttpServletRequest;  
    15.   
    16. /**  
    17.  * 描述:GetMethodEncodingFilter  
    18.  *     针对GET方式提交的表单,进行编码转换 
    19.  * <pre>  
    20.  * HISTORY  
    21.  * ****************************************************************  
    22.  *  ID   DATE           PERSON          REASON  
    23.  *  1    2015-3-6       Shixy           Create  
    24.  * ****************************************************************  
    25.  * </pre>  
    26.  *   
    27.  * @author Shixy  
    28.  * @since 1.0  
    29.  */  
    30. public class GetMethodEncodingFilter implements Filter {  
    31.   
    32.     private String charset = "utf-8";  
    33.       
    34.     @Override  
    35.     public void destroy() {  
    36.     }  
    37.   
    38.     @Override  
    39.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {  
    40.           
    41.         HttpServletRequest req = (HttpServletRequest)request;    
    42.   
    43.         req = new GetHttpServletRequestWrapper(req,charset);    
    44.         filterChain.doFilter(req, response);    
    45.     }  
    46.   
    47.     @Override  
    48.     public void init(FilterConfig filterConfig) throws ServletException {  
    49.     }  
    50. }  



最后把我们的filter配置在web.xml里即可,要注意顺序,最佳位置是setCharatorEncoding那个filter后面。

  1. <!-- get method url encode -->  
  2. <filter>  
  3.     <filter-name>getMethodEncodingFilter</filter-name>  
  4.     <filter-class>com.common.filter.GetMethodEncodingFilter</filter-class>  
  5. </filter>  
  6. <filter-mapping>  
  7.     <filter-name>encodingFilter</filter-name>  
  8.     <url-pattern>/*</url-pattern>  
  9. </filter-mapping>  


  • 相关阅读:
    学习英文之社区,博客及源码 转载
    Windows 的 80 端口被 System 进程占用解决方案
    MySQL 报错:Translating SQLException with SQL state '42000', error code '1064', message
    程序员如何写工作日志?
    log4j 配置文件参数说明
    Java 后端彻底解决跨域问题(CORS)
    常用正则表达式语法
    使用 windows 批处理指令(BAT文件)进行压缩文件(zip)解压操作
    使用 windows 批处理指令(BAT文件)进行文件删除、复制操作
    idea 关于高亮显示与选中字符串相同的内容踩过的坑
  • 原文地址:https://www.cnblogs.com/jpfss/p/7851667.html
  • Copyright © 2020-2023  润新知