javaweb全站编码过滤器

今天听了朴乾老师讲的关于servlet过滤器的内容,自己跟着实现了一次朴乾老师写的全站过滤器,写篇文章记录下

1、把中文乱码问题分成两类,第一类是服务器输出到浏览器的乱码

    这个很好解决,直接 response.setContentType("text/html;charset=utf-8");

2、第二类是浏览器端传参数到服务器的乱码,又可以分成两小类,get和post

    post很好解决,直接 request.setCharacterEncoding("utf-8"); 便可以了,因为对于post方法,参数是跟在每个报文的实体内容里的

    而get传递过来的参数乱码便不好解决了,因为get方式的参数是跟在url后面的,所以像处理post过来参数那样设置request.setCharacterEncoding("utf-8")是行不通的,因为其设置的是实体内容的编码。同时http协议里url是用内什么编码的,反正不支持中文 = =, 所以需要这样处理

      String name = new String(request.getParameter("name").getBytes("iso8859-1"), "utf-8");

所以实现全站过滤器(全站过滤器就是处理每个请求和响应的中文编码问题)的关键就是处理get方式传递过来的参数

那么问题来了,request里只提供了getParameter,getParameterValues和getParameterMap三种取参数的方式,却没有设置参数的方法=  =,就意味着在过滤器的doFilter方法里我们可以参数取出来解决中文乱码,却没有办法设置回这个请求request = = 

解决的方法是我们重写一个自定义的request,然后改写getParameterMap方法,先贴代码

class MyHttpServletRequest extends HttpServletRequestWrapper {

	private HttpServletRequest request;
	//用于标识是否已经处理过map
	boolean isEncoded = false;
<span style="white-space:pre">	</span>//要处理成什么编码
	String encoding;
	
	public MyHttpServletRequest(HttpServletRequest request, String encoding) {
		super(request);
		this.request = request;
		this.encoding = encoding;
	}
	
	@Override
	public String getParameter(String name) {
		//若存在该参数值则肯定是数组里的第一个,否则返回null
		return this.getParameterValues(name) != null ? this.getParameterValues(name)[0] : null;
	}
	
	@Override
	public String[] getParameterValues(String name) {
		return (String[]) this.getParameterMap().get(name);
	}
	
	@Override
	public Map getParameterMap() {
		//取参数问题分两种情况,post和get
		//post直接设置setCharacterEncoding便可
		if (request.getMethod().equalsIgnoreCase("post")) {
			try {
				request.setCharacterEncoding(this.encoding);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return request.getParameterMap();
		} else if (request.getMethod().equalsIgnoreCase("get")) {
		//get方式则取出map里的内容逐个遍历处理
			Map<String, String[]> map = request.getParameterMap();
			if (!isEncoded) {
				isEncoded = true;
				for (Map.Entry<String, String[]> entry : map.entrySet()) {
					String[] values = entry.getValue();
					for (int i=0; i<values.length; i++) {
						try {
							values[i] = new String(values[i].getBytes("iso8859-1"), this.encoding);
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}
				}
			}
			return map;
		} else {
			return request.getParameterMap();
		}
	}
}


为什么继承HttpServletRequestWrapper这个类,因为这里采用了“装饰者模式”的策略去实现自定义的HttpServletRequest,下面是HttpServletRequestWrapper这个类的API文档说明

Provides a convenient implementation of the HttpServletRequest interface that can be subclassed by developers wishing to adapt the request to a Servlet.This class implements the Wrapper or Decorator pattern. Methods default to calling through to the wrapped request object.

如果是直接实现HttpServletRequest,那要实现很多个方法

这个自定义的类里主要是getParameterMap方法的重写,其实无非就是对request里存放参数的那个map里的每条记录进行迭代,然后让它指向一个处理过乱码的String,既values[i] = new String(values[i].getBytes("iso8859-1"), this.encoding);

有一个很关键的地方,这个类里有一个isEncoded的布尔变量,让整个map只处理一次

之所以这么做是因为比方我们在servlet的代码里写了request.getParameter("name"),经过一次过滤器的处理取出来的内容不会中文乱码,因为过滤器已经处理过了new String(values[i].getBytes("iso8859-1"), this.encoding)

但如果在服务端代码要用到同一个参数两次,既前面写了一次request.getParameter("name")而后面又写了一次,根据朴乾老师的说法是tomcat对此的处理是第一次组织这个map之后会把它缓存起来,第二次不会再进行一次组织而是直接取缓存。

那意味着第一次取参数的map里的内容已经是处理好了的没有中文乱码的内容了,而第二次取参数的时候相当于对map里处理好了的内容又进行了一次迭代处理,既new String(values[i].getBytes("iso8859-1"), this.encoding),本来已经是用utf-8编码的内容又执行了values[i].getBytes("iso8859-1"),所以又变成了乱码……

所以整个过程里我们要保证map里的参数内容只被处理过一次,所以用了isEncoded这个标识

下面贴上过滤器类的代码

public class EncodingFilter implements Filter {

	FilterConfig filterConfig;
	String encoding;
	
	public void destroy() {

	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		//处理输出问题
		response.setContentType("text/html;charset=" + this.encoding);
		//用自定义的MyHttpServletRequest处理表单传递到服务器的中文问题
		chain.doFilter(new MyHttpServletRequest((HttpServletRequest) request, this.encoding), response);
	}

	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		//从配置文件读取编码
		encoding = filterConfig.getInitParameter("encoding") == null ? "utf-8" : filterConfig.getInitParameter("encoding");
	}

}

还是比较简单的,就doFilter方法处理浏览器到服务器和服务器到浏览器两种情形的中文编码问题


自己之前在一家软件公司实习培训的时候,我们的mentor说表单传过来的数据统一用post方法。现在一想果然这样最省事了,虽然不知道这样处理恰不恰当。因为自己还是学生并木有工作经验很多东西还是寄希望于以后再工作中慢慢体会。

文章组织的很凌乱,但主要是自己记录学习历程,而不是在将为什么会有中文乱码这种原理性的东西。

附上两篇读过的将中文编码问题的文章,看得那叫一个累啊 = = 

http://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值