Spring Boot+Thymeleaf中@PostMapping出现405错误求助
Spring Boot MVC @PostMapping 405错误排查与修复
我是Java编程新手,正在开发简易银行账户项目,连续两天碰到Spring Boot MVC中@PostMapping的405错误,没法把AccountController里处理存款的executeDeposit方法和Thymeleaf表单正确关联,实现向账户余额添加存款金额的功能,求指导。
相关代码
AccountController代码
@Controller public class AccountController { @Autowired private AccountService accountService; @Autowired private AccountRepository accountRepository; // display all accounts @AliasFor(value = "accounts") @GetMapping("/accounts") public String showAccountsList(Model model) { List<Account> listAccounts = accountService.listAll(); model.addAttribute("listAccounts", listAccounts); return "accounts"; } @Autowired public AccountController(AccountService accountService) { this.accountService = accountService; } //get Account by Id @GetMapping("accounts/{id}") public ResponseEntity<Account> getAccountById(@PathVariable Long id) { Account account = accountRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Account with id: " + id + " does not exist")); return ResponseEntity.ok(account); } @GetMapping("/account_operations/{id}") public String getAccountDetailsById(@PathVariable Long id, Model model) { Account account = accountRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Account with id: " + id + " does not exist")); model.addAttribute("account", account); return "account_operations"; } @GetMapping("/account_operations/deposit/{id}") public String makeDeposit(@PathVariable Long id, Model model) { Account account = accountRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Account with id: " + id + " does not exist")); model.addAttribute("account", account); return "deposit"; } @PostMapping(value = "/account_operations/deposit/{id}") public String executeDeposit(@ModelAttribute("amount") double amount, Account account, RedirectAttributes ra) { accountService.executeDeposit(account, amount); ra.addFlashAttribute("message", "The customer has been saved successfully."); return "redirect:/customer_accounts"; } }
Thymeleaf存款页面代码
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Deposit</title> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css"/> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script> </head> <body> <div class="container-fluid text-center"> <div><h2>Deposit</h2></div> <div class="m-3"> <a class="h4" th:href="@{/accounts}">Back to accounts</a> <p>test</p> </div> <div> <div th:if="${account}"> <h2>Deposit to account:</h2> <h3>[[${account.accountNumber}]]</h3> <p>[[${account.balance}]]</p> <p>[[${account.accountType}]]</p> </div> <div> <h2>Amount to deposit</h2> <form th:action="@{/account_operations/deposit}" method="post" th:object="${account}" style="max-width: 500px; margin: 0 auto; font-size: small"> <div class="mb-3"> <label for="amount" class="form-label">Amount</label> <input type="number" th:value="${amount}" th:field="*{amount}" name="amount" id="amount" class="form-control form-control-sm" placeholder="Deposit Amount" required> </div> <div class="text-center"> <button type="submit" class="btn btn-primary m-2">Deposit</button> <button type="button" class="btn btn-secondary m-2" onclick="cancelForm()">Cancel</button> </div> </form> </div> </div> </div> </body> </html>
Account实体类代码
@Setter @Getter @NoArgsConstructor @AllArgsConstructor @Entity @Table @EntityListeners(AuditingEntityListener.class) @SecondaryTable(name = "customer", pkJoinColumns = @PrimaryKeyJoinColumn(name = "")) public class Account { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private double balance; private double interest; private String accountNumber; private AccountType accountType; private double amount; @ManyToOne @JoinColumn(name = "customer_id", insertable = false, updatable = false) @JsonBackReference private Customer customer; public Account(double balance, double interest, AccountType accountType) { this.balance = balance; this.interest = interest; this.accountNumber = generateIbanAccountNumber(); this.accountType = accountType; } public String generateIbanAccountNumber() { return new Iban.Builder().countryCode(CountryCode.PL).bankCode("696").buildRandom().toString(); } public void withdraw(double amount) { balance -= amount; } public void deposit(double amount) { balance += amount; } }
问题修复步骤
1. 修正表单提交URL(核心问题)
你的@PostMapping路径是/account_operations/deposit/{id},但Thymeleaf表单的th:action未携带账户ID,导致请求路径不匹配,触发405错误。
修改表单action:
<form th:action="@{/account_operations/deposit/{id}(id=${account.id})}" method="post" th:object="${account}">
2. 调整Controller参数绑定逻辑
原方法中参数绑定混乱,且直接使用表单提交的Account对象存在数据不全的风险,建议从数据库重新获取账户信息:
@PostMapping(value = "/account_operations/deposit/{id}") public String executeDeposit(@PathVariable Long id, @ModelAttribute("account") Account account, RedirectAttributes ra) { // 从数据库获取最新账户数据 Account existingAccount = accountRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Account with id: " + id + " does not exist")); accountService.executeDeposit(existingAccount, account.getAmount()); ra.addFlashAttribute("message", "存款操作成功完成。"); return "redirect:/accounts"; // 修正跳转路径为已存在的列表页 }
3. 优化Thymeleaf表单字段
移除冗余的th:value="${amount}",th:field会自动处理值绑定:
<input type="number" th:field="*{amount}" name="amount" id="amount" class="form-control form-control-sm" placeholder="Deposit Amount" required>
4. 统一依赖注入方式
移除字段上的@Autowired,改用构造方法注入(更符合Spring最佳实践):
@Controller public class AccountController { private final AccountService accountService; private final AccountRepository accountRepository; public AccountController(AccountService accountService, AccountRepository accountRepository) { this.accountService = accountService; this.accountRepository = accountRepository; } // 其他方法保持不变 }
5. 优化实体类设计(可选)
Account实体中的amount字段属于临时请求参数,不需要持久化到数据库,建议删除该字段,改用@RequestParam接收存款金额:
表单修改:
<form th:action="@{/account_operations/deposit/{id}(id=${account.id})}" method="post"> <div class="mb-3"> <label for="amount" class="form-label">Amount</label> <input type="number" name="amount" id="amount" class="form-control form-control-sm" placeholder="Deposit Amount" required> </div> <!-- 按钮保持不变 --> </form>
Controller方法修改:
@PostMapping(value = "/account_operations/deposit/{id}") public String executeDeposit(@PathVariable Long id, @RequestParam("amount") double amount, RedirectAttributes ra) { Account existingAccount = accountRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Account with id: " + id + " does not exist")); accountService.executeDeposit(existingAccount, amount); ra.addFlashAttribute("message", "存款操作成功完成。"); return "redirect:/accounts"; }
内容的提问来源于stack exchange,提问作者archandy
相关产品推荐
相关产品推荐

