解决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) {

        }
    }
}

 

posted @ 2019-04-14 16:52  zero_and_one  阅读(2461)  评论(0编辑  收藏  举报