首页 > 代码库 > request.getInputStream为空的改进办法
request.getInputStream为空的改进办法
最近公司的一个小项目进行模块优化,需求是:跟踪记录每个IP请求的源数据和响应数据。当源数据包括requestBody的时候,从HttpServletRequest里面获取requestBody我们采用的是以下代码:
@RequestMapping(value = "/{user}/welcome", method = RequestMethod.POST) public ModelAndView queryUpdateStatus(@PathVariable String user, HttpServletRequest request) throws IOException{ Map<String, Object> json =new HashMap<String, Object>(); int size = request.getContentLength(); InputStream is = request.getInputStream(); byte[] reqBodyBytes = StreamUtils.readBytes(is, size); String res = new String(reqBodyBytes); System.out.println("requestBody:"+res); return new ModelAndView(new JsonView(),json); }
当然也有另外一种更快捷的办法,就是引入一个JAR包apktool.jar,使用以下代码也能获得requestBody:
@RequestMapping(value = "/{user}/welcome", method = RequestMethod.POST) public ModelAndView queryUpdateStatus(@PathVariable String user, HttpServletRequest request) throws IOException{ Map<String, Object> json =new HashMap<String, Object>(); String res = IOUtils.toString(request.getInputStream(),"UTF-8"); System.out.println("requestBody:"+res); return new ModelAndView(new JsonView(),json); }
现在为了实现跟踪记录的需求,为所有控制器进行AOP拦截:
@Component @Aspect public class LogAspect { protected final Logger log = Logger.getLogger(getClass()); @Around("execution(* demo.web.controller..*.*(..))") public Object around(ProceedingJoinPoint jp) throws Throwable { String businessName = jp.getSignature().getName(); String fileName = jp.getSignature().getDeclaringTypeName(); String params = initAssist(jp); int lastIndex = fileName.lastIndexOf("."); int cIndex = fileName.lastIndexOf("Controller"); fileName = fileName.substring(lastIndex + 1, cIndex); String requestLog = "actionLog_request:fileName:" + fileName + "...businessName:" + businessName + "...params " + params; log.info(requestLog); Object returnValue = null; try { returnValue = jp.proceed(jp.getArgs()); if (returnValue != null) { if (returnValue instanceof ModelAndView) { JSONObject jsonObj = JSONObject .fromObject(((ModelAndView) returnValue).getModel()); log.info(requestLog+"||actionLog_return:" + jsonObj.toString()); } } else { log.info("return:null"); } } catch (Exception e) { // TODO Auto-generated catch block log.info("abc log err"); e.printStackTrace(); String message = e.getMessage(); // throw e; } return returnValue; } private String initAssist(JoinPoint jp) { String result = ""; Class clazz = jp.getTarget().getClass(); String method = jp.getSignature().getName(); ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(this.getClass())); CtClass cc; try { cc = pool.get(clazz.getName()); CtMethod cm = cc.getDeclaredMethod(method); Object[] args = jp.getArgs();// 外部传参,不包括内部参数 String[] paramNames = matchParam(cm, cm.getParameterTypes().length); for (int i = 0; i < args.length; i++) { if(args[i]!=null){ try { if(args[i] instanceof String){ result += paramNames[i] + "-->" + args[i].toString() + ","; } if(args[i] instanceof HttpServletRequest){ HttpServletRequest req = (HttpServletRequest)args[i]; //获取requestHeader Map<String, String> requestHeader = new HashMap<String, String>(); Enumeration<String> headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { String key = (String) headerNames .nextElement(); String value = req.getHeader(key); requestHeader.put(key, value); } //获取requestBody String requestBody=IOUtils.toString(req.getInputStream()); //获取requestParameter Map<String, String> requestParams = new HashMap<String, String>(); Enumeration<String> pNames = req.getParameterNames(); while (pNames.hasMoreElements()) { String key = (String) pNames .nextElement(); String value = req.getHeader(key); requestParams.put(key, value); } } } catch (Exception e) { e.printStackTrace(); } }else{ result += paramNames[i] + "-->" + null + ","; } } } catch (NotFoundException e) { log.error(clazz.getName(), e); } return result; } private String[] matchParam(CtMethod cm, Integer arrLength) { try { String[] paramNames = new String[arrLength]; MethodInfo methodInfo = cm.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute .getAttribute(LocalVariableAttribute.tag);// 含有内部参数 if (attr == null) { log.error(""); return null; } for (int i = 0, j = 0; i < attr.tableLength(); i++) { if ("this".equals(attr.variableName(i))) { continue; } else { if (j < paramNames.length) { paramNames[j] = attr.variableName(i); j++; } } } return paramNames; } catch (Exception e) { log.error(e.getMessage(), e); } return null; } }
此时启动服务,请求对应接口调试时候你会发现如下情况:
[demo.web.filter.LogAspect] - requestBody:abc=abc
[demo.web.controller.DemoController] - requestBody:
结论:第二次使用request.getInputStream()是无法获取到任何信息。
为了实现需求,我们使用了springmvc中的注解 @RequestBody ,控制器代码修改为以下所示:
@RequestMapping(value = "/{user}/welcome", method = RequestMethod.POST) public ModelAndView queryUpdateStatus(@PathVariable String user, HttpServletRequest request,@RequestBody String requestBodyString) throws IOException{ Map<String, Object> json =new HashMap<String, Object>(); System.out.println("requestBody:"+requestBodyString); return new ModelAndView(new JsonView(),json); }
启动服务,请求对应接口调试时候你会发现如下情况:
[demo.web.filter.LogAspect] - requestBody:
[demo.web.controller.DemoController] - requestBody:abc=abc
结论:springmvc已经把request的输入流转换为对应的数据,因此,输入流已经被springmvc读过,程序第二次读流操作必定为空。
现在只需要把日志拦截器拦截requestbody的代码部分换成拦截requestBodyString就可以了。
第一次写博文,有写得不好的地方见谅。
本文出自 “13167258” 博客,请务必保留此出处http://13177258.blog.51cto.com/13167258/1952881
request.getInputStream为空的改进办法