解决spring http输入流和输出流只能读取一次
1.需求:给某些请求接口记录日志,记录请求的数据和响应的数据和请求所花费的时间。这里采用非侵入式编程,也业务代码进行解耦。按照spring AOP 的编程思想。
2.编程设计:在spring 拦截器中植入日志代码。因为其刚好满足非侵入,且能捕获到请求和响应的数据。
3.了解spring 拦截器和过滤器的运行原理
先执行过滤器,然后执行拦截器。
4. 分析:当在拦截器中获取请求的输入流和响应的输出流的时候发现,只能读取一次,拦截器在具体的业务代码之前执行,导致请求的输入流被拦截器使用,到controller 的时候将获取不到数据。当响应的数据流被拦截器的postHandler 使用之后,输出到客户端没有响应的数据。
流程:先创建过滤器,过滤器是链式执行,配置的web.xml 的中。
WrapperFilter 在web.xml 中的配置
<filter>
<filter-name>sosWrapperFilter</filter-name>
<filter-class>com.xiao.handler.filter.WrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sosWrapperFilter</filter-name>
<!-- 过滤的url地址 -->
<url-pattern>/myTest/*</url-pattern>
</filter-mapping>
增加一个拦截器配置,配置需要过滤器的请求地址,走 spring mvc的拦截器,拦截的是请求地址(不包含上下文 eg:http://localost:8080/xiao)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test/*"/>
<bean class="com.xiao.interceptor.SosOrderInterceptor"/>
</mvc:interceptor>
</mvc:interceptors> </beans>
开始编写过滤器,拦截器
过滤器
/**
*
* @describ request 请求过滤器增强
* @date 2019-03-06
* @author coder_xiao
*/
public class WrapperFilter implements Filter { private static Log log = LogFactory.getLog(WrapperFilter.class); @Override
public void init(FilterConfig filterConfig) throws ServletException { } @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
CacheContentRequestWrapper requestWrapper=null;
CacheContentResponseWrapper responseWrapper=null;
HttpServletRequest request=(HttpServletRequest)servletRequest;
HttpServletResponse response=(HttpServletResponse) servletResponse;
try {
requestWrapper = new CacheContentRequestWrapper(request);
responseWrapper = new CacheContentResponseWrapper(response);
}catch (Exception e){
log.error("获取requestWrapper失败!",e);
}finally {
if((requestWrapper!=null)&&(responseWrapper!=null)) {
filterChain.doFilter(requestWrapper, responseWrapper);
responseWrapper.reWriteResponse(responseWrapper,(HttpServletResponse) servletResponse);
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
} } @Override
public void destroy() { }
}
拦截器
public class SosOrderInterceptor implements HandlerInterceptor { private final static Log logger = LogFactory.getLog(SosOrderInterceptor.class); @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
BufferedReader bufferedInput=null;
String data=null;
try{
String urlPath=request.getRequestURI();
String contentType=request.getContentType();
Boolean typeIndex=contentType==null?false:contentType.indexOf("application/json")>-1;
Boolean createIndex=urlPath.indexOf("createOrder")>-1;
Boolean buildIndex=urlPath.indexOf("buildDraftOrder")>-1;
Boolean urlIndex=(urlPath.indexOf("createSosOrder")+urlPath.indexOf("buildDraftSosOrder"))>-2;
//POST 请求 contentType=application/json
if(typeIndex&&urlIndex) {
//支持mark and reset 为使流复用
CacheContentRequestWrapper contentRequestWrapper= new CacheContentRequestWrapper(request);
data = contentRequestWrapper.getBodyString();
String requestId=UUIDUtil.uuid();
request.setAttribute("requestId",requestId);
BuildAudit audit=BeanTool.getBean(BuildAudit.class);
//创建订单
if (createIndex) {
logger.info("开始调用创建订单");
audit.buildData(data,"1","1","创建订单",requestId);
}
//创建草稿
if (buildIndex) {
logger.info("创建草稿单!");
audit.buildData(data,"2","1","创建草稿单",requestId);
}
}
}catch (Exception e){
e.printStackTrace();
logger.error(e);
}finally {
if(bufferedInput!=null) {
bufferedInput.close();
}
}
return true;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
logger.info("postHandle");
try{
String urlPath=request.getRequestURI();
String contentType=request.getContentType();
Boolean typeIndex=contentType==null?false:contentType.indexOf("application/json")>-1;
Boolean createIndex=urlPath.indexOf("createOrder")>-1;
Boolean buildIndex=urlPath.indexOf("buildDraftOrder")>-1;
Boolean urlIndex=(urlPath.indexOf("createSosOrder")+urlPath.indexOf("buildDraftOrder"))>-2;
String requestId= request.getAttribute("requestId")==null?null:request.getAttribute("requestId").toString();
if(typeIndex&&urlIndex) {
CacheContentResponseWrapper cacheResponse =(CacheContentResponseWrapper)((WebStatFilter.StatHttpServletResponseWrapper) response).getResponse();
String data=new String(cacheResponse.getResponseData(),"UTF-8");
BuildAudit audit=BeanTool.getBean(BuildAudit.class);
//创建订单
if (createIndex) {
audit.buildData(data,"1","2",null,requestId);
}
//创建草稿
if (buildIndex) {
audit.buildData(data,"2","2",null,requestId);
}
//草稿单转正
if (urlPath.indexOf("buildDraftSosOrder") > -1) { }
}
}catch (Exception e){
logger.error(e);
}
} @Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
logger.info("afterCompletion");
try{ }catch (Exception ex){
logger.error(ex);
}
}
两个缓存器
package com.lacesar.handler.wrapper; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; /**
* @description 包装spring 的 HttpServletRequestWrapper
*/
public class CacheContentRequestWrapper extends HttpServletRequestWrapper { private static Log log = LogFactory.getLog(CacheContentRequestWrapper.class); /**
* 存储body数据的容器
*/
private final byte[] body; private String requestId; public CacheContentRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 将body数据存储起来
String bodyStr = getBodyString(request);
body = bodyStr.getBytes("UTF-8");
} /**
* 获取请求Body
*
* @param request request
* @return String
*/
public String getBodyString(final ServletRequest request) {
try {
return inputStream2String(request.getInputStream());
} catch (IOException e) {
log.error("", e);
throw new RuntimeException(e);
}
} /**
* 获取请求Body
*
* @return String
*/
public String getBodyString() {
if (body != null) {
final InputStream inputStream = new ByteArrayInputStream(body);
return inputStream2String(inputStream);
} else {
return null;
}
} /**
* 将inputStream里的数据读取出来并转换成字符串
*
* @param inputStream inputStream
* @return String
*/
private String inputStream2String(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null; try {
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("", e);
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.error("", e);
}
}
} return sb.toString();
} public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
} public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); return new ServletInputStream() {
public int read() throws IOException {
return inputStream.read();
} public boolean isFinished() {
return false;
} public boolean isReady() {
return false;
} public void setReadListener(ReadListener readListener) {
}
};
} public String getRequestId() {
return requestId;
} public void setRequestId(String requestId) {
this.requestId = requestId;
}
}
package com.lacesar.handler.wrapper; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException; import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper; public class CacheContentResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer = null;
private ServletOutputStream out = null;
private PrintWriter writer = null; public CacheContentResponseWrapper(HttpServletResponse resp) throws IOException {
super(resp);
buffer = new ByteArrayOutputStream();
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, "UTF-8"));
} /**
* 重新写入response 数据
*
* @param wrapperResponse
* @param response
*/
public void reWriteResponse(CacheContentResponseWrapper wrapperResponse, HttpServletResponse response) {
String result = null;
try {
result = new String(wrapperResponse.getResponseData(), "UTF-8");
//解决可能在运行的过程中页面只输出一部分
response.setContentLength(-1);
response.setCharacterEncoding("UTF-8");
PrintWriter out = null;
out = response.getWriter();
if (out != null) {
out.write(result);
out.flush();
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
} public ServletOutputStream getOutputStream() throws IOException {
return out;
} public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
} public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
} public void reset() {
buffer.reset();
} public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
} private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
bos = stream;
} @Override
public void write(int b) throws IOException {
bos.write(b);
} @Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
} public boolean isReady() {
return false;
} public void setWriteListener(WriteListener writeListener) { }
}
}
解决spring http输入流和输出流只能读取一次的更多相关文章
- java里如何使用输入流和输出流实现读取本地文件里内容和写出到本地文件里
不多说,直接上干货! 第一种方法 PWDemo.java package zhouls.bigdata.DataFeatureSelection.filter; import java.io.File ...
- 解决HttpServletRequest的输入流只能读取一次的问题
背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...
- 解决SpringMVC拦截器中Request数据只能读取一次的问题
解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...
- 大白话讲解如何解决HttpServletRequest的请求参数只能读取一次的问题
大家在开发过程中,可能会遇到对请求参数做下处理的场景,比如读取上送的参数中看调用方上送的系统编号是否是白名单里面的(更多的会用request中获取IP地址判断).需要对请求方上送的参数进行大小写转换或 ...
- request.getInputStream() 流只能读取一次问题
问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...
- C++输入流和输出流、缓冲区
一.C++输入流和输出流 输入和输出的概念是相对程序而言的. 键盘输入数据到程序叫标准输入,程序数据输出到显示器叫标准输出,标准输入和标准输出统称为标准I/O,文件的输入和输出叫文件I/O. cout ...
- Java的IO流以及输入流与输出流的异同
一:流的基本概念: Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.J ...
- Java:字节流和字符流(输入流和输出流)
本文内容: 什么是流 字节流 字符流 首发日期:2018-07-24 什么是流 流是个抽象的概念,是对输入输出设备的抽象,输入流可以看作一个输入通道,输出流可以看作一个输出通道. 输入流是相对程序而言 ...
- java中的IO流(输入流与输出流)概述与总结
Java中IO流,输入输出流概述与总结 总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都 ...
随机推荐
- bzoj1799同类分布——数位DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1799 数位DP. 1.循环方法 预处理出每个位数上,和为某个数,模某个数余某个数的所有情况: ...
- Linux : linux命令之 svn
感谢前辈的整理,让我直接站在巨人的肩膀上.来自:http://www.jb51.net/os/RedHat/2461.html 1.将文件checkout到本地目录 svn checkout path ...
- vector中resize()和reserve()区别
先看看<C++ Primer>中对resize()函数两种用法的介绍: 1.resize(n) 调整容器的长度大小,使其能容纳n个元素. 如果n小于容器的当前的size,则删除多出来的元素 ...
- 1.SJ-SLAM-14
1.引言 SLAM:Simultaneous Localization and Mapping 同时定位与地图构建 搭载特定传感器的主体,在没有环境先验信息的情况下,于运动过程中建立环境的模型,同时估 ...
- Flutter实战视频-移动电商-16.补充_保持页面状态
16.补充_保持页面状态 修正一个地方: 设置了item的高度为380 横向列表为380.最终build的高度也增加了50为430. 增加了上面的高度以后,下面那个横线划掉的价格可以显示出来了. 但是 ...
- UVa 820 Internet Bandwidth (裸板网络流)
题意:有一个计算机网络,输入节点数n,输入网络流源点和汇点src,des,再输入双向边数m.给出m条边的负载,求最大流. 析:直接上网络流的最大流. 代码如下: #pragma comment(lin ...
- HDU - 3410 Passing the Message 单调递减栈
Passing the Message What a sunny day! Let’s go picnic and have barbecue! Today, all kids in “Sun Flo ...
- C#操作句柄
1.直接上例子吧:收集系统信息msinfo32时,会有一个弹窗,现在要隐藏该弹窗,首先看没有通过句柄隐藏弹窗的现象 2.收集系统信息导入到一个位置 代码: Process[] msinfo32proc ...
- 动态加载dll
extern "C" MMUPDATENOTIFY_IMPEXP bool _cdecl NotifyThrift(char* chThriftIp, char* chPor) H ...
- OPENGL1_环境
补充说明: 1 vs2010自带opengl的库,不需要单独下载或做任何配置,(但是lib文件,dll文件放的位置过于分散,用的时候感觉不好用) 使用时直接用 #include <gl/GL.h ...