關於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進行增強。(當然也可以使用動態代理來解決)
關於裝飾者模式的注意點:
- 增強的類要和被增強的類實現同一個接口
- 增強的類需要傳入一個被增強類的引用
以下是增強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的請求過濾。