先说下这个请求包装器在我自身项目上的应用:
参数拦截
通过定义 ApiImplicitParams 中的过滤模式 fillMode(宽松或者严谨)来达到最终 getParaMap 中值的设值(具体查看以下代码)
ApiImplicitParams(参考Swagger相关类进行自身项目的调整)
参数过滤
暂未实现(自己之前某些项目有单独实现),其实很简单,还是通过注解的方式对每个参数进行定义最后它要过滤成的一个格式,通常用于存在 xss 字段注入的字段用于后台直接转义其值进行保存
请求体的多次调用支持
理论上 HttpKit.readData(getRequest()) 仅能获取一次请求体,第二次获取会报错,这个我想大家都懂的,流已经被取走了,你再去掏也掏不出啥。
package plus.jfinal.core.http; import jodd.io.StreamUtil; import plus.jfinal.annotations.swagger.ApiImplicitParam; import plus.jfinal.annotations.swagger.ApiImplicitParams; import plus.jfinal.enums.ParamFillMode; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Vector; /** * 参数包裹体 */ public class ParameterHttpServletRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest originalRequest = null; private final static String ENCODING = "UTF-8"; private String contentType = "application/x-www-form-urlencoded;"; private ApiImplicitParams apiImplicitParams; private static final String JSON_CONTENT_TYPE_ = "application/json"; private Map<String, String[]> parameterMap = null; /** * 请求体 */ private byte[] body; public ParameterHttpServletRequestWrapper(HttpServletRequest request, ApiImplicitParams apiImplicitParams) throws IOException { super(request); this.originalRequest = request; this.apiImplicitParams = apiImplicitParams; /** * 要实现的功能: * 1. 对参数接收的限制 * 2. 对json参数的获取及转换为parametermap * 3. 接收到表单请求(含文件时)的处理 */ /** * 判断上传请求类型 * null * application/x-www-form-urlencoded; charset=UTF-8 * multipart/form-data; boundary=----WebKitFormBoundarytkozbhbsNZEAQBN4 * application/json */ contentType = super.getContentType() == null ? "application/x-www-form-urlencoded;" : super.getContentType(); contentType = contentType.split(";")[0]; if(JSON_CONTENT_TYPE_.equals(contentType)){ body = StreamUtil.readBytes(request.getReader(), ENCODING); } } /** * 构造参数 */ public Map<String, String[]> buildParameterMap(){ if(parameterMap!=null){ return this.parameterMap; } this.parameterMap = new HashMap<>(); if(apiImplicitParams !=null && ParamFillMode.Strict == apiImplicitParams.fillMode()){ ApiImplicitParam[] apiImplicitParam = apiImplicitParams.value(); if(apiImplicitParam.length > 0){ for(ApiImplicitParam param : apiImplicitParam){ /** * 后续在这里考虑对值进行处理 */ /** * 设置值 */ if(super.getParameterValues(param.name())!=null){ this.parameterMap.put(param.name(),super.getParameterValues(param.name())); } } }else{ /* 若列为空就默认变成宽松模式 */ this.parameterMap.putAll(super.getParameterMap()); } }else{ this.parameterMap.putAll(super.getParameterMap()); } return this.parameterMap; } /** * 获取所有参数名 * * @return 返回所有参数名 */ @Override public Enumeration<String> getParameterNames() { Vector<String> vector = new Vector<String>(buildParameterMap().keySet()); return vector.elements(); } /** * 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型 * * @param name 指定参数名 * @return 指定参数名的值 */ @Override public String getParameter(String name) { String[] results = buildParameterMap().get(name); return results[0]; } /** * 获取指定参数名的所有值的数组,如:checkbox的所有数据 * 接收数组变量 ,如checkobx类型 */ @Override public String[] getParameterValues(String name) { return buildParameterMap().get(name); } @Override public Map<String, String[]> getParameterMap() { return buildParameterMap(); } public void setParameterMap(Map<String, String[]> parameterMap) { this.parameterMap = parameterMap; } /** * 若是 JSON 类型的,就优先读取本地缓存的数据 * @return * @throws IOException */ @Override public BufferedReader getReader() throws IOException { if(JSON_CONTENT_TYPE_.equals(contentType)){ return new BufferedReader(new InputStreamReader(getInputStream())); }else{ return super.getReader(); } } @Override public ServletInputStream getInputStream() throws IOException { if(JSON_CONTENT_TYPE_.equals(contentType)){ final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bais.read(); } }; }else{ return super.getInputStream(); } } }
调用方式
1. 定义一个 handler 在其里面对 request 进行包装
request = new ParameterHttpServletRequestWrapper(request,apiImplicitParams);
2. 在你自身工程的 config 中,把你自己的这个 handler 配置进去
以上只是我自己整理的框架中的一个支持,在用的框架支持的另外一个功能就是直接根据swagger注解(有微调,命名一样,里面field有增减)达到生成swagger文档以及基于这个注解上面实现参数验证(包含请求体中的参数!)
以下是示例:
package x.x.x.web.index.controller; import plus.jfinal.annotations.AccessPass; import plus.jfinal.annotations.RestController; import plus.jfinal.annotations.swagger.ApiImplicitParam; import plus.jfinal.annotations.swagger.ApiImplicitParams; import plus.jfinal.annotations.swagger.ApiOperation; import plus.jfinal.core.controller.BaseController; import plus.jfinal.enums.ParamType; /* 定义controller控制层,仅定义请求路径若要定义视图格式: @RestController(path="/test",viewPath="/views/test")*/ @RestController("/test") public class TestController extends BaseController { @AccessPass //定义访问放行 @ApiOperation() //定义它是要生成swagger接口文档 @ApiImplicitParams(value = { @ApiImplicitParam(name="b",description="参数B",required = true), @ApiImplicitParam(name="d.b",description="参数D.B",required = true) //代表我要验证请求体中 d 下的 b 参数 }, paramType = ParamType.BODY) //定义参数验证 public void index(){ String json = getParams().requestBody().toJSONString(); //自己封装的方法,支持直接把请求体转为JSONObject(基于 fastjson 增强了一些便捷取值的方法),还有其它的支持哦~ renderJson(json); } }
请求内容
{ "a":1, "b":"111", "d":{ "a":2 } }
返回
{ "code": 430, "success": false, "message": "参数D.B 不能为空" }
getRawData() 的优点是可以反复多次调用,不会抛异常