由前一节,我们知道过滤器依赖于 servlet 容器。有时候我们需要在一个请求到达 Controller 之前能够截获其请求,并且根据其具体情况对 HttpServletRequest 中的参数进行过滤或者修改。比如:过滤低俗文字、危险字符等。
那我们就需要了解下ServletRequest、HttpServletRequest、ServletRequestWrapper以及HttpServletRequestWrapper这几个接口或者类之间的层次关系。
ServletRequest
是基接口。
HttpServletRequest
继承 ServletRequest。
ServletRequestWrapper
实现 ServletRequest。
HttpServletRequestWrapper
实现 HttpServletRequest ,又继承 了 ServletRequestWrapper。
那怎么如何在 Filter 中修改后台 Controller 中获取到的 HttpServletRequest 中的参数?
只需要在 Filter 中自定义一个类继承于
HttpServletRequestWrapper
,并重写 getParameterNames、getParameter、getParameterValues 等方法即可。
新增装饰类 ModifyParametersWrapper,继承于 HttpServletRequestWrapper
,(复用HttpServletRequestWrapper既有功能),用于获取请求参数。
package com.ymqx.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
public class ModifyParametersWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> parameterMap; // 所有参数的Map集合
/**
* 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
*/
public ModifyParametersWrapper(HttpServletRequest request) {
super(request);
parameterMap = request.getParameterMap();
}
// 重写几个HttpServletRequestWrapper中的方法
/**
* 获取所有参数名
*
* @return 返回所有参数名
*/
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<String>(parameterMap.keySet());
return vector.elements();
}
/**
* 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
*
* @param name
* 指定参数名
* @return 指定参数名的值
*/
@Override
public String getParameter(String name) {
String[] results = parameterMap.get(name);
if (results == null || results.length <= 0)
return null;
else {
System.out.println("修改之前: " + results[0]);
return modify(results[0]);
}
}
/**
* 获取指定参数名的所有值的数组,如:checkbox的所有数据
* 接收数组变量 ,如checkobx类型
*/
@Override
public String[] getParameterValues(String name) {
String[] results = parameterMap.get(name);
if (results == null || results.length <= 0)
return null;
else {
int length = results.length;
for (int i = 0; i < length; i++) {
System.out.println("修改之前2: " + results[i]);
results[i] = modify(results[i]);
}
return results;
}
}
/**
* 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
*
* @param string
* 原参数值
* @return 修改之后的值
*/
private String modify(String string) {
return "Modified: " + string;
}
}
新增 ModifyParametersWrapper,然后复写了 ServletRequest 中的几个方法,具体来说就是将原来的每个参数的值的前面加上了 “Modified: ”这个字符串。
新增过滤器 MyFilter ,继承于 OncePerRequestFilter
,重写 doFilterInternal()
方法,将装饰类 ModifyParametersWrapper
加入 filterChain
。
package com.ymqx.filter;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);
filterChain.doFilter(mParametersWrapper, response);
}
}
public void doFilter ( ServletRequest request, ServletResponse response );
方法中第一个参数是 ServletRequest,而 ModifyParametersWrapper 继承 HttpServletRequestWrapper ,而 HttpServletRequestWrapper 实现 ServletRequest 。
最终传参ModifyParametersWrapper 对象,会调用ModifyParametersWrapper 自定义逻辑方法。
<filter>
<filter-name>MyFilterfilter-name>
<filter-class>com.ymqx.filter.MyFilterfilter-class>
filter>
<filter-mapping>
<filter-name>MyFilterfilter-name>
<url-pattern>/filter/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
filter-mapping>
也可以通过注解方式声明:
@WebFilter(filterName = “MyFilter”, urlPatterns = {“/filter/*”})
public class MyFilter extends OncePerRequestFilter {
…
}
FilterController :
package com.ymqx.controller;
import com.ymqx.pojo.Product;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/filter")
public class FilterController {
@RequestMapping("/action1")
public void action1(@RequestParam("name") String name){
System.out.println("修改之后: " + name);
}
}
执行 http://localhost:8080/filter/action1?name=123 ,结果如下:
修改之前2: 123
修改之后: Modified: 123
这就表明了我们前面自定义的过滤器已经将 HttpServletRequest 中原来的参数成功修改了。
同时,还说明SpringMVC的
@RequestParam
注解本质上调用的是ServletRequest中的getParameterValues(String name)
方法,而不是getParameter(String name)
方法。
其实装饰类 ModifyParametersWrapper 只用到了构造方法 ModifyParametersWrapper() 和 getParameterValues() 方法,简化装饰类ModifyParametersWrapper:
package com.ymqx.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
public class ModifyParametersWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> parameterMap; // 所有参数的Map集合
/**
* 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
*/
public ModifyParametersWrapper(HttpServletRequest request) {
super(request);
parameterMap = request.getParameterMap();
}
/**
* 获取指定参数名的所有值的数组,如:checkbox的所有数据
* 接收数组变量 ,如checkobx类型
*/
@Override
public String[] getParameterValues(String name) {
String[] results = parameterMap.get(name);
if (results == null || results.length <= 0)
return null;
else {
int length = results.length;
for (int i = 0; i < length; i++) {
System.out.println("修改之前2: " + results[i]);
results[i] = modify(results[i]);
}
return results;
}
}
/**
* 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
*
* @param string
* 原参数值
* @return 修改之后的值
*/
private String modify(String string) {
return "Modified: " + string;
}
}
getParameterValues()
方法。doFilterInternal()
方法,将装饰类 ModifyParametersWrapper 加入 filterChain。getInputStream()
方法会返回 HttpServletRequest 请求里 body数据,将获取的原数据修改后,用修改后的数据构造新的 InputStream 流,替换原 InputStream 流。
新增装饰类 ModifyBodyWrapper ,继承于 HttpServletRequestWrapper
,用于获取请求报文体body数据。
public class ModifyBodyWrapper extends HttpServletRequestWrapper {
// 存放JSON数据主体
private String body;
/**
* Constructs a request object wrapping the given request.
*
* @param request
* @throws IllegalArgumentException if the request is null
*/
public ModifyBodyWrapper(HttpServletRequest request) {
super(request);
/* 1、获取Request的body数据 */
String requestBody = getBody(request);
System.out.println("修改前requestBody=" + requestBody);
/* 2、更改Request的body数据 */
JSONObject jsonObject = JSONObject.parseObject(requestBody);
Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
for (Map.Entry<String, Object> entry : entries) {
System.out.println("key=" + entry.getKey());
System.out.println("value=" + entry.getValue());
if (entry.getValue() instanceof String) {
entry.setValue(((String) entry.getValue()).trim());
}
}
body = jsonObject.toJSONString();
System.out.println("修改后requestBody=" + body);
/* 3、重写getInputStream()方法,将修改后的RequestBody返回 */
}
//获取Request的body数据
public String getBody(HttpServletRequest request){
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String buffer = null;
while (null != (buffer = bufferedReader.readLine())){
stringBuilder.append(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
定义全局变量 body 存放修改后的数据。
inputStream = request.getInputStream();
调用 HttpServletRequest 的方法 getInputStream(),获取 body 请求体;然后解析报文体,进行去空格处理。
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
重写 getInputStream()
方法,将修改后的报文体 body 返回。
其实装饰类ModifyBodyWrapper 只用到了构造方法 ModifyBodyWrapper() 和 getInputStream() 方法
新增过滤器 MyFilter2,继承于 OncePerRequestFilter
,重写 doFilterInternal()
方法,将装饰类 ModifyBodyWrapper 加入 filterChain。
public class MyFilter2 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ModifyBodyWrapper modifyBodyWrapper = new ModifyBodyWrapper(request);
filterChain.doFilter(modifyBodyWrapper, response);
}
}
<filter>
<filter-name>MyFilter2filter-name>
<filter-class>com.ymqx.filter.MyFilter2filter-class>
filter>
<filter-mapping>
<filter-name>MyFilter2filter-name>
<url-pattern>/filter/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
filter-mapping>
也可以通过注解方式声明:
@WebFilter(filterName = “MyFilter2”, urlPatterns = {“/filter/*”})
public class MyFilter2 extends OncePerRequestFilter {
…
}
FilterController :
@Controller
@RequestMapping("/filter")
public class FilterController {
@RequestMapping("/action1")
public void action1(@RequestParam("name") String name){
System.out.println("修改之后: " + name);
}
@RequestMapping("/action2")
public void action1(@RequestBody Product product){
System.out.println("Product:" + product);
}
}
postman 发送报文体,地址 http://localhost:8080/filter/action2
结果如下:
修改前requestBody={"id":"1001 ","name":"李雷 ","price":"1.2"}
key=price
value=1.2
key=name
value=李雷
key=id
value=1001
修改后requestBody={"price":"1.2","name":"李雷","id":"1001"}
Product:编号(id):1001,名称(name):李雷,价格(price):1.2
上送报文体的内容被修改。
getInputStream()
方法。doFilterInternal()
方法,将装饰类 ModifyBodyWrapper 加入 filterChain。为了打印简洁,去除装饰类ModifyParametersWrapper、ModifyBodyWrapper不必要打印输出,同时将两个过滤器注册到web.xml中。
<filter>
<filter-name>MyFilterfilter-name>
<filter-class>com.ymqx.filter.MyFilterfilter-class>
filter>
<filter-mapping>
<filter-name>MyFilterfilter-name>
<url-pattern>/filter/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
filter-mapping>
<filter>
<filter-name>MyFilter2filter-name>
<filter-class>com.ymqx.filter.MyFilter2filter-class>
filter>
<filter-mapping>
<filter-name>MyFilter2filter-name>
<url-pattern>/filter/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
filter-mapping>
控制器FilterController:
@Controller
@RequestMapping("/filter")
public class FilterController {
...
@RequestMapping("/action3")
public void action3(@RequestParam("name") String name, @RequestBody Product product){
System.out.println("修改之后: " + name);
System.out.println("Product:" + product);
}
}
postman 发送报文体,地址 http://localhost:8080/filter/action3?name=lili
结果如下:
修改前requestBody={"id":"1001 ","name":"李雷 ","price":"1.2"}
修改后requestBody={"price":"1.2","name":"李雷","id":"1001"}
修改之后: Modified: lili
Product:编号(id):1001,名称(name):李雷,价格(price):1.2
参考文章:
springmvc总结(配置传递参数去除前后空格、参数绑定时处理日期)
使用filter拦截参数去掉两端的空格
如何获取HttpServletRequest里body数据