HttpServletRequest重复读取

发布于 2019-12-22  5.04k 次阅读


 

背景

由于项目需要用到拦截器验证用户的Token,但是实际结果如下:

HttpServletRequest重复读取

拦截器读取HttpServletRequest输入流之后Controller再次读取就获取不到输入流了。

原因

HttpServletRequest提供了getInputStream()的方法来获取一个输入流(InputStream对象)。InputStream内部的read()函数返回值说明:

HttpServletRequest重复读取

就是说有一个下标position记录了当前读取到的位置,如果一个输入流被读取完后position会被置为-1然后返回。然后还提供了一个reset()方法从注释中可以知道这个方法可以重置position的位置。

HttpServletRequest重复读取

但是需要markSupported()方法返回true才可以。然而在InputStream内并没有真正实现rest()方法,markSupported()方法返回的值也是为false。这就导致了HttpServletRequest输入流只能读取一次。

HttpServletRequest重复读取

解决方案

我们可以重写HttpServletRequestWrapper,然后添加过滤器解决。

BodyReaderHttpServletRequestWrapper.java

package com.demo.demo.config.Filter;

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

/**
 * @Classname BodyReaderHttpServletRequestWrapper
 * @Description TODO
 * @Date 2019/12/19 16:11
 * @Created by Alienworm
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body; // 报文

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = InputStreamToByte(request.getInputStream());
    }

    private byte[] InputStreamToByte(InputStream is) throws IOException {
        ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int ch;
        while ((ch = is.read(buffer)) != -1)
            bytestream.write(buffer, 0, ch);
        byte[] data = bytestream.toByteArray();
        bytestream.close();
        return data;
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        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(); }
        };
    }
}


BodyReaderHttpServletRequestFilter.java

package com.demo.demo.config.Filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;

/**
 * @Classname BodyReaderHttpServletRequestFilter
 * @Description TODO
 * @Date 2019/12/19 16:11
 * @Created by Alienworm
 */
@WebFilter(filterName="bodyReaderHttpServletRequestFilter", urlPatterns="/*")
public final class BodyReaderHttpServletRequestFilter extends HttpFilter {

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        InputStream inputStream = request.getInputStream();
        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest)request);
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void init(FilterConfig filterConfig) { }
}

在SpringBoot启动类里添加过滤器

DemoApplication.java

package com.demo.demo;

import com.demo.demo.config.Filter.BodyReaderHttpServletRequestFilter;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@MapperScan("com.demo.demo.mapper")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean Filters() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new BodyReaderHttpServletRequestFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("BodyReaderHttpServletRequestFilter");
        return registrationBean;
    }
}

最后把HttpServletRequest转换成JSONObject

RequestUtil.java

package com.demo.demo.util;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.*;
import java.util.Enumeration;
import java.util.stream.Stream;

/**
 * @Classname RequestUtil
 * @Description TODO
 * @Date 2019/12/15 00:53
 * @Created by Alienworm
 */
@Slf4j
@SuppressWarnings("ALL")
public class RequestUtil {

    // 客户端请求
    private static HttpServletRequest httpServletRequest;

    /*
     * @Author Alienworm
     * @Description 将HttpServletRequest转换为JSONObject
     * @Date 23:13 2019/12/18
     * @Param [request]
     * @return com.alibaba.fastjson.JSONObject
     **/
    public static JSONObject toJSONObject(HttpServletRequest request) {
        httpServletRequest = request;
        log.info("Get parameter url: " + request.getRequestURL());
        JSONObject pathParameter = getParameterFromPath();
        log.info("Path parameter: " + pathParameter.toJSONString());
        JSONObject bodyParameter = getParameterFromBody();
        log.info("Body parameter: " + bodyParameter.toJSONString());
        return combineJSONObject(bodyParameter, pathParameter);
    }

    /*
     * @Author Alienworm
     * @Description 合并两个JSONObject
     * @Date 23:13 2019/12/18
     * @Param [jsonObject1, jsonObject2]
     * @return com.alibaba.fastjson.JSONObject
     **/
    private static JSONObject combineJSONObject(JSONObject jsonObject1, JSONObject jsonObject2) {
        for (String key : jsonObject2.keySet())
            jsonObject1.put(key, jsonObject2.get(key));
        return jsonObject1;
    }

    /*
     * @Author Alienworm
     * @Description 获取GET参数
     * @Date 23:13 2019/12/18
     * @Param []
     * @return com.alibaba.fastjson.JSONObject
     **/
    private static JSONObject getParameterFromPath() {
        Enumeration parameterNames = httpServletRequest.getParameterNames();
        JSONObject pathParameter = new JSONObject();
        while (parameterNames.hasMoreElements()) {
            String parameterName = (String)parameterNames.nextElement();
            Object parameter = httpServletRequest.getParameter(parameterName);
            pathParameter.put(parameterName, parameter);
        }
        return pathParameter;
    }

    /*
     * @Author Alienworm
     * @Description 获取POST参数
     * @Date 23:14 2019/12/18
     * @Param []
     * @return com.alibaba.fastjson.JSONObject
     **/
    private static JSONObject getParameterFromBody() {
        JSONObject bodyParameter = new JSONObject();
        try {
            String bodyParameterString = "";
            Stream stringStream = httpServletRequest.getReader().lines();
            for (Object line : stringStream.toArray())
                bodyParameterString += line.toString();
            JSONObject tmp = JSONObject.parseObject(bodyParameterString);
            bodyParameter = tmp != null ? tmp : bodyParameter;
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return bodyParameter;
    }
}