Request Response repeatable read

background

Usually in order to facilitate the localization problem, we need to record the interface to the Senate and the parameters. However, due to the characteristics of the stream can not be repeated read, it will lead to unexpected problems.

Wrapper

As a request, response wrapper class, we can transfer by rewriting getOutputStream getInputStream and control data, so as to achieve a repeatable read data.

HttpServletRequestWrapper

package cn.caojiantao.spider;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * @author caojiantao
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    private byte[] data;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        data = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

    public byte[] toByteArray() throws IOException {
        return data;
    }
}
复制代码

HttpServletResponseWrapper

package cn.caojiantao.spider;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * @author caojiantao
 */
public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream byteArrayOutputStream;

    private ServletOutputStream servletOutputStream;

    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        byteArrayOutputStream = new ByteArrayOutputStream();
        servletOutputStream = new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {

            }

            @Override
            public void write(int b) throws IOException {
                response.getOutputStream().write(b);
                // 同时写入字节数组
                byteArrayOutputStream.write(b);
            }
        };
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return servletOutputStream;
    }

    public byte[] toByteArray() {
        return byteArrayOutputStream.toByteArray();
    }
}
复制代码

response.getOutputStream () and response.getWriter () mutex, not both.

Examples of - log filter

package cn.caojiantao.spider.configuration;

import cn.caojiantao.spider.RequestWrapper;
import cn.caojiantao.spider.ResponseWrapper;
import cn.caojiantao.spider.util.LogContext;
import cn.caojiantao.spider.util.NetUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @author caojiantao
 */
@Slf4j
@WebFilter(urlPatterns = {"/*"})
public class SpiderFilter implements Filter {

    private List<String> excludePathList = Arrays.asList("/", "/favicon.ico", "/index.html", "/css/*", "/js/*");

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if (excludePathList.contains(request.getRequestURI())) {
            filterChain.doFilter(request, response);
            return;
        }

        // 追踪日志
        LogContext.setTraceId();
        // 包装流,可重复读取
        RequestWrapper requestWrapper = new RequestWrapper(request);
        ResponseWrapper responseWrapper = new ResponseWrapper(response);
        // 请求参数
        String traceId = LogContext.getTraceId();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String data = new String(requestWrapper.toByteArray());
        String query = request.getQueryString();
        String ip = NetUtils.getIpAddress(request);

        log.info("request traceId:{} method:{} uri:{} data:{} query:{} ip:{}", traceId, method, uri, data, query, ip);

        long t = System.currentTimeMillis();
        filterChain.doFilter(requestWrapper, responseWrapper);
        // 响应参数
        String resp = new String(responseWrapper.toByteArray());
        long cost = System.currentTimeMillis() - t;

        log.info("response traceId:{} method:{} uri:{} data:{} query:{} ip:{} response:{} cost:{}", traceId, method, uri, data, query, ip, resp, cost);

        LogContext.clear();
    }
}
复制代码

Here LogContext tracking to log traceId management, implemented by ThreadLocal, easy to locate the problem.

package cn.caojiantao.spider.util;

import java.util.UUID;

/**
 * @author caojiantao
 */
public class LogContext {

    private static ThreadLocal<String> traceIdLocal = new ThreadLocal<>();

    public static void setTraceId() {
        String traceId = UUID.randomUUID().toString().replaceAll("-", "");
        setTraceId(traceId);
    }

    public static void setTraceId(String traceId) {
        traceIdLocal.set(traceId);
    }

    public static String getTraceId() {
        return traceIdLocal.get();
    }

    public static void clear() {
        traceIdLocal.remove();
    }
}
复制代码

Guess you like

Origin juejin.im/post/5e70364fe51d4526ca15e9a9