<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    xylz,imxylz

    關注后端架構、中間件、分布式和并發編程

       :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

    早起的國內互聯網都使用GBK編碼,久之,所有項目都以GBK來編碼了。對于J2EE項目,為了減少編碼的干擾通常都是設置一個編碼的Filter,強制將Request/Response編碼改為GBK。例如一個Spring的常見配置如下:

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>GBK</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    

    毫無疑問,這在GBK編碼的頁面訪問、提交數據時是沒有亂碼問題的。但是遇到Ajax就不一樣了。Ajax強制將中文內容進行UTF-8編碼,這樣導致進入后端后使用GBK進行解碼時發生亂碼。網上的所謂的終極解決方案都是扯淡或者過于復雜,例如下面的文章:

    這樣的文章很多,顯然:

    • 使用VB進行UTF-8轉換成GBK提交是完全不可行(多瀏覽器、多平臺完全不可用)
    • 使用復雜的js函數進行一次、多次編碼,后端進行一次、多次解碼也是不靠譜的,成本太高,無法重復使用

    如果提交數據的時候能夠告訴后端傳輸的編碼信息是否就可以避免這種問題?比如Ajax請求告訴后端是UTF-8,其它請求告訴后端是GBK,這樣后端分別根據指定的編碼進行解碼是不是就解決問題了。

    有兩個問題:

    1. 如何通過Ajax告訴后端的編碼?Header過于復雜,Cookie成本太高,使用參數最方便。
    2. 后端何時進行解碼?每一個請求進行解碼,過于繁瑣;獲取參數時解碼,此時已經亂碼;在Filter里面動態設置編碼是最完善的方案。
    3. 如何從參數中獲取編碼?如果是POST的body顯然無法獲取,因此在獲取之前所有參數就已經按照某種編碼解碼過了,無法還原。所以通過URL傳遞編碼最有效。支持GET/POST,同時成本很低。

    解決了上述問題,來看具體實現方案。 列一段Java代碼:

    import java.io.IOException;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.util.ClassUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    /** 自定義編碼過濾器
     * @author imxylz (imxylz#gmail.com)
     * @sine 2011-6-9
     */
    public class MutilCharacterEncodingFilter extends OncePerRequestFilter {
    
        static final Pattern inputPattern = Pattern.compile(".*_input_encode=([\\w-]+).*");
    
        static final Pattern outputPattern = Pattern.compile(".*_output_encode=([\\w-]+).*");
    
        // Determine whether the Servlet 2.4 HttpServletResponse.setCharacterEncoding(String)
        // method is available, for use in the "doFilterInternal" implementation.
        private final static boolean responseSetCharacterEncodingAvailable = ClassUtils.hasMethod(HttpServletResponse.class,
              "setCharacterEncoding", new Class[] { String.class });
    
        private String encoding;
    
        private boolean forceEncoding = false;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
     throws ServletException, IOException {
            String url = request.getQueryString();
            Matcher m = null;
            if (url != null && (m = inputPattern.matcher(url)).matches()) {//輸入編碼
                String inputEncoding = m.group(1);
                request.setCharacterEncoding(inputEncoding);
                m = outputPattern.matcher(url);
                if (m.matches()) {//輸出編碼
                    response.setCharacterEncoding(m.group(1));
                } else {
                    if (this.forceEncoding && responseSetCharacterEncodingAvailable) {
                        response.setCharacterEncoding(this.encoding);
                    }
                }
            } else {
                if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
                    request.setCharacterEncoding(this.encoding);
                    if (this.forceEncoding && responseSetCharacterEncodingAvailable) {
                        response.setCharacterEncoding(this.encoding);
                    }
                }
            }
            filterChain.doFilter(request, response);
        }
    
        public void setEncoding(String encoding) {
            this.encoding = encoding;
        }
    
        public void setForceEncoding(boolean forceEncoding) {
            this.forceEncoding = forceEncoding;
        }
    }
    
    

    解釋下:

    • 如果URL的QueryString中包含_input_encode就使用此編碼進行設置Request編碼,以后參數按照此編碼進行解析,例如如果是Ajax就傳入UTF-8,如果是普通的GBK請求則無視此參數。
    • 如果無視此參數,則按照web.xml中配置的編碼規則進行反編碼,如果是GBK就按照GBK規則解析。
    • 對于輸出編碼同樣使用上述規則。需要輸出編碼依賴輸入編碼,也就是說如果有一個_output_encode的輸出編碼,則同時需要有一個_input_encode編碼來指定輸入編碼。當然你可以改造成不依賴輸入編碼。
    • 完全兼容Spring的org.springframework.web.filter.CharacterEncodingFilter編碼規則,只需要替換類即可。
    • 沒有繼承org.springframework.web.filter.CharacterEncodingFilter類的原因是,org.springframework.web.filter.CharacterEncodingFilter里面的encoding參數和forceEncoding參數是private,子類無法使用。在有_input_encode而無_output_encode的時候想依然保持Spring的Response解析規則的話無法做到,所以將里面的代碼拷貝過來使用。(為了展示方便,注釋都刪掉了)
    • 正則表達式可以改進成只需要匹配一次,從而可以提高一點點效率。
    • 所有后端請求將無視編碼的存在,前端Ajax的GET/POST請求也將無視編碼的存在,只是在URL地址中加上一個_input_encode=UTF-8的參數。僅此而已。
    • 如果想輸出的編碼也是UTF-8,比如手機端請求、跨站請求等,則需要URL地址參數_input_encode=UTF-8&_output_encode=UTF-8。
    • 對于POST請求,編碼參數不能寫在body里面,否則無法解析。
    • 顯然,這種終極解決方案,在任何編碼中都可以解決,GBK/UTF-8/ISO8859-1等編碼任何組合都可以實現。
    • 唯一局限性是,此解決方案限制在J2EE項目中,其它平臺不知是否有類似Filter這樣的組件能夠設置編碼的概念。


    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2011-06-10 23:46 imxylz 閱讀(8728) 評論(6)  編輯  收藏 所屬分類: J2EE

    評論

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2011-06-14 13:30 懶人網站助手
    謝謝分享,也可以看看http://www.lrtool.net,里面也有介紹  回復  更多評論
      

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2011-06-16 12:48 jacklondon chen
    統一用 post 就不會亂碼了。
    參照這里:JQuery UI autocomplete 中文亂碼的解決方法
    http://blog.csdn.net/jacklondon/archive/2010/10/14/5940643.aspx  回復  更多評論
      

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2011-06-16 13:51 xylz
    @jacklondon chen
    解決不了AJAX POST到GBK的問題  回復  更多評論
      

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2011-06-19 14:28 open-lib.com
    http://www.open-lib.com/Forum/Read_69_1.action

    這才是終極吧....  回復  更多評論
      

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2011-12-12 08:25 setin
    @xylz
    是的
    在tomcat容器中,AJax post提交,過濾器設置編碼無效,以容器編碼。
      回復  更多評論
      

    # re: 當Ajax遭遇GBK編碼 (完全解決方案) 2016-05-13 11:34
    前面說的都同意 就是解決方法說的 太抽象 沒看懂 給個具體的方法   回復  更多評論
      


    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 久久精品国产亚洲AV麻豆网站| 亚洲欧洲免费无码| 国产2021精品视频免费播放| 亚洲日韩AV一区二区三区四区| 免费jjzz在线播放国产 | 日韩精品无码免费一区二区三区| 亚洲人成7777影视在线观看| 亚洲国产精品日韩专区AV| 久久大香香蕉国产免费网站 | 久久成人免费大片| 亚洲精品蜜夜内射| 亚洲av色影在线| 日本免费v片一二三区| 你懂的免费在线观看网站| 亚洲成a∨人片在无码2023| 亚洲国产精品va在线播放| 最近最新中文字幕完整版免费高清 | 成人免费视频网址| a级精品九九九大片免费看| 最新亚洲卡一卡二卡三新区| 亚洲熟妇中文字幕五十中出| 午夜一区二区免费视频| 小草在线看片免费人成视久网| 噜噜噜亚洲色成人网站| 亚洲精品456在线播放| 国产亚洲精品精品国产亚洲综合 | 国产精品亚洲产品一区二区三区| 国产在线jyzzjyzz免费麻豆| 国产裸体美女永久免费无遮挡 | 国产亚洲精品成人久久网站| 亚洲制服丝袜在线播放| 亚洲精品高清国产一线久久| 国产无遮挡吃胸膜奶免费看视频 | 亚洲老熟女五十路老熟女bbw| 亚洲AV无码乱码国产麻豆 | 亚洲爆乳成av人在线视菜奈实| 久久精品国产精品亚洲蜜月| 亚洲日本一区二区一本一道| 日韩毛片无码永久免费看| 黄色永久免费网站| 男人进去女人爽免费视频国产|