web应用程序中解决Request和Response只能获取一次的问题

由于Request和Response是用流的方式传递数据,所以只能读取一次。tomcat中已有SavedRequest类,没有SavedResponse类,我们创建两个容器类来装载Request/Response->写一个过滤器Filter拦截请求将Info装载入容器中。

RequestWrapper:

 
 
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.longshine.luxicrmboot.commons.utils.ApplicationUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.HtmlUtils;
 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
 
 
/**
 * Request包装类
 * <p>
 * 1.预防xss攻击
 * 2.拓展requestbody无限获取(HttpServletRequestWrapper只能获取一次)
 * </p>
 *
 * @author Caratacus
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
 
    /**
     * 存储requestBody byte[]
     */
    private final byte[] body;
 
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        byte[] body = new byte[0];
        try {
            body = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (IOException e) {
            log.error("Error: Get RequestBody byte[] fail," + e);
        }
        this.body = body;
    }
 
    @Override
    public BufferedReader getReader() {
        ServletInputStream inputStream = getInputStream();
        return Objects.isNull(inputStream) ? null : new BufferedReader(new InputStreamReader(inputStream));
    }
 
    @Override
    public ServletInputStream getInputStream() {
        if (ObjectUtils.isEmpty(body)) {
            return null;
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            @SuppressWarnings("EmptyMethod")
            public void setReadListener(ReadListener readListener) {
 
            }
 
            @Override
            public int read() {
                return bais.read();
            }
        };
    }
 
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = htmlEscape(values[i]);
        }
        return encodedValues;
    }
 
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value == null) {
            return null;
        }
        return htmlEscape(value);
    }
 
    @Override
    public Object getAttribute(String name) {
        Object value = super.getAttribute(name);
        if (value instanceof String) {
            htmlEscape((String) value);
        }
        return value;
    }
 
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null) {
            return null;
        }
        return htmlEscape(value);
    }
 
    @Override
    public String getQueryString() {
        String value = super.getQueryString();
        if (value == null) {
            return null;
        }
        return htmlEscape(value);
    }
 
    /**
     * 使用spring HtmlUtils 转义html标签达到预防xss攻击效果
     *
     * @param str
     * @see org.springframework.web.util.HtmlUtils#htmlEscape
     */
    protected String htmlEscape(String str) {
        return HtmlUtils.htmlEscape(str);
    }
 
}

ResponseWrapper:

 
import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.longshine.luxicrmboot.commons.msg.AjaxResult;
import com.longshine.luxicrmboot.commons.msg.ErrorCode;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.MimeTypeUtils;
 
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
 
/**
 * response包装类
 *
 * @author Caratacus
 */
@Slf4j
public class ResponseWrapper extends HttpServletResponseWrapper {
 
    private ErrorCode errorcode;
 
    public ResponseWrapper(HttpServletResponse response) {
        super(response);
    }
 
    public ResponseWrapper(HttpServletResponse response, ErrorCode errorcode) {
        super(response);
        setErrorCode(errorcode);
    }
 
    /**
     * 获取ErrorCode
     *
     * @return
     */
    public ErrorCode getErrorCode() {
        return errorcode;
    }
 
    /**
     * 设置ErrorCode
     *
     * @param errorCode
     */
    public void setErrorCode(ErrorCode errorCode) {
        if (Objects.nonNull(errorCode)) {
            this.errorcode = errorCode;
            super.setStatus(this.errorcode.getHttpCode());
        }
    }
 
    /**
     * 向外输出错误信息
     *
     * @param e
     * @throws IOException
     */
    public void writerErrorMsg(Exception e) {
        if (Objects.isNull(errorcode)) {
            log.warn("Warn: ErrorCodeEnum cannot be null, Skip the implementation of the method.");
            return;
        }
        printWriterApiResponses(AjaxResult.failure(this.getErrorCode(), e));
    }
 
    /**
     * 设置ApiErrorMsg
     */
    public void writerErrorMsg() {
        writerErrorMsg(null);
    }
 
    /**
     * 向外输出AjaxResult
     *
     * @param ajaxResult
     */
    public void printWriterApiResponses(AjaxResult ajaxResult) {
        writeValueAsJson(ajaxResult);
    }
 
    /**
     * 向外输出json对象
     *
     * @param obj
     */
    public void writeValueAsJson(Object obj) {
        if (super.isCommitted()) {
            log.warn("Warn: Response isCommitted, Skip the implementation of the method.");
            return;
        }
        super.setContentType(MimeTypeUtils.APPLICATION_JSON_VALUE);
        super.setCharacterEncoding(StandardCharsets.UTF_8.name());
        try (PrintWriter writer = super.getWriter()) {
            writer.print(JSON.toJSONString(obj));
            writer.flush();
        } catch (IOException e) {
            log.warn("Error: Response printJson faild, stackTrace: {}", Throwables.getStackTraceAsString(e));
        }
    }
 
}

过滤器:

 
import com.longshine.luxicrmboot.commons.wrapper.RequestWrapper;
import org.springframework.stereotype.Component;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/**
 * 记住Request/Response 过滤器
 * 解决Request/Response不能重复使用问题
 *
 * @author Caratacus
 */
@Component
@WebFilter(filterName = "crownFilter", urlPatterns = "/*")
public class MemoryReqResFilter implements Filter {
 
    @Override
    @SuppressWarnings("EmptyMethod")
    public void destroy() {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse res,
                         FilterChain chain) throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;
        chain.doFilter(new RequestWrapper(req), res);
    }
 
    @Override
    @SuppressWarnings("EmptyMethod")
    public void init(FilterConfig config) {
    }
 
}