瀏覽器在向服務器發送請求時會對攜帶的請求參數進行編碼(UTF-8格式),服務器在接收到請求參數時會對其進行解碼,因爲瀏覽器與服務器編碼格式不一樣產生亂碼。不一樣服務器默認編碼格式不一樣,Tomcat默認ISO-8859-1。
html
get請求有三種解決方案。
java
a. 經過先編碼再解碼方式。服務器以不一樣編碼格式解碼後致使亂碼,此時能夠經過先以與服務器相同的編碼格式將字符串編碼成原始的字節流,再經過String類的構造方法解碼生成正確的字符串。代碼以下:web
String newJoo = new String(joo.getBytes("ISO-8859-1"), "UTF-8"); //joo爲字符串
b. 修改tomcat服務器配置文件,設置url編碼格式。tomcat安裝目錄下conf文件夾內server.xml配置文件,在<Connector connectionTimeout="20000" port="8090" protocol="HTTP/1.1" redirectPort="8443" />中添加 URIEncoding="UTF-8"屬性。該方法對post亂碼無做用,由於它只能設置url中攜帶的請求參數,而post請求參數存放在請求體中。瀏覽器
c. 在頁面中用js函數如:encoding(),進行編碼,後臺用java代碼解碼。這種方法麻煩,就很少說了。tomcat
三種方法各有各的缺陷,使用a方法須要在每一個servlet類中都添加那段代碼,形成代碼冗餘,可維護性差;使用b方法能夠達到一勞永逸的效果,可是若是服務器中有其餘項目須要用不一樣的url編碼格式怎麼辦?因此這樣不靈活,不友好,並且對post請求亂碼無效;c方法的缺陷同a方法。服務器
post請求亂碼解決方式很簡單,只須要一行代碼。request對象中有個設置編碼格式的方法:app
request.setCharacterEncoding("utf-8")
一樣的,這種方法也有缺陷。首先,它形成代碼冗餘,使可維護性變差,其次,它只對請求體中的參數有做用,對get請求方式亂碼無效。
ide
在Servlet中獲取請求參數有三種方法:getParameter(),getParameterValues(),getParameterMap(),getParameter()返回String類型,用於獲取單個請求參數,getParameterValues()返回String[]類型,也用於獲取單個請求參數,只不過這個請求參數包含多個值,如複選框,getParameterMap()返回值爲Map<String, String[]>,用於獲取全部請求參數。函數
ServletRequest接口中定義了這三個方法,tomcat實現這三個方法,但實現源碼並無作編碼相關的處理,若是咱們本身定義一個類實現ServletRequest接口而後重寫這三個方法,並在其中解決post和get亂碼不就既能夠一勞永逸解決問題,避免代碼冗餘,同時還保證了靈活性,確保不會影響同服務器中的其它項目。可是實現ServletRequest接口就必須實現其中的全部方法,工做量太大!post
sun公司早已考慮到這個問題,很體貼的設計了一個包裝類實現ServletRequest接口,咱們只須要定義一個類繼承HttpServletRequestWrapper類,並重寫三個方法就能達到目的。先來看看HttpServletRequestWrapper類的繼承關係:
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest 能夠看到它繼承了ServletRequestWrapper類,實現了HttpServletRequest接口,因此HttpServletRequestWrapper實現了ServletRequest接口的全部方法。
咱們的目的是要讓全部Servlet都使用重寫的方法,上面咱們解決了怎麼重寫三個獲取請求參數的方法,可是還有一個問題——如何才能讓全部Servlet都使用咱們重寫的方法,即在哪裏重寫這三個方法。解決這個問題首先要明白Servlet的生命週期,這裏簡單的介紹下,tomcat啓動時實例化Servlet對象並執行其中的init()方法進行初始化操做,當tomcat接收到請求時會建立HttpServletRequest和HttpServletResponse對象,而後執行Servlet對象中的Service()方法判斷請求方式並調用doGet()或者doPost()方法,這裏會將兩對象看成參數傳遞給doGet()和doPost()方法,當tomcat正常stop時執行Servlet類中的destory()方法進行銷燬操做。知道了Servlet的生命週期咱們知道,若是要讓全部Servlet類都使用重寫的方法咱們能夠修改tomcat的源代碼,使它在建立HttpServletRequest和HttpServletResponse對象的過程當中建立的是咱們自定義對象,但這樣工做量很大,並且還會影響其餘項目,不靈活也不友好。到這裏java web三大組件之一Filter就浮出水面了。
Filter——過濾器,能夠先於全部Servlet執行,咱們在自定義的Filter中定義個類繼承HttpServletRequestWrapper,重寫三個獲取請求參數方法,再將它傳遞給Servlet就能夠達到咱們的目的。
web.xml配置
<filter> <filter-name>encodingFilter</filter-name> <filter-class>ff.charset.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
自定義Filter類
public class EncodingFilter implements Filter{ private String encoding; // 編碼格式 @Override public void init(FilterConfig filterConfig) throws ServletException { // 從web.xml中獲取編碼格式 this.encoding = filterConfig.getInitParameter("encoding"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 向下轉型成HttpServletRequest HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // 設置數據響應編碼格式 response.setContentType("text/html;charset=" + encoding); // 建立裝飾類 MyHttpRequest myhr = new MyHttpRequest(req, encoding); // 放行 chain.doFilter(myhr, res); } @Override public void destroy() { // TODO Auto-generated method stub } }
自定義類繼承HttpServletRequestWrapper
上面提到了getParameterMap()用於獲取全部請求參數,那它也能夠獲取單個請求參數,全部咱們只須要重寫這個方法,而後其餘兩方法調用此方法就能夠。
public class MyHttpRequest extends HttpServletRequestWrapper { private HttpServletRequest req; // 原始請求對象 private String encoding; // 編碼格式 private boolean flag = true; // 開關 public MyHttpRequest(HttpServletRequest request, String encoding) { super(request); this.req = request; this.encoding = encoding; } @Override public String getParameter(String name) { Map<String, String[]> map = getParameterMap(); String[] values = map.get(name); if (null == values) { return null; } return values[0]; } @Override public String[] getParameterValues(String name) { Map<String, String[]> map = getParameterMap(); return map.get(name); } @Override public Map<String, String[]> getParameterMap() { String method = req.getMethod(); if (method.equalsIgnoreCase("post")) { try { req.setCharacterEncoding(encoding); return req.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get") && true == flag) { // 經過flag使這段代碼一次請求只執行一次,由於執行屢次至關於解碼再編碼屢次,形成亂碼 Map<String, String[]> map = req.getParameterMap(); // 對象賦值傳遞的是地址,map指向req地址,修改map值至關於修改req中屬性值 Set<String> set = map.keySet(); // 迭代遍歷map中全部值,經過解碼再編碼解決get亂碼 Iterator<String> iterator = set.iterator(); while (iterator.hasNext()) { String[] values = map.get(iterator.next()); if (null != values) { for (int i = 0; i < values.length; i++) { try { values[i] = new String(values[i].getBytes("ISO-8859-1"), encoding); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } flag = false; return map; } // 其它請求執行原始獲取參數方法,除了get post外還有其餘請求方式 return super.getParameterMap(); } }