表單提交一般有兩種方式,一種是GET方式,一種時POST方式,兩種方式這裏就不詳細解釋了;而後表單參數的傳遞,也有兩種方式,一種是直接把參數加在URL上,以key=value的方式傳遞,一種是在表單內部添加帶name屬性的標籤,例如input,select標籤等。那麼它們組合在一塊兒,就有4種方式:java
|
URL傳參web |
表單標籤傳參ajax |
混合傳參spring |
GET服務器 |
Aapp |
Bide |
Cpost |
POSTthis |
D編碼 |
E |
F |
先說一下使用中會出現的問題。A、C方式中,URL上的參數會被表單的參數沖掉,因此A、C方式不要使用。
在說說這幾種方式的特色,在GET方式中,表單中全部的參數實際上都是被追加到URL上的(這也是get方式的url傳參,url參數被沖掉的緣由),表單最後提交給服務器的就是一個url(url長度通常限制爲255字符)。這種方式產生的亂碼最難纏。
在POST方式中,若是參數位於表單中(等同於ajax提交數據時的data內容),參數是以非url形式提交的,因此這種一般不會出現亂碼,並且也容易解決。若是參數位於url中,那參數的傳遞方式和get方式是同樣的,這時產生亂碼的緣由和get方式是同樣的。
如今咱們把問題抽象出來了,參數傳遞有兩種,一種是經過url傳參,一種是經過data傳參。
亂碼之因此亂碼,是由於編碼和解碼的格式不一致。
說說咱們一般解決亂碼的方法。一般有兩類解決辦法,一類是對參數進行編碼,而後後臺進行解碼,這種方式對於以上幾種傳參都適用,可是由於前臺要編碼,後臺須要解碼,因此增長了代碼複雜性。另外一種方式就是弄個filter(spring自帶一個,就是這貨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,這個類是「主角」,完成對參數的篩選和轉碼:
- /*
- * Copyright (c) 2014, ShiXiaoyong. All rights reserved.
- */
- package com.common.filter;
-
- import java.io.UnsupportedEncodingException;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.Map;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletRequestWrapper;
-
- /**
- * 描述:GetHttpServletRequestWrapper
- *
- * <pre>
- * HISTORY
- * ****************************************************************
- * ID DATE PERSON REASON
- * 1 2015-3-6 Shixy Create
- * 2 2015-3-23 Shixy 增長對post混合傳參方式的支持
- * ****************************************************************
- * </pre>
- *
- * @author Shixy
- * @since 1.0
- */
- public class GetHttpServletRequestWrapper extends HttpServletRequestWrapper {
-
- private String charset = "UTF-8";
-
- private static final String ENCODED = "__encoded";
-
- private Map<String, String> urlParamNames = null;
-
- /**
- * @param request
- */
- public GetHttpServletRequestWrapper(HttpServletRequest request) {
- super(request);
- initUrlParameterNames();
- }
-
- /**
- * 得到被裝飾對象的引用和採用的字符編碼
- *
- * @param request
- * @param charset
- */
- public GetHttpServletRequestWrapper(HttpServletRequest request, String charset) {
- super(request);
- this.charset = charset;
- initUrlParameterNames();
- }
-
- @Override
- public Enumeration<String> getParameterNames() {
- return super.getParameterNames();
- }
-
- /**
- * 實際上就是調用被包裝的請求對象的getParameter方法得到參數,而後再進行編碼轉換
- */
- @Override
- public String getParameter(String name) {
- String value = super.getParameter(name);
- // 根據urlParamNames是否包含此值來判斷是否須要對其進行get方式轉碼
- if (!urlParamNames.containsKey(name)) {
- return value;
- }
- if (null != value) {
- value = convert(value);
- }
- return value;
- }
-
- @Override
- public String[] getParameterValues(String name) {
- // values也是傳值
- String[] values = super.getParameterValues(name);
- if ((!urlParamNames.containsKey(name))) {
- return values;
- }
- for (int i = 0; i < values.length; i++) {
- values[i] = convert(values[i]);
- }
-
- return values;
- }
-
- @Override
- public Map<String, String[]> getParameterMap() {
- Map<String, String[]> map = super.getParameterMap();
- // 是否已經轉碼的標識位
- // 由於map是傳引用的,所以屢次調用時,原值會被轉碼轉碼在轉碼,所以要設置此標誌位,防止屢次轉碼
- if ("1".equals(this.getAttribute(ENCODED))) {
- return map;
- }
-
- // 對map中全部的url傳參進行編碼
- // 遍歷map中的參數,轉換器編碼
- for (String key : urlParamNames.keySet()) {
- String[] value = map.get(key);
- if (value != null) {
- for (int i = 0; i < value.length; i++) {
- value[i] = convert(value[i]);
- }
- }
- }
-
- this.setAttribute(ENCODED, "1");
- return map;
- }
-
- /**
- * 將字符串轉碼
- * ISO-8859-1爲國際通用url編碼
- * @param target
- * @return
- */
- private String convert(String target) {
- try {
- return new String(target.trim().getBytes("ISO-8859-1"), charset);
- } catch (UnsupportedEncodingException e) {
- return target;
- }
- }
-
- /**
- * 初始化設置url傳值的參數名
- */
- private void initUrlParameterNames() {
- if (null != urlParamNames) {
- return;
- }
- // 獲取全部的url傳參的參數名
- urlParamNames = new HashMap<String, String>();
- String st = this.getQueryString();
- if (null == st || 0 == st.length()) {
- return;
- }
- String[] params = this.getQueryString().split("&");
- for (String p : params) {
- if (!p.contains("=")) {
- continue;
- }
- urlParamNames.put(p.substring(0, p.indexOf("=")), null);
- }
- }
-
- }
第二個就是一個簡單的filter,用於使用上面的RquestWrapper轉碼咱們的參數:
- /*
- * Copyright (c) 2014, ShiXiaoyong. All rights reserved.
- */
- package com.common.filter;
-
- import java.io.IOException;
-
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
-
- /**
- * 描述:GetMethodEncodingFilter
- * 針對GET方式提交的表單,進行編碼轉換
- * <pre>
- * HISTORY
- * ****************************************************************
- * ID DATE PERSON REASON
- * 1 2015-3-6 Shixy Create
- * ****************************************************************
- * </pre>
- *
- * @author Shixy
- * @since 1.0
- */
- public class GetMethodEncodingFilter implements Filter {
-
- private String charset = "utf-8";
-
- @Override
- public void destroy() {
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
-
- HttpServletRequest req = (HttpServletRequest)request;
-
- req = new GetHttpServletRequestWrapper(req,charset);
- filterChain.doFilter(req, response);
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
- }
最後把咱們的filter配置在web.xml裏便可,要注意順序,最佳位置是setCharatorEncoding那個filter後面。
- <!-- get method url encode -->
- <filter>
- <filter-name>getMethodEncodingFilter</filter-name>
- <filter-class>com.common.filter.GetMethodEncodingFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>encodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>