解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法

大家知道, StringMVC中@RequestBody是读取的流的方式, 如果在之前有读取过流后, 发现就没有了.

我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合法, 是否有篡改过值.

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    if (Boolean.valueOf(authentication)) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (BasicdataCommonsConstant.POST.equalsIgnoreCase(httpServletRequest.getMethod())) {
            // 防止流读取一次后就没有了, 所以需要将流继续写出去
            ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
            String body = HttpHelper.getBodyString(requestWrapper);
            if (StringUtils.isBlank(body)) {
                LOGGER.error("非法请求, 没有APP_KEY, APP_SECRET");
                OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                return;
            }
            Map<String, String> parameters = gson.fromJson(body, new TypeToken<Map<String, String>>() {
            }.getType());

            String APP_KEY = parameters.get("appKey");
            String APP_SECRET = parameters.get("appSecret");

            TAuthUser authUser = authUserMap.get(APP_KEY);
            if (authUser == null) {
                LOGGER.error("非法请求, 没有APP_KEY, APP_SECRET");
                OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                return;
            } else if (StringUtils.isBlank(APP_SECRET)) {
                LOGGER.error("非法请求, APP_SECRET为空, user={}", gson.toJson(authUser));
                OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_FROMTYPE_AND_KEY_CANNT_BE_NULL)));
                return;
            } else if (!APP_SECRET.equals(authUser.getAppSecret())) {
                LOGGER.error("非法请求: 没有APP_KEY, APP_SECRET不匹配. user={}, password={}, name={}", APP_KEY, APP_SECRET, authUser.getName());
                OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_KEY)));
                return;
            }
            String SIGNATURE = parameters.get("signature");
            // 对参数进行签名
            String md5 = SignatureUtil.decryptSignature(parameters);
            if (!md5.equals(SIGNATURE)) {
                LOGGER.error("非法请求, signature ={}", SIGNATURE);
                OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_SIGNATURE_ERROR)));
                return;
            }
            threadLocalUser.setAuthUser(authUser);
            chain.doFilter(requestWrapper, response);
        }
    }
    chain.doFilter(request, response);
}

流只能读一次, 读了就没有了, 为了后面的代码还能够取得流, 我们应该还需要将其写出去才行.

所以, 我新建立了一个类. 看代码:

import javax.servlet.ReadListener;  
import javax.servlet.ServletInputStream;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletRequestWrapper;  
import java.io.BufferedReader;  
import java.io.ByteArrayInputStream;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.nio.charset.Charset;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/31/14
 * Time: 8:49 PM
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("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 int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}
import javax.servlet.ServletRequest;  
import javax.servlet.http.HttpServletRequest;  
import java.io.BufferedInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.io.InputStream;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/24/14
 * Time: 10:39 AM
 */
public class HttpHelper {

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

}

请注意这里的编码, 最好将其转换成UTF-8的编码格式, 不然你获取到的中文则会使乱码的. 我自己也习惯于UTF-8的编码.

这样子就应该差不多了哦~