在 RESTful API 開發中,前端傳遞陣列/集合參數是常見需求:多選框、標籤過濾、批量刪除 ID 列表等。Spring MVC、JAX-RS(Jersey/RESTEasy)對陣列參數的綁定規則各有差異,且表單提交(application/x-www-form-urlencoded)與檔案上傳(multipart/form-data)的註解不同。本文系統梳理各場景下的正確姿勢,避開「只拿到第一個值」或「綁定報錯」的坑。
@PostMapping("/users")
public ResponseEntity createUser(
@RequestParam String username,
@RequestParam List keywords) { // 自動綁定同名多值參數
// 前端:keywords=java&keywords=spring&keywords=boot
// keywords = ["java", "spring", "boot"]
}
List 或 String[] 接收,Spring 自動處理同名參數綁定。Set 或自定義集合,除非註冊 Formatter。@POST @Path("/user") @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createUser(@FormParam("username") String username,
@FormParam("keyword") List keywords) {
// 同理支援 List 自動綁定多值
}
當介面同時接收檔案與陣列參數時,不能繼續使用 List,會拋出 IllegalArgumentException: wrong number of arguments。
// 錯誤:multipart 下 List 無法綁定
@FormDataParam("keyword") List keywords
@POST @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
@FormDataParam("filename") String filename,
@FormDataParam("file") InputStream fileStream,
@FormDataParam("file") FormDataContentDisposition fileMeta,
@FormDataParam("keyword") List keywordParts) { // 關鍵:用 BodyPart
if (keywordParts != null) {
for (FormDataBodyPart part : keywordParts) {
String keyword = part.getValueAs(String.class);
System.out.println("關鍵字:" + keyword);
}
}
}
原理:multipart 請求中每個同名部分都是獨立的 Body Part,FormDataBodyPart 封裝了該部分的 Header 與 Body,透過 getValueAs() 可安全轉型。
@PostMapping("/upload")
public ResponseEntity upload(
@RequestPart("file") MultipartFile file,
@RequestParam("keyword") List keywords) { // Spring 封裝後可直接用 List
// Spring MultipartResolver 已處理好多部分綁定
}
Spring 的 @RequestParam 在 multipart 下對集合參數支援良好,但若用底層 Servlet API 或 JAX-RS 則需用 FormDataBodyPart。
| 註解 | 用途 | 典型屬性 |
|---|---|---|
@GET @POST @PUT @DELETE @HEAD @PATCH | HTTP 方法映射 | 無 |
@Path | 資源路徑 | value 支援變量 {id}、正則 {id:\\d+} |
@Consumes | 指定請求 MIME 類型 | value = MediaType.APPLICATION_JSON |
@Produces | 指定回應 MIME 類型 | value = MediaType.APPLICATION_JSON |
@PathParam | 路徑變量綁定 | @PathParam("id") Long id |
@QueryParam | 查詢參數綁定 | @QueryParam("page") int page |
@HeaderParam | 請求頭綁定 | @HeaderParam("Authorization") String token |
@CookieParam | Cookie 綁定 | @CookieParam("JSESSIONID") String sid |
@FormParam | 表單欄位綁定 | @FormParam("name") String name |
@FormDataParam | multipart 表單欄位綁定 | @FormDataParam("file") InputStream is |
@BeanParam | 聚合參數物件 | 注入一個包含多個 *Param 的 Bean |
@DefaultValue | 預設值 | @QueryParam("size") @DefaultValue("20") int size |
@Context | 注入上下文物件 | HttpHeaders, UriInfo, Request, SecurityContext |
<form action="/api/tags" method="post">
<select name="tagIds" multiple>
<option value="1">Java</option>
<option value="2">Spring</option>
&select>
<button type="submit">提交</button>
</form>
後端:@RequestParam List
fetch('/api/batch-delete', {
method: 'DELETE',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ids: [101, 102, 103]})
});
後端:@RequestBody BatchDeleteDTO dto,DTO 內 List。
const formData = new FormData();
formData.append('file', fileBlob);
['java', 'spring'].forEach(k => formData.append('keyword', k));
fetch('/api/upload', {method: 'POST', body: formData});
後端:JAX-RS 用 List,Spring 用 @RequestParam List。
String 接收多值參數 → 改用 List 或 String[]。@FormParam 或 List 接收 → 改用 @FormDataParam + FormDataBodyPart。multipart/form-data 或 application/json → 檢查 fetch/axios headers。keywords、後端 keyword → 統一命名或用 @RequestParam("keywords") List kw 。List / String[] / DTO 綁定。List 逐個解析。@RequestParam List 即可,框架已封裝好。@QueryParam 查詢參數、@PathParam 路徑變量、@FormParam 表單、@FormDataParam 檔案上傳。掌握這些規則,RESTful 介面的陣列參數處理將不再是阻礙。