范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

解决HttpServletRequest流数据不可重复读

  背景介绍
  甲方客户的生产系统,有安全风险预警和安全事件快速溯源要求,需要做一套日志管理规范。
  要求我们接入的系统,要对用户登录、注册、密码修改等重要场景,严格按照提供的格式,输出相应的日志。
  后续通过filebeat对接,收集我们系统上的日志信息。
  简单来说,就是应用系统,处理接口请求时,统一打印相应日志。问题描述
  成熟且常见的日志统一打印方案,就是使用AOP技术,自定义注解,在切面上使用环绕通知@Around,拦截请求,获取Controller类上方法的入参、出参即可。
  奈何业务场景使用到的接口,以前的人在实现的时候,使用了如下方式:@RequestMapping(value = "/auth", method = { RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS }) public void auth(HttpServletRequest req, HttpServletResponse resp) {    authService.auth(req, resp); }
  把传参直接丢在 HttpServletRequest 中。
  返回参数,又是采用 HttpServletResponse 输出。public void printResult(HttpServletRequest req, HttpServletResponse resp,       String action, int code, String msg, Object result) {    PrintWriter p = null;    Ret ret = new Ret();    Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")          .serializeNulls()          .create();    try {       p = resp.getWriter();       ret.setRspCode(code);       ret.setRspDesc(msg);       ret.setData(result);        p.write(gson.toJson(ret));       return;    } catch (Exception e) {       logger.error(e.getMessage());    } finally {       p.flush();       p.close();    } }
  不像平时熟练的做法,把具体入参和出参,用对象封装,直接放在方法上即可。
  因为上面的做法,导致我们在拦截器中,想提前拦截请求获取传参,使用 request.getParameter() 等方法时,能拿到参数。 但是在具体接口业务流程中,再使用request.getParameter() 等方法,传入参数就获取不到了。
  因为流只能被读一次。
  因此就抛出一个问题:Request 和 Response 怎么重复读取?解决方案
  使用request.getParameter() 等方法,最终会调用getInputStream方法。
  需要重写HttpServletRequestWrapper包装类,在调用getInputStream方法时,将流数据同时写到缓存。
  后面想获取参数,直接读取缓存数据即可。
  这样就可以实现Request的内容多次读取。实现代码封装request
  自定义类 ContentCachingRequestWrapperimport 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.nio.charset.StandardCharsets;  /**  *  * 重写 HttpServletRequestWrapper  *  * @Author: linzengrui  * @Date: 2021/11/22 15:33  */ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {      private final byte[] body;      public ContentCachingRequestWrapper(HttpServletRequest request) {         super(request);         StringBuilder sb = new StringBuilder();         try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))){             String line = "";             while ((line = reader.readLine()) != null) {                 sb.append(line);             }         } catch (IOException e) {             e.printStackTrace();         }         body = sb.toString().getBytes(StandardCharsets.UTF_8);     }      @Override     public BufferedReader getReader() throws IOException {         return new BufferedReader(new InputStreamReader(getInputStream()));     }      @Override     public ServletInputStream getInputStream() throws IOException {          final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);          return new ServletInputStream() {              @Override             public boolean isFinished() {                 return false;             }              @Override             public boolean isReady() {                 return false;             }              @Override             public void setReadListener(ReadListener readListener) {              }              @Override             public int read() throws IOException {                 return inputStream.read();             }         };     }      public byte[] getBody() {         return body;     } }封装response
  自定义类 ContentCachingResponseWrapper import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.*;  /**  *  * 重写 HttpServletResponseWrapper  *  * @Author: linzengrui  * @Date: 2021/11/22 19:45  */ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {      private ByteArrayOutputStream byteArrayOutputStream;     private ServletOutputStream servletOutputStream;     private PrintWriter printWriter;      public ContentCachingResponseWrapper(HttpServletResponse response) {         super(response);         byteArrayOutputStream = new ByteArrayOutputStream();         servletOutputStream = new ServletOutputStream() {             @Override             public boolean isReady() {                 return false;             }              @Override             public void setWriteListener(WriteListener writeListener) {              }              @Override             public void write(int b) throws IOException {                 byteArrayOutputStream.write(b);             }         };         printWriter = new PrintWriter(byteArrayOutputStream);     }      @Override     public PrintWriter getWriter() {         return printWriter;     }      @Override     public ServletOutputStream getOutputStream() throws IOException {         return servletOutputStream;     }      public byte[] toByteArray() {         return byteArrayOutputStream.toByteArray();     }  }过滤器 Filter 拦截请求
  拦截器 LogFilter 使用上面封装的包装类,即可获取传参。@Slf4j @WebFilter(urlPatterns = "/*") public class LogFilter implements Filter {     @Override     public void init(FilterConfig filterConfig) throws ServletException {      }      @Override     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {         // 使用 重写 HttpServletRequestWrapper 的自定义包装类         ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest)request);         // 使用 重写 HttpServletResponseWrapper 的自定义包装类         ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);          // 只能执行一次获取流方法         try(ServletOutputStream outputStream = response.getOutputStream()){             // 获取传参             String requestParamJson = new String(requestWrapper.getBody());             log.info("requestParamJson --> {}", requestParamJson);              // 具体方法执行流程             chain.doFilter(requestWrapper, responseWrapper);              // 触发获取流操作后,可以从缓存多次拿数据             String respDataJson = new String(responseWrapper.toByteArray());             log.info("respDataJson <-- {}", respDataJson);              // TODO 写日志                           // 需要重新写入内容,否则流无输出内容             outputStream.write(respDataJson.getBytes(StandardCharsets.UTF_8));             outputStream.flush();         }catch (Exception e){             e.printStackTrace();         }      }       @Override     public void destroy() {      } }springboot 启动类添加注解 @ServletComponentScan
  注意:启动类要加上注解@ServletComponentScan识别上面注入的Filter。import org.springframework.boot.SpringApplication; import org.springframework.boot.web.servlet.ServletComponentScan;  @ServletComponentScan @SpringBootApplication public class SpringBootApplication {  	public static void main(String[] args) { 		SpringApplication.run(SpringBootApplication.class, args); 	} 	  }
  具体业务接口,原来的逻辑保持不变,仍然可以获取到入参。

年纪大耳背,配助听器有用吗?只要是有残余听力就可以佩戴助听器,效果是因人而异的。要到听力中心做听力检查和耳道检查,选择适合的助听器。您好!耳背要及时验配助听器。假如有听力损失,却不用助听器,会加速听觉退化。保尼康d810配什么样的镜头比较适合用来拍风景?总体来说,风光摄影中,广角镜头用的相对多一下,1424F2。8或者1635F4都是可以考虑的。长焦镜头,可以捕捉画面中的经典之处,70200这样的镜头也是应该配置的。这也是我风光摄WinPE能破解Windows的密码,这样Windows就不安全吗?更正一下,PE是清空密码,以实现免密码登陆,原理就是操作win的密码文件。这是会快速被发现的,而且严格来说不能算破解,只能算是绕过win的安全机制。至于服务器或非物理接触到的电脑,每天泡在头条里,也就几毛几分的,有必要坚持吗?每天泡在头条里,是为了及时了解国内外,发生的新闻,和学习其他有关方面,报导的新知识需要。在这个公众大平台上,广交良师益友,不断提高自己的写作水平和创作能力,写出好的作品,为弘扬主旋信用卡逾期,网贷逾期,想还清欠款,各位大神有什么推荐的副业吗?有驾驶证吗?怕不怕吃苦?怕不怕累?如果你上面的问题都搞清楚了,那你可以去跑滴滴,做代驾,送外卖等等,这些工作都可以兼职,辛苦点可以赚钱!1不怕辛苦的话,可以请送外卖快递,做代驾滴滴哪些远程操作软件比较好用?远程操控软件主要有两类一类是命令行远程工具,一类是图形界面远程工具。命令行工具主要程序员用得多,一般用来链接远程服务器图形界面工具普通用户用得多,一般用来链接远程桌面电脑进行辅助操奇特的索尼LinkBuds耳道内耳塞产品泄露机身似乎是完全开放的当涉及到耳机时,索尼从来就不是一个回避尝试新事物的公司,而泄露的LinkBuds不同于目前市场上的任何其他产品。我们所看到的似乎是一套开放式耳内TWS耳塞,所以极不可能在这里看到任情人节该送什么?这两款好评率92以上的数码产品,好看又实用马上就是2月14日情人节了,不知道你准备好礼物了没?在移动互联网浪潮的当下,如果觉得口红首饰送腻了,送一款有颜有才的数码产品无疑是非常合适的,有新意又实用。下面就为大家臻选几款颜值比特早报苹果成2021年全球芯片最大买家,AMD将完成对赛灵思收购2022年2月11日消息,昨夜今晨,科技圈都发生了哪些大事?行业大咖抛出了哪些新的观点?比特网为您带来值得关注的科技资讯苹果成2021年全球芯片的最大买家近日,Gartner发布了5款产品打造高颜值极简桌面办公环境清爽效率才高清爽整洁的桌面不光是看起来美观清爽,更能提高办公效率,让自己的心情更加愉悦。我自己就是一个喜欢极简风数码产品的人,身边的数码产品都是极简风格的,整体氛围和谐统一。这里我挑选了5款桌家用热门WIFI6评测2020年已经进入WiFi6普及之年,以3000Mbps性能的WiFi6路由器为例,其价格从高不可攀的千元级别,直降到300元左右的级别。目前大多数家庭用户都愿意花钱升级网络体验,
全球芯片疯狂涨价,为何苹果还能降价推出新机?台积电在背后帮忙给苹果涨价3,其他公司涨价10苹果召开新品发布会之后,其年度重磅新品iPhone13系列机型的售价让大家喜出望外产品性能升级情况下,售价还要比iPhone12系列的首发价便宜300元。而苹果这家美国巨头的操作更太疯狂!iPhone13卖爆了,苹果官网被买到崩,加量不加价底气何在?新机备货数据遭泄密热点复盘如果苹果发布会是果粉的春晚,那么发售阶段的火爆程度则等同于春运。9月17日,iPhone13系列正式开启预售,加量不加价的新款,叠加各大平台推出的优惠政策,依旧高企的售价也难阻热情跟王兴创业的清华高材生,41岁急流勇退,拿1。5亿年薪享受人生农村走出来的清华毕业生,跟王兴共同创办了美团,正值壮年却选择了退出公司的经营体系,如今每年的年薪高达1。5亿,他究竟是谁?又是如何帮助王兴实现成功的呢?王兴的清华舍友随着国内线上消强脑科技(BrainCo)黑科技点亮人生,助力残障人士5月28日,失去双手的倪敏成借助BrainCo研发的智能仿生手在今日中国节目中使用毛笔写下今日中国四个大字,又一次引发了社会对残疾患者的关注。目前中国约有八千多万的残疾人士,其中肢佳能5D4尼康D850索尼A7R3摄影哪个画质更好?让我们先看看国外著名机构的评测对比图片分别是5D4D850A7R3。上面几张照片,朋友们能分辨出来吗,我们在看看日光下不同ISO比较图上图是ISO100上图是ISO1600上图是I你觉得华为手机卖的贵吗?华为手机不贵,比苹果和三星便宜不少,很多方面已经超过苹果和三星,特别是华为的高端手机质量品质杠杠的,如今的华为手机引领世界潮流,在国内卖的很实惠,在国外的售价是不便宜的,但一样非常想问下升级了鸿蒙系统的华为手机用户,会比EMUI11好用吗?特别流畅,功能增加,界面亲和。界面一样,有一点不一样,比安卓流畅多了,丝般顺滑我觉得比安卓好用,好玩的东西比较多,和安卓比,安卓显得比较乏味。鸿蒙系统非常好!本人工薪阶层,买的荣耀道德经是否推崇愚民统治?道德经非是愚民的统治,是暗示人的灵魂回过头来如何统治(驾驭)自己的有机整体。道德经老子全篇的论述都是对一个人潜在的道德本性而言的。人身喻世界,世界喻人身,就是说一个有机的整体是和我华为是否有足够的底气让欧美国家不得不买,竞争力确实如此之强吗?应邀回答本行业问题。暂时来看,华为是有足够的底气让欧美国家不得不买的,这块竞争力的确是比较强的。一直在通信业里混了20年,也在移动通信这块混了10年,不得不说,看到了华为推出的这些神舟十二号返回过程中有没有可能有人故意扰乱或者拦截行为?基本不可能。第一,截不住。返回舱返回过程中的各种高端数据属于国家核心机密,一般小国无法获取,二般大国不屑获取。第二,没有必要。返回舱并非有什么极高端机密,就目前来讲,我国载人航天工怎样才能延长电动车电池寿命?现在电动车真的是非常的普及了,几乎很很多人家中都会有电动车,但是很多人家在电动车使用了一段时间之后都会发现这样的一件事,就是电量越来越不耐用,这时候可能就要会有很多人问了,这到底是