解决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包中.其中, 所有输入流类都 ...
随机推荐
- C++对C的增强
一.namespace命名空间 1.C++命名空间基本常识所谓namespace,是指标识符的各种可见范围.c++标准程序库中的所有标识符都被定义与一个名为std的namespace中. 1.1:&l ...
- UVA562(01背包均分问题)
Dividing coins Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Descriptio ...
- TQ210——S5PV210 uboot顶层mkconfig分析
转自:http://blog.csdn.net/wqx521/article/details/52491300 ############################################ ...
- eclipse编辑窗口不见了(打开左边的java、xml文件,中间不会显示代码)
转自:https://blog.csdn.net/u012062810/article/details/46729779?utm_source=blogxgwz4 1. windows-->re ...
- Android开发--Activity
一:Activity生命周期 (1)Activity生命周期中的几种方法: protected void onCreate(Bundle savedInstanceState): protected ...
- Introduction to Multi-Threaded, Multi-Core and Parallel Programming concepts
https://katyscode.wordpress.com/2013/05/17/introduction-to-multi-threaded-multi-core-and-parallel-pr ...
- flex+blazeds
BlazeDS开发指南: http://www.cnblogs.com/xia520pi/archive/2012/05/26/2519343.html 使用BlazeDS实现Flex和Java通信 ...
- CodeForces - 357C Knight Tournament 伪并查集(区间合并)
Knight Tournament Hooray! Berl II, the king of Berland is making a knight tournament. The king has a ...
- JavaScript 基础 (变量声明, 数据类型, 控制语句)
创建: 2017/09/16 更新: 2017/09/24 更改标题 [JavaScript 概要]-> [JavaScript 基础] 完成: 2017/09/25 更新: 2017/10/0 ...
- BZOJ3289【莫队算法+树状数组+离散化】
思路: 区间逆序数即是交换次数. 逆序数,可以用树状数组吧. 怎么处理区间变换的时候求逆序数啊.. 这里分成左边的增/删,右边的增/删 因为是按时序插入, 所以左边增,增一个数,计算:ans+=sun ...