`

JavaWeb之装饰 HttpServletRequest 对象

 
阅读更多

 

装饰 HttpServletRequest 对象 

 

需求:在 HttpServletRequest 对象到达 Servlet 之前把用户输入的多余空格都去掉

情景:因为 HttpServletRequest 对象里的请求参数都实际包含在 java.util.Map 对象里,而Map是不允许修改的,所以包含在 HttpServletRequest 对象里的请求参数不能被修改

解决方案:采取 Decorator(装饰器)模式

 

为何使用装饰模式增强HttpServletRequest(为何使用HttpServletRequestWrapper )?  先看下面实现敏感内容处理的过滤器的实现: 

 

 

package org.rabbitx.web.javaweb.filter.example;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.rabbitx.web.javaweb.filter.HttpFilter;
import org.rabbitx.web.javaweb.filter.MyHttpServletRequest;

public class ContentFilter extends HttpFilter{

    public void doFilter(HttpServletRequest request,
    		HttpServletResponse response, FilterChain filterChain)
    		throws IOException, ServletException {
    	//1. 获取请求 content 参数的值
    	String content = request.getParameter("content");
    	
    	//2. 把其中 fuck, shit 等字符串替换换为 ****
    	if(content.contains(" fuck ")){
    		//ServletRequest, HttpServletRequest 中并没有提供诸如 setParameter(paramName, paramValue)
    		//类似于这样的方法. 
    		
    		//目标: 改变 HttpServletRequest 的 getParameter(String) 方法的行为: 若该方法的返回值中
    		//包含 " fuck ", 则替换为 " **** "
    	}
    	
    	//3. 转到目标页面
    	filterChain.doFilter(req, response);
    }

}
  

 

 实现中发现一个问题,ServletRequest, HttpServletRequest 中并没有提供诸如 setParameter(paramName, paramValue)类似于这样的方法.所以我们只能尝试重写getParameter(String)方法:

1. 若对于一个类的方法不满意, 需要进行重写, 最常见的方式是, 继承父类, 重写方法. 若实现则需要继承 org.apache.catalina.connector.RequestFacade, 而这仅是 Tomcat服务器的实现, 若更换服务器, 该方案将无法使用. 与具体容器耦合,没有移植性.

2. 直接写一个 HttpServletRequest 接口的实现类: 很麻烦,而且也需要和具体的容器相耦合.

 

发现上面两种方法都不理想,来看下第三种方法:

 

装饰目前的 HttpServletRequest 对象: 装饰其 getParameter 方法, 而其他方法还和其实现相同.创建一个类, 该类实现 HttpServletRequest 接口, 把当前 doFilter 中的 request 传入到该类中, 作为其成员变量, 使用该成员变量去实现接口的全部方法. 对无需要增强或改变的方法,可以在装饰类中重写。 

 

具体实现

 

 

package org.rabbitx.web.javaweb.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class MyHttpServletRequest extends HttpServletRequestWrapper{

	public MyHttpServletRequest(HttpServletRequest request) {
		super(request);
	}
	
	@Override
	public String getParameter(String name) {
		String val = super.getParameter(name);
		if(val != null && val.contains(" fuck ")){
			val = val.replace("fuck", "****");
		}
		return val;
	}
}
  

 

 

 

package org.rabbitx.web.javaweb.filter.example;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.rabbitx.web.javaweb.filter.HttpFilter;
import org.rabbitx.web.javaweb.filter.MyHttpServletRequest;

public class ContentFilter extends HttpFilter{

    public void doFilter(HttpServletRequest request,
    		HttpServletResponse response, FilterChain filterChain)
    		throws IOException, ServletException {
    	//1. 获取请求 content 参数的值
    	String content = request.getParameter("content");
        //此处为实现 
    	HttpServletRequest req = new MyHttpServletRequest(request);

    	//3. 转到目标页面
    	filterChain.doFilter(req, response);
    }

}
 

 

 

 HttpServletWrapper 和 HttpServletResponseWrapper

 

1). Servlet API 中提供了一个 HttpServletRequestWrapper 类来包装原始的 request 对象,

HttpServletRequestWrapper 类实现了 HttpServletRequest 接口中的所有方法, 

这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法

 

//包装类实现 ServletRequest 接口. 

public class ServletRequestWrapper implements ServletRequest {

 

    //被包装的那个 ServletRequest 对象

    private ServletRequest request;

 

//构造器传入 ServletRequest 实现类对象

    public ServletRequestWrapper(ServletRequest request) {

if (request == null) {

   throw new IllegalArgumentException("Request cannot be null");   

}

this.request = request;

    }

 

//具体实现 ServletRequest 的方法: 调用被包装的那个成员变量的方法实现。 

    public Object getAttribute(String name) {

return this.request.getAttribute(name);

}

 

    public Enumeration getAttributeNames() {

return this.request.getAttributeNames();

}    

 

//...

}

 

相类似 Servlet API 也提供了一个 HttpServletResponseWrapper 类来包装原始的 response 对象

 

2). 作用: 用于对 HttpServletRequest 或 HttpServletResponse 的某一个方法进行修改或增强.

 

public class MyHttpServletRequest extends HttpServletRequestWrapper{

 

public MyHttpServletRequest(HttpServletRequest request) {

super(request);

}

 

@Override

public String getParameter(String name) {

String val = super.getParameter(name);

if(val != null && val.contains(" fuck ")){

val = val.replace("fuck", "****");

}

return val;

}

}

 

3). 使用: 在 Filter 中, 利用 MyHttpServletRequest 替换传入的 HttpServletRequest

 

HttpServletRequest req = new MyHttpServletRequest(request);

filterChain.doFilter(req, response);

 

此时到达目标 Servlet 或 JSP 的 HttpServletRequest 实际上是 MyHttpServletRequest

 

 

Decorator 模式

 

因为继承的关系,当需要改变某个对象的行为时,只须扩展这个对象所属的类并重写其有关的方法就可以达到目的。但是,当想要改变其行为的对象是由应用程序里的另一个子系统(例如:一个对象工厂或是一个Servlet 容器)负责构造,继承机制将无能为力

 

 

Decorator 模式----情景

 

已知:Messager 类的定义(可以从它派生处一个之类);Messager 对象总是来自一个对象工厂(MessagerFactory),该工厂可以对它创建的每一个 Messager 对象进行初始化----通过调用 getMessage() 方法而获得的 message 属性也不例外(即不能对 Messager 对象进行初始化)

假设:需要使用 Messager 类的 getMessage() 方法。有一个Util的使用工具类,该类中有如下方法:

      public static void broadcast(Message messager){

          System.out.println(messager.getMessage());

      }

 

Decorator 模式----需求,方案

 

需求:让 broadcast 方法打印的字母都是大写字母

方案:从 Messager 类派生一个子类,把子类对象传递给 broadcast 方法。因为只有对象工厂知道如何初始化 Messager 对象,所以该方案无意义

Decorator 模式:

从 Messager 类派生一个子类 MessagerDecorator,把子类对象传递给 broadcast 方法

在 MessagerDecorator 类里实现构造器:接受一个 Messager 对象作为输入参数,而这个 Messager 就是想要装饰的对象:public MessagerDecorator(Messager messager)

重写 getMessage 方法,让重写的方法用大写字母来返回 message 属性

 

 

HttpServletRequestWrapper 类

 

Servlet API 中提供了一个 HttpServletRequestWrapper 类来包装原始的 request 对象, HttpServletRequestWrapper 类实现了 HttpServletRequest 接口中的所有方法,这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法

相类似 Servlet API 也提供了一个 HttpServletResponseWrapper 类来包装原始的 response 对象

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics