今天在开发的时候遇到一个问题,在interceptor拦截器中读取流request.getInputStream,然后接口报错,没有传递实体类参数,经过一番排查发现:request.getInputStream只能调用一次,当在拦截器中获取流之后,流的下次读取位置发生了改变,而且无法调用reset方法,调用reset会报错。

解决思路:

写一个过滤器,将request进行包装,然后在过滤器中将包装类传递到拦截器中,这样拦截器读取request的包装类,就不会报错。 过滤器和拦截器的执行链路:过滤前-拦截前-Action处理-拦截后-过滤后

以下实现代码:

request包装类:

import org.apache.commons.lang.StringUtils;

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.Charset;


/**
 * @Author: BillYu
 * @Description: 解决request getInputStream 流只能读一次的问题
 * 如果拦截器中进行直接读取,接口无法转会流和实体 就会报错
 * Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: java.lang.String com.iflytek.cloudbaseserver.controller.FileController.testContent(com.iflytek.cloudbaseserver.model.user.User)
 * @Date: Created in 16:12 2019-11-21.
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public RequestWrapper(HttpServletRequest request)
            throws IOException {
        super(request);
        body = readBytes(request.getReader(), "utf-8");
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {
        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 listener) {

            }

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

    /**
     * 通过BufferedReader和字符编码集转换成byte数组
     * @param br
     * @param encoding
     * @return
     * @throws IOException
     */
    private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
        String str = null,retStr="";
        while ((str = br.readLine()) != null) {
            retStr += str;
        }
        if (StringUtils.isNotBlank(retStr)) {
            return retStr.getBytes(Charset.forName(encoding));
        }
        return null;
    }

    public byte[] getBody() {
        return body;
    }
}

过滤器:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Author: BillYu
 * @Description:
 * @Date: Created in 17:38 2019-11-21.
 */
public class RepeatReadFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new RequestWrapper((HttpServletRequest) request);
        }
        if (null == requestWrapper) {
            System.out.println(1);
            chain.doFilter(request, response);
        } else {
            System.out.println(2);
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {

    }
}

使过滤器生效:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author: BillYu
 * @Description:
 * @Date: Created in 14:28 2019-11-21.
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private SignInterceptor signInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signInterceptor)
                .addPathPatterns("/file/**");
//                .excludePathPatterns("/qrcode/**","/productdefect/getList",
//                        "/base/erpProductArchives/fuzzyQuery","/produce/interactionPoint/getByIpAndType");
    }

    @Bean
    public FilterRegistrationBean repeatedlyReadFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        RepeatReadFilter repeatedlyReadFilter = new RepeatReadFilter();
        registration.setFilter(repeatedlyReadFilter);
        registration.addUrlPatterns("/file/*");
        return registration;
    }
}

拦截器中获取流:

import com.iflytek.cloudbaseserver.util.GsonUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import sun.misc.BASE64Encoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.util.Map;

/**
 * @Author: BillYu
 * @Description:
 * @Date: Created in 13:48 2019-11-21.
 */
@Component
public class SignInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        int contentLength = request.getContentLength();
        System.out.println("contentLength:"+contentLength);
        String contentType = request.getContentType();
        System.out.println("contentType:"+contentType);
        String method = request.getMethod();
        System.out.println("method:"+method);
        String uri = request.getRequestURI();
        System.out.println("uri:"+uri);
        Map map = request.getParameterMap();
        System.out.println("parameterMap:"+GsonUtil.toJson(map));
        if (request instanceof StandardMultipartHttpServletRequest) {
        // 拿到封装的RequestWrapper
            request = ((StandardMultipartHttpServletRequest) request).getRequest();
        }
        if (request instanceof RequestWrapper) {
            System.out.println("===>in interceptor");
            // 签名处理过程 start....
            RequestWrapper requestWrapper = (RequestWrapper) request;
            byte[] buffer = requestWrapper.getBody();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            BASE64Encoder base64en = new BASE64Encoder();
            byte[] digest = md5.digest(buffer);
            //利用BASE64Encoder编码加密
            String md5Str = base64en.encode(digest);
            System.out.println("md5Content:"+md5Str);
            System.out.println("md5ContentLength:"+md5Str.length());
        }


        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}