關於Java WEB項目中中文亂碼的解決方法

關於Java WEB項目中中文亂碼的解決方法


例子

如下表單:

<form action="${pageContext.request.contextPath}/UserServlet" method="post">
    姓名:<input type="text" name="name">
    <input type="submit" value="提交">
</form>    

在這個表單中如果‘姓名’爲中文,提交至web後端進行獲取,如:

name = request.getParameter("name");

此時獲取到的name的值一定是亂碼,此時就要進行中文亂碼處理了。

處理方案可根據請求的類型分爲兩類:

POST請求

request.setCharacterEncoding("UTF-8");//在接收請求參數之前,將request的緩衝區字符集設置爲UTF-8
request.getParameter("name");//此時再獲取表單中的中文數據就不會亂碼了

GET請求

String param = reques.getParameter("name");//首先獲取表單數據
String name = new String(param.getBytes("ISO-8859-1"),"UTF-8");//將獲取到的表單數據以ISO-8859-1字符集轉成字節數組,再按照UTF-8字符集編碼成新的字符串,即可解決中文亂碼

重點:
表單數據之所以亂碼,是因爲字符集不匹配,導致的,在Post請求時,我們在獲取請求參數之前,將request緩衝區的字符集設置成UTF-8,即可在表單數據發送過來時,將表單數據按照UTF-8的編碼放入緩衝區,在取出來時,自然就已經不會亂碼了。在get請求時較爲繁瑣。需要首先將表單數據以ISO-8859-1字符集轉成字節數組,因爲request緩衝區的默認字符集是ISO-8859-1,所以以該字符集來將亂碼數據轉換成字節數組,不會丟失字節,從而保證了表單數據的完整性,進而以UTF-8字符集構造成新的字符串,將中文讀出。
但是如果需要將中文數據在頁面展示,還需要:

    response.setContentType("text/html;charset=UTF-8");//向瀏覽器指明要以UTF-8字符集解析返回的數據

以上是針對單個網頁的中文亂碼解決方法

在一個web項目中,中文亂碼的解決方案

我認爲最佳解決方案就是使用過濾器,攔截所有包含有中文的請求數據,轉碼後再傳遞給請求的目標地址。

依舊以上述表單數據的中文亂碼爲例:
Filter代碼如下:

public class GenericEncodingFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // 轉型爲與協議相關對象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 對request包裝增強
        HttpServletRequest myrequest = new MyRequest(httpServletRequest);
        chain.doFilter(myrequest, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

}

在上述代碼中,我們思考到,之所以在獲取表單數據時,拿到的中文都是亂碼,是因爲request對象本身並不具備對中亂碼的解決能力,因此,我們想到以裝飾者模式對request進行增強。(當然也可以使用動態代理來解決)
關於裝飾者模式的注意點:

  1. 增強的類要和被增強的類實現同一個接口
  2. 增強的類需要傳入一個被增強類的引用

以下是增強HttpServletRequest對象的實現:

// 自定義request對象
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;

    private boolean hasEncode;

    public MyRequest(HttpServletRequest request) {//此處傳入了request的引用
        super(request);// super必須寫
        this.request = request;
    }

    // 對需要增強方法 進行覆蓋
    @Override
    public Map getParameterMap() {
        // 先獲得請求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post請求
            try {
                // 處理post亂碼
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get請求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 確保get手動編碼邏輯只運行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 處理get亂碼
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }

        return super.getParameterMap();
    }

    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回參數的第一個值
    }

    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }

}

在上述方法中,對request進行了增強,根據請求方法的不同,對request所攜帶的表單數據進行中文亂碼的處理。然後將增強後的request向後傳遞:

// 對request包裝增強
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);//將包裝後的myrequest對象作爲request對象向後傳遞

在web.xml中做如下配置

<filter>
    <display-name>GenericEncodingFilter</display-name>
    <filter-name>GenericEncodingFilter</filter-name>
    <filter-class>com.wfm.web.filter.GenericEncodingFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>GenericEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

上述配置中<url-pattern></url-pattern>,決定了該編碼過濾器對哪些請求進行過濾,/*意思是指,對訪問該web項目的所有請求都進行過濾。

如何對所有的servlet進行請求的過濾?

以下是一個servlet的配置:

<servlet>
    <description></description>
    <display-name>FormServlet</display-name>
    <servlet-name>FormServlet</servlet-name>
    <servlet-class>com.wfm.web.servlet.FormServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>FormServlet</servlet-name>
    <url-pattern>/FormServlet</url-pattern>
  </servlet-mapping>

在上述配置中,請求通過訪問/FormServlet路徑觸發FormServlet,因此,我們可以攔截所有對該路徑的請求。但是如果我們要攔截的是指定的幾個Servlet怎麼辦?此時,想到了如下方法:

<servlet>
    <description></description>
    <display-name>FormServlet</display-name>
    <servlet-name>FormServlet</servlet-name>
    <servlet-class>com.wfm.web.servlet.FormServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>FormServlet</servlet-name>
    <url-pattern>/servlet/FormServlet</url-pattern>
</servlet-mapping>

我們可以在FormServlet的請求路徑中加上一個虛擬路徑,並將所有待攔截的Servlet的路徑都添加/servlet即可構造出一個請求的目錄,然後將Filter的<url-pattern>/servlet/*</url-pattern>,這樣就實現了對特定幾個servlet的請求過濾。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章