Spring MVC Controller 嵌套参数在三种 Content-Type 下的绑定方式

以下将介绍 application/jsonmultipart/form-data, application/x-www-form-urlencoded 三种 Content-Type 传参情况下,Spring MVC 控制器中参数绑定的方式。

1. 前置内容

1.1 关于三种 Content-Type

这里主要介绍三种 Content-Type:

  1. multipart/form-data 请求中既可以携带文件,又可以携带参数。其中参数以键值对的方式传递,参数之间、参数与文件之间以 content-disposition 分隔;
  2. application/x-www-form-urlencoded 只能上传参数,不能携带文件,参数通过 ?xxx=xxx&xxx=xxx 的方式被组织在一起;
  3. application/json 只能上传参数,不能携带文件,参数不被特殊组织,保持原 JSON 字符串的形式。

1.2 扩展:浏览器调试工具中请求参数的形式

在前端发送请求时,我们可以通过浏览器看到请求的参数。在浏览器调试工具中,参数栏会有多种标题:

  1. Query String Parameters


当使用 GET 方式提交请求时,采用这一标题

  1. Request Payload


当使用 application/json 方式提交时,采用这一标题

  1. Form Data


当使用 multipart/form-dataapplication/x-www-form-urlencoded 方式提交时,采用这一标题,注意这两种 form-data 的区别。

1.3 测试数据

这里采用嵌套数据如下:

{
    "username": "dailybird",
    "password": "dailybirdo",
  "ids": [1,2,3],
    "detail": {
        "gender": "male",
        "location": "Beijing",
    "ids": [4,5,6]
    }
}

注:与文件上传相关的参数后面会单独提到,这里先进行非文件参数提交的实验。

1.4 预期绑定的对象

这里,仿照请求参数的格式创建 User 对象,我们试图将请求参数绑定到该对象上。这里使用 Lombok 来减少 setter 的创建:

@ToString
@Data
public class User {
    private String username;
    private String password;
    private List<Integer> ids;

    private Detail detail;

    @Data
    public static class Detail {
        private String gender;
        private String location;
        private List<Integer> ids;
    }
}

2. 绑定方式

2.1 利用 @RequestBody 解析 application/json 的 POST请求

控制器代码如下:

  @RequestMapping(value = "/application/json")
  public String applicationJson(@RequestBody User user) {
      log.info("{}", user.toString());
      return user.toString();
  }

当使用 POST,并携带 Content-Type: application/json 头发送请求时,控制器能够完全解析嵌套的参数。

注:由于 @RequestBody 本身是调用 HttpMessageConverter 解析请求体中的数据,而 GET 方式的参数不会存在于请求体中,所以 @RequestBody 不能处理 GET 方式的请求。

2.2 利用 @RequestParam 接收 multipart/form-data 及 application/x-www-form-urlencoded 中的请求

控制器代码如下:

public String xWwwFormUrlencoded(@RequestParam("username") String username,
                                     @RequestParam("password") String password,
                                     @RequestParam("ids")List<Integer> ids,
                                     @RequestParam("detail") Detail detail) {
        log.info("{}, {}, {}, {}", username, password, ids, detail);
        return "";
    }

其中 Detail 类为与之前 User 内部类等同的类。

2.2.1 application/x-www-form-urlencoded

这里我们借助 jquery 的相关函数进行测试:

$.post("http://localhost:8083/application/x-www-form-urlencoded", {
    "username": "aaa",
    "password": "bbb",
    "ids": [1,2,3],
    "detail": {
        "gender": "ccc",
        "location": "ddd",
        "ids": [4,5,6]
    }
})

然后我们收到了如下提示:

Required List parameter 'ids' is not present

但我们确实已经发送了 ids 参数,为什么没有获取到呢?这一点我们放到之后再谈,先试一下 multipart/form-data 的方式。

2.2.2 multipart/form-data

当然,采用这一方式,我们会收到同样的提示:

Required List parameter 'ids' is not present
2.2.3 参数接收到的问题

让我们在浏览器的开发者工具中看一看请求参数实际的样子:

不同于 PHP 框架 Laravel,@RequestParam 并不会将 ids[] 之类的数组类参数和 detail[xxx] 之类的嵌套参数进行重组。因而,控制器会认为收到了 ids[] 参数,而不是 ids 参数,同理也适用于嵌套参数。

那我们该怎么做的?我在 Stack Overflow 上得到了解答,我们可以采用以下方法之一:

  1. ids[] 改为 ids 传参,即 ids=1&ids=2&... 的方式( 注意对比上图 ),将嵌套类参数 detail[gender] 等改为 detail.gender
  2. 将嵌套参数采用 Map 类型接收。

注:在 Spring MVC 中,我们可以不书写 @RequestParam,直接使用相与请求参数同名的变量进行接收( 或直接使用一个 POJO 对象 ),但该方式也存在着与以上相同的问题。

2.3 文件上传问题

最开始已经说过,若要上传文件,在上述三种 Content-Type 中,只能使用 multipart/form-data,在注意到 2.2 中所提到的问题后,我们便可以通过 MultipartFile 类型的属性来获取到文件参数了。

3. 总结

从 Laravel 过渡到 Spring Boot,确实感到了在控制器层面二者的差异( 当然在 DAO 层更是如此 ),以下给出一个列表,用以纪念自己踩的坑:

参考链接

  1. postman中 form-data、x-www-form-urlencoded、raw、binary的区别 - CSDN博客
  2. http - What's the difference between "Request Payload" vs "Form Data" as seen in Chrome dev tools Network tab - Stack Overflow
  3. spring - What is difference between @RequestBody and @RequestParam? - Stack Overflow
  4. 浅谈@RequestMapping @ResponseBody 和 @RequestBody 注解的用法与区别 - CSDN博客
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC是一种基于MVC(Model-View-Controller)设计模式的Web框架,它可以帮助我们快速地开发Web应用程序。在Spring MVC中,Controller是控制器的核心组件,它负责接收用户请求并决定如何处理这些请求。 在Spring MVC中,我们可以通过编写Controller类来实现请求的处理。在Controller类中,我们可以定义多个方法,每个方法对应处理一个具体的请求。这些方法通常使用注解来标识它们应该处理哪些请求。 例如,我们可以使用@Controller注解来标识一个类为Controller,并使用@RequestMapping注解来标识一个方法应该处理哪些请求。下面是一个简单的Controller类的示例: ``` @Controller @RequestMapping("/hello") public class HelloController { @RequestMapping("/world") public ModelAndView helloWorld() { String message = "Hello World, Spring MVC!"; return new ModelAndView("hello", "message", message); } } ``` 在上面的示例中,我们使用@Controller注解将HelloController类标识为Controller,并使用@RequestMapping注解将该类处理的请求路径设置为“/hello”。我们还使用@RequestMapping注解将helloWorld方法标识为处理“/hello/world”请求的方法。该方法返回一个包含“Hello World, Spring MVC!”消息的ModelAndView对象。 需要注意的是,Controller类的方法可以返回不同类型的结果。除了ModelAndView之外,还可以返回String、void、HttpEntity、ResponseEntity等类型的结果。这些结果将由Spring MVC框架进行处理,并将相应的内容返回给客户端。 总的来说,Spring MVCController是Web应用程序的核心组件之一,它可以帮助我们快速地开发功能强大、易于维护的Web应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值