SpringBoot如何配置获取request中body的json格式参数

目录
  • 背景
  • 获取请求中的参数(非json格式参数)
    • 获取方法
    • 结论
    • 获取POST请求json格式的参数
    • 经过检索推荐方法(参看后边完整方法)
    • 实现方法
    • 使用

背景

最近开发项目,因为有第三方调用我们的接口,我们使用SpringBoot以JavaBean的方式接收了我们预期的参数,参数接收也没有什么异常。但是有一些需求问题需要沟通,需要拿到合作第三方传入的所有参数,来进行参数核验。

如何拿到请求的所有参数呢?正常的思路肯定是从request中获取,如果是GET请求,参数在请求路径中拼接;如果是POST请求,参数在request的请求体(body)中。

一番检索,很容易拿到相关代码。但是经过实操,发现并不能如期获取到参数。经过思考,我的接口是POST请求,参数形式是json格式(使用了@RequestBody来修饰参数)。

具体过程参看如下分析

获取请求中的参数(非json格式参数)

获取方法

方法一

Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
    String paraName = parameterNames.nextElement();
    System.out.println(paraName + ": " + request.getParameter(paraName));
}

方法二

Map<String, String[]> map = request.getParameterMap();
Set<Map.Entry<String, String[]>> keSet = map.entrySet();
for (Iterator<Map.Entry<String, String[]>> itr = keSet.iterator(); itr.hasNext(); ) {
    Map.Entry<String, String[]> me = itr.next();
    Object ok = me.getKey();
    Object ov = me.getValue();
    String[] value = new String[1];
    if (ov != null) {
        value = (String[]) ov;
    } else {
        value[0] = ov.toString();
    }
    for (int k = 0; k < value.length; k++) {
        System.out.println(ok + "=" + value[k]);
    }
}

结论

经过测试以上两个方法可以获取GET请求的参数,以及参数格式为form-data、x-www-form-urlencoded的POST请求,但是json格式参数(postman中为raw)的参数不能获得

根据代码的简介程度,选择方法一,明显更舒服一些。

以上结论经过postman实测.

获取POST请求json格式的参数

以上方法已经可以获取大多数情况下的请求的参数,但是明显还不能满足需求,需要获取传入json格式的参数。

经过检索推荐方法(参看后边完整方法)

经过一番检索,网上推荐的方法一般都是使用流来进行参数读取,即使用getInputStream()和getReader():

Map<String, Object> params = new HashMap<>();
BufferedReader br = null;
try {
    try {
        br = request.getReader();
    } catch (IOException e) {
        e.printStackTrace();
    }
    String str;
    StringBuilder wholeStr = new StringBuilder();
    while ((str = Objects.requireNonNull(br).readLine()) != null) {
        wholeStr.append(str);
    }
    if (StrUtil.isNotEmpty(wholeStr.toString())) {
        params = JSON.parseObject(wholeStr.toString(), Map.class);
    }
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println(params);

遇到的问题及解决思路

问题1 流不能多次被调用

但是又会遇到如下问题:

ERROR m.e.handler.GlobalExceptionHandler - getInputStream() has already been called for this request
java.lang.IllegalStateException: getInputStream() has already been called for this request
    at org.apache.catalina.connector.Request.getReader(Request.java:1212)
    at org.apache.catalina.connector.RequestFacade.getReader(RequestFacade.java:504)

根据报错信息分析简单来说,就是getInputStream()已经被调用了,不能再次调用。可是我看代码上,我也没调用。经过一番检索,原来@RequestBody注解配置后,默认会使用流来读取数据。

具体原因:

  • 默认配置时,getInputStream()和getReader()一起使用会报错,使用两遍getInputStream(),第二遍会为空。
  • 当存在@RequestBody等注解时,springMVC已读取过一遍流,默认单独使用getInputStream()或getReader()都为空。

实测,不加@RequestBody注解,可以如期获得请求中的json参数,但是又不得不加@RequestBody注解。这样就需要新的思路

解决思路

写filter继承HttpServletRequestWrapper,缓存InputStream,覆盖getInputStream()和getReader()方法,使用ByteArrayInputStream is = new ByteArrayInputStream(body.getBytes());读取InputStream。

实现方法

1.定义增强类,继承继承HttpServletRequestWrapper

将请求体中的流copy一份,覆写getInputStream()和getReader()方法供外部使用。每次调用覆写后的getInputStream()方法都是从复制出来的二进制数组中进行获取,这个二进制数组在对象存在期间一直存在,这样就实现了流的重复读取。

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(StandardCharsets.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) {
            }
        };
    }
    public void setInputStream(byte[] body) {
        this.body = body;
    }
}

