多次读取request中的参数值

Scroll Down

背景

背景:最近项目开发中遇到一个问题,在一个spring boot接口项目中需要对一个filter改造。在filter中使用request用流的方式读取过获取参数后,后续的controller中使用@RequestBody读取参数报错,通过阅读源码发现@RequestBody是读取的流的方式, 如果在之前有读取过流后, 后续就读取不到了。随设计解决方法为:在filter读取参数时,HttpServletRequestWrapper重写Request请求参数,将原始的参数流保存起来,待filter处理完成后将该流放入doFilter方法。

1.自定义类继承HttpServletRequestWrapper

@Slf4j
public class AuthRequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    public AuthRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.body = HttpRequestUtils.getBody();
    }

    public String getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream()  {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read(){
                return bais.read();
            }
        };
    }
    @Override
    public BufferedReader getReader(){
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

2.request参数处理工具

@Slf4j
public class HttpRequestUtils {

    /**
     * 原始的请求body
     */
    private static String body;
    /**
     * 通用请求格式转换
     * @param httpServletRequest
     * @return
     */
    public static Map<String, Object> commonHttpRequestParamConvert(HttpServletRequest httpServletRequest) {
        Map<String, Object> params = new HashMap<>();
        try {
            Map<String, String[]> requestParams = httpServletRequest.getParameterMap();
            if (requestParams != null && !requestParams.isEmpty()) {
                requestParams.forEach((key, value) -> params.put(key, value[0]));
            } else {
                StringBuilder paramSb = new StringBuilder();
                try {
                    String str = "";
                    BufferedReader br = httpServletRequest.getReader();
                    while((str = br.readLine()) != null){
                        paramSb.append(str);
                    }
                } catch (Exception e) {
                    log.error("httpServletRequest 获取requestBody发生异常:" + e);
                }
                if (paramSb.length() > 0) {
                    JSONObject paramJsonObject = JSON.parseObject(paramSb.toString());
                    if (paramJsonObject != null && !paramJsonObject.isEmpty()) {
                        paramJsonObject.forEach((key, value) -> params.put(key, value));
                    }
                }
                //将原始的请求体内容保存起来
                setBody(paramSb.toString());
            }
        } catch (Exception e) {
            log.error("commonHttpRequestParamConvert转换发生异常:" + e);
        }
        return params;
    }

    public static void setBody(final String body){
        HttpRequestUtils.body = body;
    }
    public static String getBody(){
        return HttpRequestUtils.body;
    }
}

使用FastJSON将参数解析成Map<String,Object>,后续可直接对该参数map进行其他操作。

3.filter中使用

 @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
	//获取请求中参数
    Map<String, Object> paramMap = HttpRequestUtils.commonHttpRequestParamConvert(request);
    JSONObject params = new JSONObject(paramMap);
	//对请求参数params处理。。。
	...
	//取Body数据,将原始request中参数带回
    AuthRequestWrapper requestWrapper = new AuthRequestWrapper(request);
    filterChain.doFilter(requestWrapper != null ? requestWrapper : request, response);
}

4.总结

    项目中使用过滤器处理通用参数后,controller再 处理业务接口参数时非常常见的业务,通过这套逻辑基本可以优雅实现一整套的需要。