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输入流和输出流只能读取一次的更多相关文章

  1. java里如何使用输入流和输出流实现读取本地文件里内容和写出到本地文件里

    不多说,直接上干货! 第一种方法 PWDemo.java package zhouls.bigdata.DataFeatureSelection.filter; import java.io.File ...

  2. 解决HttpServletRequest的输入流只能读取一次的问题

    背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...

  3. 解决SpringMVC拦截器中Request数据只能读取一次的问题

    解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...

  4. 大白话讲解如何解决HttpServletRequest的请求参数只能读取一次的问题

    大家在开发过程中,可能会遇到对请求参数做下处理的场景,比如读取上送的参数中看调用方上送的系统编号是否是白名单里面的(更多的会用request中获取IP地址判断).需要对请求方上送的参数进行大小写转换或 ...

  5. request.getInputStream() 流只能读取一次问题

    问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...

  6. C++输入流和输出流、缓冲区

    一.C++输入流和输出流 输入和输出的概念是相对程序而言的. 键盘输入数据到程序叫标准输入,程序数据输出到显示器叫标准输出,标准输入和标准输出统称为标准I/O,文件的输入和输出叫文件I/O. cout ...

  7. Java的IO流以及输入流与输出流的异同

    一:流的基本概念:           Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列.J ...

  8. Java:字节流和字符流(输入流和输出流)

    本文内容: 什么是流 字节流 字符流 首发日期:2018-07-24 什么是流 流是个抽象的概念,是对输入输出设备的抽象,输入流可以看作一个输入通道,输出流可以看作一个输出通道. 输入流是相对程序而言 ...

  9. java中的IO流(输入流与输出流)概述与总结

    Java中IO流,输入输出流概述与总结 总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都 ...

随机推荐

  1. 基于WinDbg的内存泄漏分析

    在前面C++中基于Crt的内存泄漏检测一文中提到的方法已经可以解决我们的大部分内存泄露问题了,但是该方法是有前提的,那就是一定要有源代码,而且还只能是Debug版本调试模式下.实际上很多时候我们的程序 ...

  2. Linux--top命令查看系统状态,所有值讲解

    Linux系统可以通过top命令查看系统的CPU.内存.运行时间.交换分区.执行的线程等信息.通过top命令可以有效的发现系统的缺陷出在哪里.是内存不够.CPU处理能力不够.IO读写过高. 一.top ...

  3. MPTCP in Wireshark(转)

    最新的wireshark可以直接识别出mptcp. Wireshark is a widely used network analyzer that can capture network traff ...

  4. c# 字符串大小写转换

    //小转大 string lower = "converted from lowercase"; Console.WriteLine(lower.ToUpper()); //大转小 ...

  5. Codeforces643A【一种暴力】

    mdzz,今天好烦啊,连特么暴力都不会写了. 题意是:给你n个数(<=n),然后让你求对于每个数输出含有他最多数量的区间数,还有如果存在相等的话,这个区间算小的那个 思路: 暴力起点,然后从小区 ...

  6. TyvjP1863 [Poetize I]黑魔法师之门(2014-8-27)

    P1863 [Poetize I]黑魔法师之门 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 经过了16个工作日的紧张忙碌,未来的人类终于收集到了足够的能源 ...

  7. nginx 的启动、停止与重启

    详解 ./nginx #打开 nginx nginx -s reload|reopen|stop|quit #重新加载配置|重启|停止|退出 nginx nginx -t #测试配置是否有语法错误 n ...

  8. PJzhang:最基本的正则表达式实例

    猫宁!!! 参考链接: https://www.cnblogs.com/fozero/p/7868687.html http://tool.oschina.net/regex/# http://too ...

  9. bzoj4300 绝世好题 【dp】By cellur925

    题目描述: 给定一个长度为\(n\)的数列\(a\),求\(a\)的子序列\(b\)的最长长度,满足bi&bi-1!=0(\(2<=i<=len\)). 90分做法: 并没有部分分 ...

  10. Hexo搭建博客教程(2) - 博客的简单个性化配置

    本章主要讲博客的个性化,譬如站点的基本配置(语言.头像.站点图标等).安装新的Hexo主题(NexT主题)以及主题的配置. 1. 修改站点配置 打开站点配置文件 ,找到: # Site title: ...