继承HttpServletRequestWrapper实现HttpServletRequest复用

概述

HttpServletRequestWrapper提供了许多处理HttpServletRequest的方法,通常情况下,程序调用HttpServletRequest之后,该request无法被再次调用。

场景

在有些场景下,需要多次调用request中的参数值,例如身份验证。将多个校验参数放入请求体中,自定义验证码过滤器CaptchaFilter和密码过滤器PwdFilter放入FilterChain中。当request被CaptchaFilter获取并得到验证码校验通过后,调用filterChain.doFilter(request, response)放行,当请求进入PwdFilter时,该过滤器期望获取到请求体中的用户名和密码,但是此时request通过getReader()读取后,结果为空,后续处理中可能产生NullPointerException。

本文通过继承HttpServletRequestWrapper,重写部分方法,自定义包装类实现request复用。

在使用本文方法之前,要确保request在此之前没有被任何方法使用(即调用过getInputStream()方法)

实现

首先,继承HttpServletRequestWrapper,读取一次request并缓存。


public class CustomizeServletRequestWrapper extends HttpServletRequestWrapper {

    private final ServletInputStream inputStream;

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public CustomizeServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 读取request并缓存
        byte[] body = IOUtils.toByteArray(request.getInputStream());
        this.inputStream = new RequestCachingInputStream(body);
    }

    //代理ServletInputStream
    private static class RequestCachingInputStream extends ServletInputStream {

        private final ByteArrayInputStream is;

        public RequestCachingInputStream(byte[] bytes) {
            this.is = new ByteArrayInputStream(bytes);
        }

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

        @Override
        public boolean isFinished() {
            return this.is.available() == 0;
        }

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

        @Override
        public void setReadListener(ReadListener readlistener) {
        }

    }
}

缓存request数据后,重写HttpServletRequestWrapper的getInputStream()和getReader()方法:


private BufferedReader reader;

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (this.inputStream != null) {
            return this.inputStream;
        }
        return super.getInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (this.reader == null) {
            this.reader = new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
        }
        return this.reader;
    }
    

在类中添加获取请求体参数的方法:


   private String requestBodyLine;   
   
   /**
     * 将请求体参数存入Map中
     * @return
     * @throws IOException
     */
    public Map<String, Object> getRequestBodyToMap() throws IOException {
        String jsonInfo = this.getRequestBodyToStr();
        return JsonUtils.parseMap(jsonInfo);
    }
    
    public String getRequestBodyToStr() throws IOException {
        if (this.requestBodyLine == null) {
            BufferedReader reader = this.getReader();
            StringBuilder builder = new StringBuilder();
            String line = reader.readLine();
            while (line != null) {
                builder.append(line);
                line = reader.readLine();
            }
            requestBodyLine = builder.toString();
            reader.close();
        }
        return requestBodyLine;
    }
    

完整的装饰类:


import com.jingzheng.common.utils.json.JsonUtils;
import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;

public class CustomizeServletRequestWrapper extends HttpServletRequestWrapper {

    private final ServletInputStream inputStream;

    private BufferedReader reader;

    private String requestBodyLine;

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public CustomizeServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //读一次 然后缓存 实现同一request多次读取
        byte[] body = IOUtils.toByteArray(request.getInputStream());
        this.inputStream = new RequestCachingInputStream(body);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (this.inputStream != null) {
            return this.inputStream;
        }
        return super.getInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (this.reader == null) {
            this.reader = new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
        }
        return this.reader;
    }

    /**
     * 将请求体参数存入Map中
     * @return
     * @throws IOException
     */
    public Map<String, Object> getRequestBodyToMap() throws IOException {
        String jsonInfo = this.getRequestBodyToStr();
        return JsonUtils.parseMap(jsonInfo);
    }

    public String getRequestBodyToStr() throws IOException {
        if (this.requestBodyLine == null) {
            BufferedReader reader = this.getReader();
            StringBuilder builder = new StringBuilder();
            String line = reader.readLine();
            while (line != null) {
                builder.append(line);
                line = reader.readLine();
            }
            requestBodyLine = builder.toString();
            reader.close();
        }
        return requestBodyLine;
    }

    //代理ServletInputStream 内容为当前缓存的bytes
    private static class RequestCachingInputStream extends ServletInputStream {

        private final ByteArrayInputStream is;

        public RequestCachingInputStream(byte[] bytes) {
            this.is = new ByteArrayInputStream(bytes);
        }

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

        @Override
        public boolean isFinished() {
            return this.is.available() == 0;
        }

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

        @Override
        public void setReadListener(ReadListener readlistener) {
        }
    }
}

实例调用


public void requestWrapperDemo(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    // 复用request
    RepeatableServletRequestWrapper requestWrapper;
    requestWrapper = new RepeatableServletRequestWrapper(request);

    // 获取请求参数
    Map<String, Object> requestBodyToMap = requestWrapper.getRequestBodyToMap();

    // 不直接将request传入过滤器链中,而是将requestWrapper传入
    filterChain.doFilter(requestWrapper, response);
}

原创不易,转载、引用请标明出处。
如有帮助,欢迎点赞!!!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