2.构建过滤器

@Slf4j
@WebFilter(filterName = "RequestWrapperFilter", urlPatterns = "/api/test/test2")
public class RequestWrapperFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException
            , IOException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if (null == requestWrapper) {
            log.error("过滤器包装request失败!将返回原来的request");
            chain.doFilter(request, response);
        } else {
            log.info("过滤器包装request成功");
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy() {
    }
}

注意事项:

  • 使用@WebFilter注解的urlPatterns属性,可指定过滤哪一个接口方法。
  • 过滤器类最好不要交由Spring管理,否则每一个接口都会进行过滤(实测)。例如:不要添加@Component注解. 3.编写工具类方便调用
/**
 * description:http辅助工具类
 *
 * @author RenShiWei
 * Date: 2021/5/7 22:11
 **/
public class HttpHelper {
    /**
     * description:从request获取body的json数据
     *
     * @param request /
     * @return /
     * @author RenShiWei
     * Date: 2021/5/7 22:44
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        ServletInputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.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();
    }
    /**
     * description:从request获取body的json数据,并格式化成map形式
     *
     * @param request /
     * @return /
     * @author RenShiWei
     * Date: 2021/5/7 22:44
     */
    @SuppressWarnings("all")
    public static Map<String, Object> getBodyMap(ServletRequest request) {
        Map<String, Object> params = new HashMap<>();
        String bodyString = getBodyString(request);
        if (StrUtil.isNotEmpty(bodyString)) {
            params = JSON.parseObject(bodyString, Map.class);
        }
        return params;
    }
}

4.在SpringBoot启动类上添加@ServletComponentScan注解

使用

@RestController
@RequestMapping("/api/test")
public class TestController {
    @GetMapping("/test1")
    @AnonymousAccess
    public ResponseEntity<String> test1(HttpServletRequest request) {
        System.out.println("---GET请求 getParameterNames 入参---");
        try {
            request.setCharacterEncoding("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paraName = parameterNames.nextElement();
            System.out.println(paraName + ": " + request.getParameter(paraName));
        }
        System.out.println("---GET请求 getParameterMap 入参---");
        Map<String, String[]> map = request.getParameterMap();
        Set<Map.Entry<String, String[]>> keSet = map.entrySet();
        for (Iterator<Map.Entry<String, String[]>> itr = keSet.iterator(); itr.hasNext(); ) {
            Map.Entry<String, String[]> me = itr.next();
            Object ok = me.getKey();
            Object ov = me.getValue();
            String[] value = new String[1];
            if (ov != null) {
                value = (String[]) ov;
            } else {
                value[0] = ov.toString();
            }
            for (int k = 0; k < value.length; k++) {
                System.out.println(ok + "=" + value[k]);
            }
        }
        return ResponseEntity.ok(null);
    }
    @PostMapping("/test2")
    @AnonymousAccess
    public ResponseEntity<String> test2(@RequestBody TestParam testParam, HttpServletRequest request) {
        String body = HttpHelper.getBodyString(request);
        Map<String, Object> bodyMap = HttpHelper.getBodyMap(request);
        System.out.println("body: " + body);
        System.out.println("bodyMap: " + bodyMap);
        return ResponseEntity.ok(null);
    }
}

postman测试

GET请求

POST的JSON格式参数(其他方式结果与GET请求结果一致)

结果

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot自动配置的原理及@Conditional条件注解

    目录 1 @SpringBootApplication自动配置原理 2 @Conditional系列条件注解 1 @SpringBootApplication自动配置原理 @SpringBootApplication是一个组合注解,主要由@ComponentScan.@SpringBootConfiguration.@EnableAutoConfiguration这三个注解组成.@EnableAutoConfiguration是Spring Boot实现自动配置的关键注解. @Component

  • Spring Boot详解配置文件的用途与用法

    目录 1. SpringBoot 配置文件 1.1 配置文件的作用 1.2 配置文件的格式 1.3 properties 配置文件说明 1.3.1 properties 基本语法 1.3.2 读取配置文件 1.4 yml 配置文件说明 1.4.1 yml 基本语法 1.4.2 yml 使用进阶 1.4.3 配置对象 1.4.4 配置集合 1.4.5 yml的另一种写法(行内写法) 1.5 properties 和 yml 比较 2. 读取 SpringBoot 配置文件的方法 2.1 使用 @V

  • SpringBoot如何使用applicationContext.xml配置文件

    目录 使用applicationContext.xml配置文件 applicationContext 加载配置文件 案例 多文件的加载方法 使用applicationContext.xml配置文件 SpringBoot默认是通过Java代码进行依赖注入,但也为xml形式的依赖注入提供了入口,就是@ImportResource注解. 我们可以在SpringBoot的启动类上添加这个注解并在注解的locations属性中指定xml配置文件.(可以使用一个文件集合也可以只引入主配置文件然后在主配置文件

  • Spring深入了解常用配置应用

    目录 常用配置 一.别名 二.bean 的配置 三.import 存在问题 总结 常用配置 现在这里简单了解一下spring 配置文件中的一些常用配置,在后面我们还会遇到更多的配置,在后文继续进行介绍了. spring中的配置一共也就这几个 description描述不太重要, bean在之前已经见识过了, alias给bean起别名, import在当前xml文件中导入其他xml文件 一.别名 在spring中别名主要是给bean的id起一个别名,同样也有好几种方式. 1.alias 配置 <

  • SpringBoot多环境配置教程详解

    目录 一.为什么要配置多环境 二.如何进行多环境配置呢 1.针对 yaml 配置文件 2.针对 properties 配置文件 三.命令行启动参数 第一步:打包项目 第二步:输入命令行 补充内容 一.为什么要配置多环境 当我们工作开发真实的项目时,可能会遇到不同的环境,如:开发环境.生产环境.测试环境等,不同的环境所需要的配置内容也会不尽相同,如果我们每次切换环境时再去配置对应的环境配置,肯定会降低我们的开发效率,所以,掌握多环境配置还是非常有必要的. 二.如何进行多环境配置呢 我们还是以配置端

  • SpringBoot超详细讲解自动配置原理

    目录 SpringBoot自动配置原理 SpringBoot特点 1.依赖管理 A.父项目做依赖管理 B.开发导入starter场景启动器 C.可以修改默认版本号 2.自动配置 A.自动配好Tomcat B.自动配好SpringMVC C.默认的包结构 D.各种配置拥有默认值 E.按需要加载所有自动配置项 SpringBoot自动配置原理 了解SpringBoot自动配置原理 1.SpringBoot特点 2.容器功能 3.自动配置原理入门 4.开发技巧 SpringBoot特点 1.依赖管理

  • 使用SpringBoot根据配置注入接口的不同实现类(代码演示)

    目录 一.引言 二.代码演示 1.问题描述 2.解决方案 2.1使用@Autowired的时候将接口变量名改为实现类的限定名 2.2 使用@Autowired配合@Qualifier指定限定名注入实现类 2.3@ConditionalOnProperty 三.总结 一.引言 我们在使用springboot进行开发的时候经常用到@Autowired和@Resource进行依赖注入,但是当我们一个接口对应多个不同的实现类的时候如果不进行一下配置项目启动时就会报错,那么怎么根据不同的需求注入不同的类型

  • Spring详解四种加载配置项的方法

    目录 1.spring加载yml文件 2.spring 加载 properties 文件 3.spring加载系统磁盘(properties)文件 4.spring加载xml文件 5.Java基于InputStream读取properties配置文件 本文默认 spring 版本是 spring5 1 spring 加载 yml 文件 2 spring 加载 properties 文件 3 spring 加载 系统磁盘 文件 4 spring 加载 xml 文件 5 Java 基于 InputS

  • SpringBoot如何配置获取request中body的json格式参数

    目录 背景 获取请求中的参数(非json格式参数) 获取方法 结论 获取POST请求json格式的参数 经过检索推荐方法(参看后边完整方法) 实现方法 使用 背景 最近开发项目,因为有第三方调用我们的接口,我们使用SpringBoot以JavaBean的方式接收了我们预期的参数,参数接收也没有什么异常.但是有一些需求问题需要沟通,需要拿到合作第三方传入的所有参数,来进行参数核验. 如何拿到请求的所有参数呢?正常的思路肯定是从request中获取,如果是GET请求,参数在请求路径中拼接:如果是PO

  • springboot接口如何多次获取request中的body内容

    1. 概述 在使用springboot开发接口时,会将参数转化为Bean,用来进行参数的自动校验.同时也想获取request中原始body报文进行验签(防止报文传输过程中被篡改). 因为通过将bean再转化为字符串后,body里面的报文格式.字段顺序会发生改变,就会导致验签失败.因此只能通过request来获取body里面的内容. 既想接口自动实现参数校验,同时又想获取request中的原始报文,因此我们可以通过在controller中的restful方法中,写入两个参数,获取多次request

  • 使用JavaScript获取Request中参数的值方法

    假设现在有一个URL,如下. http://www.jb51.net 如何通过JS访问到id和name里面的值呢,实现我们来分析一下思路. 先获取当前页面的URL,通过window.location.href. 提取该URL?后面的部分,通过slice()方法. 把获取到的Request对象分割成字符串数组,通过split() 方法. 接下来看代码. function getUrlVars() { var vars = [], hash; var hashes = window.location

  • java 获取request中的请求参数代码详解

    1.get 和 post请求方式 (1)request.getParameterNames(); 获取所有参数key后.遍历request.getParameter(key)获取value (2)request.getParameterMap() .直接包含参数key和value值,简单方便 Map<String, String[]>maps = request.getParameterMap(); for (Map.Entry<String, String[]> entry :

  • JFINAL+Ajax传参 array 数组方法 获取request中数组操作

    前台代码js var _list =[]; for (var i = 0; i < array.length; i++) { _list[i] = array[i]; } $.ajax({ type: "post", url: "", data: {"ids": _list }, dataType: "json", success: function(data){ alert("添加成功"); } }

  • python3实现从kafka获取数据,并解析为json格式,写入到mysql中

    项目需求:将kafka解析来的日志获取到数据库的变更记录,按照订单的级别和订单明细级别写入数据库,一条订单的所有信息包括各种维度信息均保存在一条json中,写入mysql5.7中. 配置信息: [Global] kafka_server=xxxxxxxxxxx:9092 kafka_topic=mes consumer_group=test100 passwd = tracking port = 3306 host = xxxxxxxxxx user = track schema = track

  • Python中xml和json格式相互转换操作示例

    本文实例讲述了Python中xml和json格式相互转换操作.分享给大家供大家参考,具体如下: Python中xml和json格式是可以互转的,就像json格式转Python字典对象那样. xml格式和json格式互转用到的xmltodict库 安装xmltodict库 C:\Users\Administrator>pip3 install xmltodict Collecting xmltodict   Downloading xmltodict-0.11.0-py2.py3-none-any

  • python中Requests发送json格式的post请求方法

    目录 前言 1.普通string类型 2.string内是字典的 3.元组(嵌套列表或者) 4.字典 5.json 6.传入非嵌套元组或列表 7.以post(url,json=data)请求 前言 问题: 做requests请求时遇到如下报错: {“code”:“500”,“message”:"JSON parse error: Cannot construct instance of com.bang.erpapplication.domain.User (although at least

  • jsp中利用jquery+ajax在前后台之间传递json格式参数

    经过一段时间的实验琢磨,终于将前后台之间的参数传递搞定了,实验所用工具myeclipse+structs1.2. 总结:容易出错的地方:1.ajax中data的格式一定要写对,这里举了两种形式,一种是 data:{参数:""} 另一种是 data:"参数="+变量. 2.后台传递到前台数据转化为json格式,步骤要掌握好. 3.在js使用jquery必须要引用进来,否则会不执行jquery语句,这个问题困扰了我半天才解决掉,菜鸟的悲哀啊.jquery引用流程如下:网

  • 配置iis6,iis7.5支持解析.json格式文件的方法

    配置iis支持.json格式的文件 发现要让IIS支持json文件并不是单纯的添加mime这么简单啊,以下是设置方法: 一.IIS 6 1. MIME设置: 在IIS的站点属性的HTTP头设置里,选MIME 映射中点击"文件类型"-"新类型",添加一个文件类型: 关联扩展名:*.json 内容类型(MIME):application/x-javascript 2. Script Map Handler设置: 还是在IIS的站点属性里,"主目录"-

随机推荐