Spring MVC 中如何处理表单提交?
- 标签:后端、Spring
🏛 业务架构师
Spring MVC 处理表单提交的传统链路:HTML 表单 POST → @ModelAttribute 把表单字段按名绑定到对象(走 WebDataBinder 类型转换)→ @Validated 校验 → Controller 处理 → 返回视图名 → ViewResolver 渲染结果页。
java
@PostMapping("/register")
public String register(@Validated @ModelAttribute UserForm form,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "register"; // 校验失败回到表单页回显错误
}
userService.register(form);
return "redirect:/success";
}业务架构师视角,我的回答会先讲机制、再讲这个场景的范式迁移:
- 传统服务端渲染时代:表单提交 + Session + 视图渲染是一体的一条链,@ModelAttribute、BindingResult 错误回显、PRG 模式(Post-Redirect-Get 防重复提交)都是围绕它演化的一整套工程实践;
- 前后端分离时代(当下主流):表单变成前端 JSON(
@RequestBody)或 FormData 文件上传(MultipartFile),校验错误统一走 400 + 字段错误列表,前端负责回显——服务端不再关心"页面",只提供"契约"; - 仍然有效的老智慧:防重复提交(幂等号)、服务端二次校验(不信任前端校验)在 JSON 时代一样是硬要求,只是实现位置变了。维护遗留系统时,BindingResult 的错误回显、类型转换定制(CustomDateEditor 等)也仍需掌握。
一句话:机制会过时,"不信任输入、防重复提交"的业务安全原则不会。
🏗 系统架构师
机制:POST 表单(application/x-www-form-urlencoded,带文件为 multipart/form-data)→ @RequestParam 逐字段或 @ModelAttribute 整对象绑定 → @Valid 校验 → 返回视图或 JSON。
先给时代判断:传统「表单提交加服务端渲染」在前后端分离主流下已是存量技术,新系统是 JSON API(@RequestBody)。但三个场景仍高频出现:
- 文件上传(multipart):流式解析、大小限制(
spring.servlet.multipart.max-file-size)必须显式设置——默认 1MB 常把业务挡住,放开又要有配额与安全扫描;大文件应直传对象存储(预签名 URL),别经应用服务器内存中转; - 存量系统维护:老门户、管理后台的表单链路;
- 安全配套:表单时代标配 CSRF token(Spring Security 默认开启)与重复提交防护(token/幂等键)——这些概念在 JSON API 时代同样必要,只是实现形态变了。
💻 全栈开发专家
传统表单(application/x-www-form-urlencoded)用 @ModelAttribute 或参数直接绑定:
java
// form: name=tom&age=20 —— 字段名自动映射
@PostMapping("/register")
public String register(@ModelAttribute @Valid UserForm form, BindingResult result, Model model) {
if (result.hasErrors()) return "register"; // 校验失败回显表单
userService.register(form);
return "redirect:/users/" + form.getName(); // PRG 防重复提交
}关键点:
- BindingResult 必须紧跟 @Valid 参数,否则校验失败直接抛异常而不是回显表单
- PRG(Post-Redirect-Get):提交后重定向,防 F5 重复提交
- 文件上传:
@RequestParam("file") MultipartFile file,配spring.servlet.multipart.max-file-size=10MB
现实情况:前后端分离后,纯表单提交只剩老管理后台还在用,但第三方支付回调是 form 格式——这个场景我处理过不少:@PostMapping("/callback") + @RequestParam Map<String,String> params 收全量,注意验签后必须按支付宝/微信的要求返回约定字符串(如 "success"),且回调会重发多次,必须做幂等。答完传统用法补回调场景,说明真踩过这块。