You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 21:45:38