参考文章 : xiaoqingge.vip/2020/12/05/…
应用场景
在Servlet编程模型中经常遇到类似场景,需要修改HttpServletRequest的内容,比如:添加或修改header及 参数列表。
但ServletRequest在设计之初是遵循“开闭原则”(类对扩展开放,对修改关闭)的。所以HttpServletRequest默认是只读的,提供了一些获取其属性的方法,未暴露相关setter method,此时便可借助HttpServletRequestWrapper修改其中的内容。
request提供的相关获取属性的方法:
设计原理
HttpServletRequestWrapper继承结构图如下:
其中,HttpServletRequestWrapper继承于ServletRequestWrapper,实现于HttpServletRequest。
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
...
}
HttpServletRequestWrapper使用装饰者模式来实现对ServletRequest的包装,
- 对现有对象的行为进行覆写(重写
ServletRequest相关getter方法); - 添加新的行为职责(新增
setter相关方法)。
实际上就是对java语言多态特性的一种应用,关于装饰者模式,有疑问请看我的另一篇文章:Java设计模式–装饰者模式
看一下装饰器如何进行包装的 ServletRequestWrapper 实现 ServletRequest 并包装 ServletRequest
public class ServletRequestWrapper implements ServletRequest {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static final ResourceBundle lStrings =
ResourceBundle.getBundle(LSTRING_FILE);
private ServletRequest request;
/**
* Creates a ServletRequest adaptor wrapping the given request object.
*
* @param request The request to wrap
*
* @throws IllegalArgumentException if the request is null
*/
public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException(lStrings.getString("wrapper.nullRequest"));
}
this.request = request;
}
HttpServletRequestWrapper 继承 ServletRequestWrapper, 并包装 HttpServletRequest
public class HttpServletRequestWrapper extends ServletRequestWrapper implements
HttpServletRequest {
/**
* Constructs a request object wrapping the given request.
*
* @param request The request to wrap
*
* @throws java.lang.IllegalArgumentException
* if the request is null
*/
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
private HttpServletRequest _getHttpServletRequest() {
return (HttpServletRequest) super.getRequest();
}
HttpServletRequest 是没有暴露出任何setter方法修改的接口的,那我们如何能够传递修改HttpServletRequest后的信息呢?
可以换一个思路,即然HttpServletRequest修改不了,那我们就换个类的对象,这个类对象需要包装HttpServletRequest这个对象,就相当于上面的 HttpServletRequestWrapper 一样,有一个成员变量就是 HttpServletRequest,并且HttpServletRequestWrapper包装类去继承HttpServletRequest「同样也实现了ServletRequest」,拥有HttpServletRequest定义的所有接口。如HttpServletRequest中有getHeader方法,HttpServletRequest里的header信息是没办法修改的,但是我们现在有包装器了呀,包装期并且实现了HttpServletRequest,也会有getHeader,并且我们可以按照自己的逻辑去重写getHeader,我们想加什么就加什么,而且基于java的多态的性质, HttpServletRequest 和 HttpServletRequestWrapper 都是 ServletRequest类型的,所以我们如果想修改原来的HttpServletRequest,那我们就可以选择不再使用原来的HttpServletRequest对象,而是选择使用HttpServletRequestWrapper,HttpServletRequestWrapper 包装 HttpServletRequest,在调用getHeader的时候也不再使用HttpServletRequest的getHeader方法,而是使用HttpServletRequestWrapper的getHeader方法。
具体实现
自定义ServletRequestWrapper
- 借助
HashMap存储请求头与参数列表; - 重写
getter方法; - 新增
setter方法(此处命名为addXXX())。
1. /**
1. * 自定义 HttpServletRequestWrapper 添加或修改请求头、参数列表
1. */
1. public class MapRequestWrapper extends HttpServletRequestWrapper {
1.
1. /**
1. * 请求头集合
1. */
1. private Map<String, String> headerMap = new HashMap<>();
1.
1. /**
1. * 参数集合
1. */
1. private Map<String, String[]> paramMap = new HashMap<>();
1.
1. public MapRequestWrapper(HttpServletRequest request) {
1. super(request);
1. // 将 request 中的参数赋值给当前 map
1. paramMap.putAll(request.getParameterMap());
1. }
1.
1. public void addHeader(String name, String value) {
1. headerMap.put(name, value);
1. }
1. // 重写 getHeader 方法,这样在使用包装器对象调用的时候,会调研重写后的方法。
//先调用被包装对象的getHeader,然后调用重写后的方法进行覆盖
1. @Override
1. public String getHeader(String name) {
1. String value = super.getHeader(name);
1. if (headerMap.containsKey(name)) {
1. return headerMap.get(name);
1. }
1.
1. return value;
1. }
1.
1. @Override
1. public Enumeration<String> getHeaderNames() {
1. ArrayList<String> nameList = Collections.list(super.getHeaderNames());
1. for (String name : headerMap.keySet()) {
1. nameList.add(name);
1. }
1.
1. return Collections.enumeration(nameList);
1. }
1.
1. @Override
1. public Enumeration<String> getHeaders(String name) {
1. List<String> valueList = Collections.list(super.getHeaders(name));
1. if (headerMap.containsKey(name)) {
1. valueList = Arrays.asList(headerMap.get(name));
1. }
1.
1. return Collections.enumeration(valueList);
1. }
1.
1. @Override
1. public int getIntHeader(String name) {
1. return Integer.valueOf(getHeader(name));
1. }
1.
1. @Override
1. public long getDateHeader(String name) {
1. String value = getHeader(name);
1. if (value == null) {
1. return -1L;
1. } else {
1. long date = FastHttpDateFormat.parseDate(value);
1. if (date == -1L) {
1. throw new IllegalArgumentException(value);
1. } else {
1. return date;
1. }
1. }
1. }
1.
1. public void addParameter(String name, String value) {
1. String[] valueArr = paramMap.get(name);
1. if (valueArr == null) {
1. valueArr = new String[]{name};
1. paramMap.put(name, valueArr);
1. } else {
1. // 将添加的参数作为数组的首元素,并拷贝后续元素
1. String[] newValueArr = new String[valueArr.length + 1];
1. newValueArr[0] = value;
1. for (int i = 0; i < valueArr.length; i++) {
1. newValueArr[i+1] = valueArr[i];
1. }
1. paramMap.put(name, newValueArr);
1. }
1. }
1.
1. public void addParameter(String name, String[] valueArr) {
1. paramMap.put(name, valueArr);
1. }
1.
1. @Override
1. public Map<String, String[]> getParameterMap() {
1. return paramMap;
1. }
1.
1. @Override
1. public String getParameter(String name) {
1. String value = super.getParameter(name);
1. if (paramMap.containsKey(name)) {
1. String[] valueArr = paramMap.get(name);
1. if (valueArr != null && valueArr.length != 0) {
1. return valueArr[0];
1. }
1. }
1.
1. return value;
1. }
1.
1. @Override
1. public Enumeration<String> getParameterNames() {
1. ArrayList<String> paramList = Collections.list(super.getParameterNames());
1. for (String name : paramMap.keySet()) {
1. paramList.add(name);
1. }
1.
1. return Collections.enumeration(paramList);
1. }
1.
1. @Override
1. public String[] getParameterValues(String name) {
1. String[] valueArr = super.getParameterValues(name);
1. if (paramMap.containsKey(name)) {
1. return paramMap.get(name);
1. }
1.
1. return valueArr;
1. }
1. }
Filter中修改Request
- 借助
HttpServletRequest构建HttpServletRequestWrapper; - 调用
setter方法修改属性。
1. @Component
1. @WebFilter
1. public class TestFilter implements Filter {
1.
1. @Override
1. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
1.
1. HttpServletRequest request = (HttpServletRequest) servletRequest;
// 使用包装器对象, 包装器重写 getHeader 方法
1. MapRequestWrapper requestWrapper = new MapRequestWrapper(request);
1. // 添加请求头
1. requestWrapper.addHeader("from", "wrapper");
1.
1. // 添加参数
1. requestWrapper.addParameter("param2", "wrapper");
1.
1. filterChain.doFilter(requestWrapper, servletResponse);
1. }
1. }
获取修改的属性
Controller中获取修改的内容并加以验证。
- @Slf4j
- @RestController
- @RequestMapping("/test")
- public class TestController {
- @PostMapping("/testRequestWrapper")
- public String testRequestWrapper(HttpServletRequest request) {
- // 获取本来就有的请求头
- String name = request.getHeader("name");
- log.info("header name: {}", name);
- // 获取添加的请求头
- Enumeration from = request.getHeaders("from");
- ArrayList fromList = Collections.list(from);
- log.info("header from: {}", fromList.toString());
- // 获取本来就传的参数
- String param1 = request.getParameter("param1");
- log.info("parameter param1: {}", param1);
- // 获取添加的参数
- String[] param2Arr = request.getParameterValues("param2");
- log.info("parameter param2: {}", Arrays.asList(param2Arr).toString());
- return "SUCCESS";
- }
- }
结果展示
发送请求:
结果展示:
由图可知,正确获取添加的头信息“from”、“param2”。
功能扩展
可根据实际应用场景,重写或添加新的操作行为,比如修改Cookies、获取输入流等等。