• 过滤器或拦截器读取请求中流的数据后导致后续控制器读取报错问题分析


    错误如图所示,提示流已经关闭,因为如果通过过滤拦截器读取流的话,IO流关闭只能读取一次, 即使不关闭的话,流有个read标志位,后续控制器会从read标志位开始读,读过流之后就读取不到数据了,除非利用void reset()方法,把pos位置位开始,重新读,但是不是任何流都可以使用,所以我们写通用的方法时候,读取完流中数据之后,需要进行包装request,将流重新写入,供后续控制器读取。

    网络上解释如下:那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。

    包装request请求代码如下:

    public class MyRequest extends HttpServletRequestWrapper {

    private String body="";
    public MyRequest(HttpServletRequest request) throws IOException {
    super(request);
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = null;
    try {
    InputStream inputStream = request.getInputStream();
    if (inputStream != null) {
    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    char[] charBuffer = new char[128];
    int bytesRead = -1;
    while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
    stringBuilder.append(charBuffer, 0, bytesRead);
    }
    } else {
    stringBuilder.append("");
    }
    } catch (IOException ex) {
    throw ex;
    } finally {
    if (bufferedReader != null) {
    try {
    bufferedReader.close();
    } catch (IOException ex) {
    throw ex;
    }
    }
    }
    body = stringBuilder.toString();
    }

    public String getBody(){
    return this.body;
    }
    @Override
    public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

    final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());

    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) {

    }
    };
    }
    }
    过滤器处理如下图:

     

    拦截器处理如下:

    Controller层正常获取post参数即可,利用requestBody等等。

    PS:在springcloud项目中,拦截器不能正常从request中读取流,暂未发现是我自己的例子问题还是跟cloud项目相关。

     



  • 相关阅读:
    uva11059
    uva725
    程序中double类型的数输出为什么要用lf
    c++形参和实参同名时,如何单步执行观察形参的变化。
    台式机的字母键和数字键都不能正常使用了呢?
    找错误——下面的程序意图在于统计字符串中字符数1的个数,可惜有瑕疵
    初学者常见错误1——赋值时的类型转换
    scanf
    c++的调试与运行
    黑猫派对
  • 原文地址:https://www.cnblogs.com/tnt-33/p/11052295.html
Copyright © 2020-2023  润新知