web过滤器中获取请求的参数(content-type:multipart/form-data)
1.前言:
1.1 在使用springMVC中,需要在过滤器中获取请求中的参数token,根据token判断请求是否合法;
1.2 通过requst.getParameter(key)方法获得参数值;
这种方法有缺陷:它只能获取 POST 提交方式中的Content-Type: application/x-www-form-urlencoded;
HttpServletRequest request= (HttpServletRequest) req;
String param = request.getParameter("param");
2.问题:
在一般的请求中,content-type为:application/x-www-form-urlencoded;在此种请求中,使用request.getParam(key)方法可以获取到key对应的属性值;
因为最近涉及到文件的上传操作,上传文件的请求中content-type为:multipart/form-data;此种请求无法直接用request.getParam(key)获取对应的属性值,request中获取的属性值全部为空,无法正常获取;
3.问题描述:
3.1 在web.xml中配置的过滤器
<filter> <filter-name>requestFilter</filter-name> <filter-class>util.web.RequestFilter</filter-class> </filter> <filter-mapping> <filter-name>requestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.2 过滤器RequestFilter.java中获取token做匹配
在如下过滤器中,上传文件中的content-type:multipart/form-data使用获取request.getParameter(key)无法获取相应的值。需要借助Spring框架中的CommonsMultipartResolver.resolveMultipart(HttpServletRequest request)将request转为MultipartHttpServletRequest,从而使用getParameter(key)方法获取指定的值;
* 在将对象转化完成后,要将转化完成的对象赋值给过滤链中的request参数中,即如下代码中的 req = multiReq; 赋值完成很重要,否则在controller层中依旧无法获取其他参数。
如果不需要再filter中获取请求中的值,则无需如下的操作,在请求经过springMVC框架后,自动会识别请求方式,如果是文件请求,会自动调用CommonsMultipartResolver.resolveMultipart(HttpServletRequest request)方法转化;
package util.web;public class RequestFilter implements Filter { protected FilterConfig filterConfig; protected boolean filterEnabled; protected int logLevel; protected boolean needVCode; protected List<String> noFilerList =null; private CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); public RequestFilter() { this.filterConfig = null; this.filterEnabled = true; this.logLevel = -1; } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { if(this.filterEnabled){ HttpServletRequest httpReq = (HttpServletRequest)req; HttpServletResponse httpResp = (HttpServletResponse)resp; String ctxPath = httpReq.getContextPath(); String requestUri = httpReq.getRequestURI(); //请求的全路径,比如: String uri = requestUri.substring(ctxPath.length());//全路径除去ctxPath String tarUri = uri.trim(); String operatorHtmlModel = (httpReq.getHeader("referer")!=null?httpReq.getHeader("referer"):"").trim(); //获取当前页面的url,判断url是否是后台而url(后台的html就两个) //不在过滤列表里的url请求,过滤列表包括t_sys_filter表中数据及visitor角色用户下的授权页面 if(!this.isInNoFilerList(tarUri)){ UserInfo uInfo = SessionUtil.getCurrentUser(); LoginAccount regAccout=SessionUtil.getCurrentPlateLoginAccount(); int type = 0 ;//平台账号未登录 if(operatorHtmlModel.endsWith(Const.LOGIN_TYPE_HTML[0])||operatorHtmlModel.endsWith(Const.LOGIN_TYPE_HTML[1])){ type = 1;//后台账号未登录 } // int type = (SessionUtil.getAttribute(Const.LOGIN_TYPE)!=null)?Integer.valueOf(SessionUtil.getAttribute(Const.LOGIN_TYPE).toString()):0; if(regAccout==null){//平台账号未登录 if(tarUri.endsWith(".do")){ httpResp.sendRedirect(ctxPath+"/"+Const.TIMEOUT_SERVICE); return; }else if(tarUri.endsWith("/")){ httpResp.sendRedirect(ctxPath+"/"+Const.INDEX_PAGE); return; } }else{//平台账号登录 if(tarUri.endsWith("/")){ httpResp.sendRedirect(ctxPath+"/error/noSecurity.htm"); return; }else if(tarUri.endsWith(".do") && !isWithoutUri(tarUri)){ String contentType = httpReq.getContentType();//获取请求的content-type String post_csrftoken = ""; if(contentType.contains("multipart/form-data")){//文件上传请求 *特殊请求
/*
CommonsMultipartResolver 是spring框架中自带的类,使用multipartResolver.resolveMultipart(final HttpServletRequest request)方法可以将request转化为MultipartHttpServletRequest
使用MultipartHttpServletRequest对象可以使用getParameter(key)获取对应属性的值
*/
MultipartHttpServletRequest multiReq = multipartResolver.resolveMultipart(httpReq); post_csrftoken=multiReq.getParameter(Const.SESSION_CSRFTOKEN);//获取参数中的token req = multiReq;//将转化后的reuqest赋值到过滤链中的参数 *重要 }else{//非文件上传请求 post_csrftoken=httpReq.getParameter(Const.SESSION_CSRFTOKEN);//获取参数中的token } //csrf防御:判断是否带token //post_csrftoken=httpReq.getParameter(Const.SESSION_CSRFTOKEN); String csrftoken=(String)SessionUtil.getAttribute(Const.SESSION_CSRFTOKEN); if(post_csrftoken==null || !csrftoken.equals(post_csrftoken)){ //判断为不安全的访问 httpResp.sendRedirect(ctxPath+"/common/goNoSecurity.do"); return; } } } // 设定网页的到期时间,一旦过期则必须到服务器上重新调用 httpResp.setDateHeader("Expires", -1); // Cache-Control 指定请求和响应应遵循的缓存机制 no-cache指示请求或响应消息是不能缓存的 httpResp.setHeader("Cache-Control", "no-cache"); // 用于设定禁止浏览器从本地缓存中调用页面内容,设定后一旦离开页面就无法从Cache中再调出 httpResp.setHeader("Pragma", "no-cache"); } chain.doFilter(req, resp); } } @SuppressWarnings("unchecked") @Override public void init(FilterConfig cfg) throws ServletException { //初始化操作 } private Boolean isWithoutUri(String tarUri){ String[] withoutUriStrings = {//无需匹配token的请求 "/common/goNoSecurity.do", "/plateFormCommon/isLoginForPlateForm.do", "/supplierForPlateForm/getCompanyListByRegId.do" /*,"/PfTaskFileCtrl/addOrUpdateTaskImgFile1.do"*/ /*,"/PfTaskFileCtrl/addOrUpdateTaskImgFileForUpdate.do"*/ }; for(String uri:withoutUriStrings){ if(uri.equals(tarUri)){ return true; } } return false; } }
3.3 控制层 PfTaskFileCtrl.java
如果在filter中未将转化的request值赋值给过滤链,则在这里无法获取fileType对应的值;
package platform.common.controller;/** * mogodb文件上传下载 * 项目名称:outsideeasy * 类名称:PfTaskFileCtrl * 创建人:mishengliang * 创建时间:2016-4-26 下午1:55:19 * 修改人:mishengliang * 修改时间:2016-4-26 下午1:55:19 * @version * */ @Controller @RequestMapping("PfTaskFileCtrl") public class PfTaskFileCtrl { @Autowired private PfRegisterAttchedService registerAttchedService; @Autowired private FileOptService fileService; @Autowired private PfUpdateRegisterAttchedService updateRegisterAttchedService; @Autowired private CompanyForPlateFormService companyForPlateFormService; @RequestMapping(value = { "/companyImageView" }, method = { RequestMethod.GET }) public ModelAndView gojsp_companyImageView(ModelAndView modelAndView ){ modelAndView.setViewName("/companyWindow/companyImageView"); return modelAndView; } /** * @Description:企业文件上传 * PfTaskFileCtrl * addOrUpdateTaskImgFile1 * @param request * @param response * @return * @throws Exception String * @author yukai * 2016-8-4 上午10:03:00 */ @DocLogger(explain="企业文件上传") @RequestMapping(value="addOrUpdateTaskImgFile1",method=RequestMethod.POST) @ResponseBody public String addOrUpdateTaskImgFile1(HttpServletRequest request,HttpServletResponse response) throws Exception{ Map<String,Object> qryParam = WebUtil.getDefaultParamsMap(request); LoginAccount regAccount = SessionUtil.getCurrentPlateLoginAccount(); Map<String,Object> params=new HashMap<String, Object>(); JSONObject json = new JSONObject(); json.put("success", true); /* * 1.检查参数 */ if(regAccount == null){//获取任务id json.put("message", "未登录"); return json.toString() ; }
// *如果在filter中未将转化的request值赋值给过滤链,则在这里无法获取fileType对应的值 if(WebUtil.isEmpty(request.getParameter("fileType"))){//获取任务id json.put("message", "没有文件类型值"); return json.toString() ; } /* *2.赋值 */ int fileType = Integer.parseInt(request.getParameter("fileType")); String fileName = request.getParameter("fileName"); String formatType = request.getParameter("formatType"); PfRegisterAttched pfRegisterAttched = new PfRegisterAttched(); pfRegisterAttched.setFile_type_id(fileType);//文件类型值 pfRegisterAttched.setFile_name(fileName);//文件名 if(fileName.indexOf(",") != -1){ json.put("message", "文件名中存在非法字符(英文逗号),请先去除后上传"); return json.toString() ; } /* * 3.对文件信息的处理 */ MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; if(WebUtil.isEmpty((CommonsMultipartFile) multipartRequest.getFile("file"))){ json.put("message", "没有文件"); return json.toString() ; } CommonsMultipartFile file = (CommonsMultipartFile) multipartRequest.getFile("file"); //对应前台文件对象 if(file!=null && file.getSize()>0){//检查文件大小和格式 if (file.getSize() >5*1024*1024) { json.put("message", "文件太大,超过5M"); return json.toString() ; } String originalName=file.getOriginalFilename(); String this_suffix = ""; params.put(Const.ISIMG, 0); params.put(Const.USE_TYPE, fileType); params.put(Const.USERNAME, regAccount.getLogin_name()); params.put(Const.COM_ID, qryParam.get("companyId")); params.put(Const.COM_NAME,companyForPlateFormService.getCompanyNameByCompanyId(qryParam)); boolean flag=false;//默认不 是图片 //获取文件后缀,与传过来的参数file_name重新组装文件名 if(originalName.indexOf(".")>0){//有后缀 XX.jpg XX.RAR this_suffix=originalName.substring(originalName.lastIndexOf(".")); String[] format = null; if("image".equals(formatType)){//判断上传文件的类型:image 图片,text 文档 format = Const.imgArray; params.put(Const.ISIMG, 1); }else if("text".equals(formatType)){ format = Const.otherArray; } for(String suffix:format){ if(suffix.equalsIgnoreCase(this_suffix)){ flag=true; break; } } } if(!flag){ json.put("message", "不是指定格式"); return json.toString() ; }else{ /* * 4.进行信息的处理 */ Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String mongodbId=fileService.SaveFile(file,params); pfRegisterAttched.setMogodb_id(mongodbId);//把存储mongoDb的文件序号存到数据库中 pfRegisterAttched.setCreate_dt(date); pfRegisterAttched.setFile_format(this_suffix); Integer id = registerAttchedService.addAppRegisterAttched(pfRegisterAttched);//获取存入信息的id json.put("fileId",id); json.put("mongodbId", mongodbId); json.put("creatDate", sf.format(date)); json.put("message", "上传成功"); } }else{ json.put("message", "文件不存在"); } return json.toString(); } }
4.解决方案
在问题描述中已有问题解决方案。
此方法主要利用了Spring框架中已有的工具类。
参考资料:http://www.itdadao.com/articles/c15a279110p0.html
5.总结
5.1 不同的content-type请求获取参数值的方法不同。
5.2 在multipart/form-data请求方式中,需要利用SpringMVC框架中的CommonsMultipartResolver类包装转化为MultipartHttpServletRequest获取参数值;
6. 参考学习
1. servlet3.0 Tomcat7.0 简洁方案
如果使用的是servlet3.0及以上版本,multipart/form-data请求方式取值可以使用 HttpServletRequest.getPart(key)方法获取指定值;
2.通常的三种请求方式获取值方法
* application/x-www-form-urlencoded
*application/json
* text/xml
可以使用将请求转化为流,再转为字符串获取相应的值,通过如下方法获取的字符串获取指定的参数值;
转化方法如下:
/** * 获取请求Body * * @param request * @return */ public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); }
参考资料:http://blog.csdn.net/pyxly1314/article/details/51802652