Skip to content

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";
}

业务架构师视角,我的回答会先讲机制、再讲这个场景的范式迁移:

  1. 传统服务端渲染时代:表单提交 + Session + 视图渲染是一体的一条链,@ModelAttribute、BindingResult 错误回显、PRG 模式(Post-Redirect-Get 防重复提交)都是围绕它演化的一整套工程实践;
  2. 前后端分离时代(当下主流):表单变成前端 JSON(@RequestBody)或 FormData 文件上传(MultipartFile),校验错误统一走 400 + 字段错误列表,前端负责回显——服务端不再关心"页面",只提供"契约";
  3. 仍然有效的老智慧:防重复提交(幂等号)、服务端二次校验(不信任前端校验)在 JSON 时代一样是硬要求,只是实现位置变了。维护遗留系统时,BindingResult 的错误回显、类型转换定制(CustomDateEditor 等)也仍需掌握。

一句话:机制会过时,"不信任输入、防重复提交"的业务安全原则不会。

🏗 系统架构师 ​

机制:POST 表单(application/x-www-form-urlencoded,带文件为 multipart/form-data)→ @RequestParam 逐字段或 @ModelAttribute 整对象绑定 → @Valid 校验 → 返回视图或 JSON。

先给时代判断:传统「表单提交加服务端渲染」在前后端分离主流下已是存量技术,新系统是 JSON API(@RequestBody)。但三个场景仍高频出现:

  1. 文件上传(multipart):流式解析、大小限制(spring.servlet.multipart.max-file-size)必须显式设置——默认 1MB 常把业务挡住,放开又要有配额与安全扫描;大文件应直传对象存储(预签名 URL),别经应用服务器内存中转;
  2. 存量系统维护:老门户、管理后台的表单链路;
  3. 安全配套:表单时代标配 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"),且回调会重发多次,必须做幂等。答完传统用法补回调场景,说明真踩过这块。