拦截器获取HttpServletRequest里body数据

2023-05-16

一、问题

通过在拦截器中获取request中的json数据,我们可以实现对参数进行校验和改写。问题是参数只能在拦截器里获取一次,往后在controller层就无法获取数据,提示body为空。

在网上查找资料后发现,request的输入流只能读取一次,那么这是为什么呢?

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

 

二、解决办法

HttpServletRequestWrapper是 httpServletRequest 的包装类

新建一个类继承HttpServletRequestWrapper实现对 httpServletRequest 的装饰,用来获取 body 数据

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {


    private final byte[] body;
    private String bodyStr;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String bodyString = getBodyString(request);
        body = bodyString.getBytes(Charset.forName("UTF-8"));
        bodyStr=bodyString;
    }

    public String getBodyStr() {
        return bodyStr;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

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

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

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }


    public  String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } 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();
    }
}

 

再新建一个 filter 实现对传入的 httpServletRequest 的转换

@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})
public class HttpServletRequestWrapperFilter 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) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            //遇到post方法才对request进行包装
            String methodType = httpRequest.getMethod();
            if ("POST".equals(methodType)) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(
                        (HttpServletRequest) request);
            }
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {

    }
}

 

最后在拦截器就可以获取request中body数据

 if(request instanceof  BodyReaderHttpServletRequestWrapper ){
            System.out.println(((BodyReaderHttpServletRequestWrapper) request).getBodyStr());
        }

经测试发现并不影响controller层获取body数据

 

为什么需要在 filter 里进行对 httpServletRequest 的包装转换,直接在拦截器里进行包装不行嘛?

过滤器(Filter)和拦截器(Interceptor)之间的最大区别就是,过滤器可以包装Request和Response,而拦截器并不能

用代码描述拦截器和过滤器的流程大概就是这样的:
拦截器:void run () {
    Request request = new Request();

    preHandle(request);

    service(request);
}

preHandler(Request request) {

    request = new RequestWrapper(request);  //在这里修改Request的引用,不会影响到service方法的request
}

过滤器void run () {
    Request request = new Request();

    doFilter(request);
}

doFilter(Request request) {

    request = new RequestWrapper(request);  //在这里修改Request的引用,会影响到service方法的request

    service(request);
}

 

转载于:https://my.oschina.net/u/3387320/blog/3094514

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

拦截器获取HttpServletRequest里body数据 的相关文章

随机推荐

  • openGauss数据库安装(2.0.0企业版安装)

    目录 1 准备环境2 预安装3 正式安装4 启动并登录数据库 前言 此次数据库的系统安装环境仍然是openEuler20 03LTS openGauss安装版本是2 0 0版本 xff0c 相对于极简版安装 xff0c 确实多了一些工具 x
  • openEuler22.03安装

    目录 1 安装2 登录3 修改登录密码输错限制次数 1 安装 如果在此时没有设置网络 xff0c 那么需要在登录后可以编辑 etc sysconfig network scripts ifcfg ens160文件 xff0c 如下红框部分所
  • Linux查看日志常用命令

    第一种 xff1a 查看实时变化的日志 比较吃内存 最常用的 xff1a tail f app log 默认最后10行 xff0c 相当于增加参数 n 10 tail 200f app log 最后200行 xff0c 某一时刻往前推 Ct
  • ubuntu查看文件和文件夹大小

    在实际使用ubuntu时候 xff0c 经常要碰到需要查看文件以及文件夹大小的情况 有时候 xff0c 自己创建压缩文件 xff0c 可以使用 ls hl 查看文件大小 参数 h 表示Human Readable xff0c 使用GB MB
  • NLTK下载错误的终极解决办法

    Downloading package brown to C Users Ken AppData Roaming nltk data Error downloading 39 brown 39 from lt https raw githu
  • Tensorboard 不显示数据的问题

    No dashboards are active for the current data set Probable causes You haven 39 t written any data to your event files Te
  • Pytorch学习(2)——训练词向量的代码

    教程 xff1a https www bilibili com video BV1vz4y1R7Mm p 61 2 span class token keyword import span torch span class token ke
  • Windows 在不修改主题色的情况下将标题栏修改为黑色

    有些软件使用深色模式之后标题栏仍然是白色的 xff0c 很不美观 如果在 Windows 10 的设置中 xff0c 将个性化 颜色 在以下区域显示主题色 标题栏和窗口边框 选中 xff0c 那么标题栏可以带颜色 此时如果将主题色改为彩色
  • 解决debian(jessie)没有声音的问题

    先检查系统声卡驱动 lspci grep Audio 00 1b 0 Audio device Intel Corporation 82801I ICH9 Family HD Audio Controller rev 03 说明系统已经识别
  • 笔记类软件总结

    我大致把笔记类软件分为三类 xff1a 传统文档 思维导图 专业软件 1 传统文档 Typora 最经典的本地软件应该是 Typora 支持 Markdown 的实时预览 xff0c 界面简洁美观 使用基于 Chromium 浏览器的 El
  • Golang Map 基本原理

    Go 语言中的 map 即哈希表 哈希表把元素分到多个桶里 xff0c 每个桶里最多放8个元素 在访问元素时 xff0c 首先用哈希算法根据 key 和哈希表种子获得哈希值 暂将其命名为 h xff0c 然后利用 h 的低 b b b 位得
  • Go 汇编器指南

    A Quick Guide to Go s Assembler Go汇编器指南 This document is a quick outline of the unusual form of assembly language used b
  • [golang] 什么情况下reflect.IsValid 返回 false?

    https stackoverflow com questions 39011295 when does reflect isvalid return false 总结成一句话 xff1a IsValid 表示是否 Value 是否 wra
  • IPv6的DNS,设置DNS

    来自下一代互联网国家工程中心的最新消息 xff0c 该中心正式宣布推出IPv6公共DNS xff1a 240c 6666 xff0c 这是面向全球免费提供的公共DNS服务 同时 xff0c 还有一个备用DNS xff1a 240c 6644
  • MongoDB 查询包含某字符串的记录

    34 key 34 regex 广东
  • Anaconda使用conda连接网络出现错误(CondaHTTPError: HTTP 000 CONNECTION FAILED for url)

    进入 HOMEPATH 目录 编辑其中的 condarc 文件 删除 default 将 https 改成 http 转载自 Anaconda使用conda连接网络出现错误 CondaHTTPError HTTP 000 CONNECTIO
  • Win10 EFI启动文件被删的修复办法

    首先确保EFI分区存在 没有的话可以进入PE创建 首先是不成功的办法 xff1a 用PE里的EFI分区修复 xff0c 成功把Win7变成了EFI启动 xff08 以前梦寐以求的 xff09 xff0c 但是Win10一直修复失败 xff0
  • 关于UITabBarController的UITabBar隐藏问题

    最开始的时候我用的 void hideTabBar if self tabBarController tabBar hidden 61 61 YES return UIView contentView if self tabBarContr
  • NSAttributedString宽高计算小技巧

    通常对于CoreText之类自己实现绘制的控件来说 xff0c 计算富文本的宽高其实需要依赖CTFramesetterSuggestFrameSizeWithConstraints这个方法 但有些时候 xff0c 我们可能只是使用UILab
  • 拦截器获取HttpServletRequest里body数据

    一 问题 通过在拦截器中获取request中的json数据 xff0c 我们可以实现对参数进行校验和改写 问题是参数只能在拦截器里获取一次 xff0c 往后在controller层就无法获取数据 xff0c 提示body为空 在网上查找资料