Spring Boot 接收数组参数

普通的数组

定义一个简单的 Controller,它接收一个 String[] 类型的数组参数,如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping
    public Object demo (@RequestParam("hobby") String[] hobby) {
        return hobby;
    }
}

Spring Boot 可以直接把以逗号分割的参数封装为集合、数组,例如:

$ curl "localhost:8080/demo?hobby=chang,tiao,rap"
["chang","tiao","rap"]

其他框架、程序不一定会根据逗号进行分割。更优雅的方式,也是通用的方式应该是多次声明同名参数,例如:

$ curl "localhost:8080/demo?hobby=chang&hobby=tiao&hobby=rap"
["chang","tiao","rap"]

数组也可以替换为 Collection 接口,Spring 都会正确地处理。特别是在一些需要对数组参数去重的场景,推荐使用 Set 作为参数,如下:

@GetMapping
public Object demo (@RequestParam("hobby") Set<String> hobby) {
    return hobby;
}

发起请求,这一次 rap 值重复传递了 3 次:

$ "localhost:8080/demo?hobby=chang&hobby=tiao&hobby=rap&hobby=rap&hobby=rap"
["chang","tiao","rap"]

得益于 Set 自带去重的特性,所以最终 hobby 集合中重复的 rap 值,只保留了一个。

使用 Set 作为参数的时候,默认使用的实现是 java.util.LinkedHashSet,目的是为了保证集合中的参数顺序和传递的顺序一样。

Spring 还会自动地把传递的字符串参数,转换为 Controller 方法声明的类型。

如,定义 Integer 类型的数组、集合:

@GetMapping
public Object demo (@RequestParam("val") Set<Integer> val) {  // Set<Integer> 类型
    return val;
}

发起请求,传递数值类型的字符串参数:

$ curl "localhost:8080/demo?val=1&val=2&val=3"
[1,2,3]

如果不能成功地转换,则会抛出异常。如下,最后一个参数传递了一个 NaN 字符串:

$ curl "localhost:8080/demo?val=1&val=2&val=NaN"
{"timestamp":"2023-09-22T09:07:48.592+00:00","status":400,"error":"Bad Request","path":"/demo"}

服务端异常消息如下:

Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String[]' to required type 'java.util.Set'; For input string: "NaN"]

甚至可以自动转换 LocalDate 类型:

@GetMapping
public Object demo (@RequestParam("times") Set<LocalDate> times) {
    return times;
}

发起请求,传递日期格式的字符串参数:

curl "localhost:8080/demo?times=2023-09-22&times=2023-09-23&times=2023-09-24"
["2023-09-22","2023-09-23","2023-09-24"]

至于其他各种类型的参数,你都可以试一试!

对象中的普通数组

先定义一个简单的 User 对象,它有一个 String[] 类型的数组属性 :

public class User {
    private Integer id;
    private String name;
    private String[] hobby; // 数组类型的参数
    // 忽略 get、set 方法
}

修改 Controller,接收一个 User 类型的参数:

注意: 请求方式改为了 @PostMapping,并且移除了 @RequestParam 注解。

@PostMapping
public Object demo (User user) {
    return user;
}

为了更加直观的感受,这次使用 Postman 发起请求,对于数组类型的 hobby 参数,我们使用了逗号分割多个值:

Postman 传递数组参数,逗号分割

也可以通过中括号 [], 多次声明 hobby[] 来传递数组参数:

Postman 传递数组参数,多次声明

甚至可以在中括号中指定参数在数组中的索引,也就是下标。如下,我们把 rap 值放在了最前面,把 chang 值放在了最后面:

Postman 传递数组参数,指定下标

对象中的对象数组

修改 User 对象,它的 hobby 属性不再是一个普通的字符串数组。而是一个 Hobby 对象数组。

Hobby 对象以内部类形式定义,它有 2 个属性,如下:

public class User {

public static class Hobby {
    private String id;
    private String title;
}

private Integer id;
private String name;
private Hobby[] hobby; // Hobby 类型的数组属性
}

Controller 不需要改变。

由于数组参数 hobby 的元素类型是一个对象,对象有多个属性,因此需要在参数中指定其属性名称和值:

Postman 传递数组对象参数