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


    错误如图所示,提示流已经关闭,因为如果通过过滤拦截器读取流的话,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项目相关。

     



  • 相关阅读:
    阅读<SQL语言艺术>实践五
    <SQL语言艺术>阅读计划
    文本类文件与VS关联实践
    接口开发原则
    逻辑部分开发原则
    <海量数据库解决方案>2011022301
    5800对于存储卡密码设置问题
    [转]Delphi用户登录窗口框架
    20世纪科学界最重要的12本书
    [转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching)
  • 原文地址:https://www.cnblogs.com/tnt-33/p/11052295.html
Copyright © 2020-2023  润新知