zoukankan      html  css  js  c++  java
  • 扯淡过滤器之乱码篇

      在JavaWeb开发中,Servlet过滤器可以很方便地帮助开发者做很多重复的事情,比如说这里要和大家分享的乱码问题。其实说起乱码自己也没有什么经验可谈,只是东拼西凑来出来的一些代码,这里说过滤器是一方面,另一方面还有其中用到的一些思想上的东西。
      乱码产生的原因说来说去就一句话,编码和解码用的码表不同造成。但是要弄清楚这其中的原理,怕是自己也不清楚,只好扬长避短。Web开发中的乱码就发生在服务器和浏览器之间,这样根据乱码的作用者可以分为请求参数乱码和响应内容乱码,响应乱码容易解决,只要response.setContentType("text/html;charset=utf-8"),就可以简单搞定;但是对于请求参数乱码又分了post提交和get提交参数的乱码处理,这两种提交方式的乱码处理也不一样,当然还有其他提交方式,只是不常用就没有考虑。
      对于post提交,使用request.setCharacterEncoding("utf-8")也可以简单搞定,但是get方式提交的中文数据就不能这样来处理。首先搞清楚产生的原因很有必要:浏览器会以服务器response中设置的编码发送请求参数到服务器,但是服务器并不知道以什么编码来解码,默认情况下会使用ISO-8859-1字符集对参数解码,而ISO-8859-1中没有中文,所以这种情况下查出来的基本上都会是"?"来显示,可以肯定的是,服务器接收到的编码本身是没有问题,只是他自作主张查了错误的编码表,所以我们就可以用ISO-8859-1编码表再查回去,然后再重新以正确的编码表查出来就解决了get方式提交的参数乱码问题,在代码上的体现也可以很直观:
        request.getParameter("nickname") -- 查错码表的数据
        request.getParameter("nickname").getBytes("ISO-8859-1") -- 使用错误的码表再错误一次
        new String(request.getParameter("nickname").getBytes("ISO-8859-1"),"utf-8") -- 正确的表得正确
      这样我们的get提交的中文乱码也可以解决了,所以通过以上剖析可以开发自己的全站乱码过滤器了,还等什么呢?代码奉上。

    package com.hitech.filter;
    
    import java.io.IOException;
    import java.util.Map;
    
    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;
    import javax.servlet.http.HttpServletRequestWrapper;
    
    public class ApplicationEncodingFilter implements Filter {
    
        private String encoding = null;
        public void init(FilterConfig filterConfig) throws ServletException {
            this.encoding = filterConfig.getInitParameter("encoding");
        }
    
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            // 解决响应中文乱码
            response.setContentType("text/html;charset=" + encoding);
            // 解决请求中文乱码
            chain.doFilter(new _HttpServletRequest((HttpServletRequest) request), response);
        }
    
        public void destroy() {
    
        }
    
        class _HttpServletRequest extends HttpServletRequestWrapper {
    
            private HttpServletRequest request = null;
            // 由于Map的缓存,所以需要控制只对请求参数做一次编解码
            private boolean flag = true;
            // 父类没有提供无参构造,所以这里也只能有无参构造
            public _HttpServletRequest(HttpServletRequest request) {
                super(request);
                this.request = request;
            }
    
            @SuppressWarnings("unchecked")
            @Override
            public Map<String, String[]> getParameterMap() {
                try {
                    if ("post".equals(request.getMethod().toLowerCase())) {
                        // 如果是post提交的参数,只需要指定编码
                        request.setCharacterEncoding(encoding);
                        return request.getParameterMap();
                    }else if ("get".equals(request.getMethod().toLowerCase())) {
                        Map<String, String[]> parameterMap = request.getParameterMap();
                        // 对于get提交的参数,系统默认用ISO-8859-1解码,所以要先用ISO-8859-1先编码回去,再按指定编码解码
                        if (flag) {// 由于Map会对提交过来的数据进行缓存,所以只需要编码一次以防止正确的编码再次被编码
                            // 遍历Map,对String[]中的每一项值进行重新编解码
                            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                                String[] values = entry.getValue(); 
                                for (int i = 0; i < values.length; i++) {
                                    values[i] = new String(values[i].getBytes("ISO-8859-1"),encoding);
                                }
                            }
                            // 编码一次后,不再需要编码
                            flag = false;
                        }
                        return parameterMap;
                    }else {
                        // 其他方式提交的数据,不作处理,直接返回
                        return super.getParameterMap();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            @Override
            public String[] getParameterValues(String name) {
                // 从getParameterMap中的取出已经修改好的Values[]
                return getParameterMap().get(name);
            }
    
            @Override
            public String getParameter(String name) {
                // 需要对NullPointerException做处理
                String[] parameterValues = getParameterValues(name);
                return parameterValues == null ? null : parameterValues[0];
            }
        }
    }

      有了filter,再在web.xml中配置filter便可以使用。

      <filter>
          <filter-name>CodecFilter</filter-name>
          <filter-class>com.hitech.filter.ApplicationEncodingFilter</filter-class>
          <init-param>
              <param-name>encoding</param-name>
              <param-value>utf-8</param-value>
          </init-param>
      </filter>
        
      <filter-mapping>
          <filter-name>CodecFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>    

      完成了filter之后,再看看其中用到的一种设计模式--包装模式,谈到包装模式先说说对于一个对象上的方法进行重写,可以有三种方式完成。
      1、使用继承
          一个类继承另一个类后,可以对父类中已经的方法进行重写,以完成子类特有的功能。但是在这里似乎不太合适,原因是无法改变ServletRequest的继承结构。
      2、使用包装设计模式
         显然这个方式在这里是可行的,也就是使用一个类实现和要包装的类相同的接口,构造方法中接受一个需要包装的对象,然后就可以重新定义其中的方法。
      3、动态代理
       使用java中的反射机制也可以完成这样的功能,但是看起来似乎稍微有些难以理解,上来一个demo吧。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class DynamicProxy {
        public static void main(String[] args) {
            final ProxyDemo demo = new ProxyDemo();
            Proxy proxy = (Proxy)java.lang.reflect.Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), ProxyDemo.class.getInterfaces(), new InvocationHandler() {
                
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    // 如果只想重新定义function方法可以这样
                    if ("function".equals(method.getName())) {
                        // 这里重新定义function方法的功能
                        System.out.println("This is a new function");
                        return null;
                    }else {
                        return method.invoke(demo, args);
                    }
                }
            });
            proxy.method();
            proxy.function();
        }
    }
    class ProxyDemo implements Proxy{
    
        public ProxyDemo(){}
        
        public void method() {
            System.out.println("This is a method.");
        }
    
        public void function() {
            System.out.println("This is a function.");
        }
    }
    interface Proxy{
        public abstract void method();
        public abstract void function();
    }

      代码看起来似乎比干巴巴的说一大堆效果好,别人的思想,自己理解接受了,就是自己的,加油。
      为了心中的美好,不妥协直到变老。

  • 相关阅读:
    Redis常见7种使用场景(PHP)
    阻塞式I/O实现简单TCP通信
    telnet客户端程序
    TCP简单回射程序
    getsockname和getpeername函数
    close函数
    TCP时间获取程序
    listen函数
    基本套接字编程
    readline.c
  • 原文地址:https://www.cnblogs.com/magics/p/3733439.html
Copyright © 2011-2022 走看看