解决spring http输入流和输出流只能读取一次
2024-08-29 08:12:29
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) { }
}
}
最新文章
- GPU 加速NLP任务(Theano+CUDA)
- HTTP请求中的Body构建——.NET客户端调用JAVA服务进行文件上传
- 曝光卖假币的店铺和旺旺ID
- TreeComboBox控件范例
- supesite 标签语法
- 用Delphi的TIdHttp控件发起POST请求和Java的Servlet响应
- java后台开发- NOTE
- CodeForces 525D Arthur and Walls
- window下查看端口命令
- Java多机部署下的定时任务处理方案(mysql)
- C++11 作用域内枚举
- mysql下载安装及常见问题
- c编译动态库可以编译但是无法导入解决方法
- Workspace in use or cannot be created, choose a different one.
- BZOJ3028食物——生成函数+泰勒展开
- Spring Boot 打war包后自定义404页面不生效解决方法
- spring boot集成MyBatis
- 用Java构建一个简单的WebSocket聊天项目之新增HTTP接口调度
- [Linux] 关于Unix哲学
- Linux下查找命令