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

Spring Boot Todo应用点击Done按钮报错:id参数存在但转为null

Spring Boot Todo应用完成功能错误排查与修复

问题描述

开发Spring Boot Todo应用时,勾选待办事项旁的复选框并点击Done按钮,触发错误:

'Required request parameter 'id' for method parameter type Integer is present but converted to null'

需求:

  • 勾选复选框点击Done后,将对应todo的isCompleted值从false改为true
  • 正确展示isCompleted为true的已完成待办事项

错误原因分析

  1. 前端参数传递错误:done-todo表单的隐藏字段绑定了控制器中新增的空Todo对象(model.addAttribute("todo", new Todo(...))),该对象的id为null,导致提交时后端接收到的id参数为空。
  2. 复选框关联逻辑错误:复选框绑定全局todo对象的isCompleted属性,而非当前遍历的待办项,无法关联到要标记完成的具体todo。
  3. 已完成事项展示逻辑错误:折叠面板中循环单个todo对象而非完整的待办列表,无法遍历展示所有已完成项。
  4. Service层空指针隐患:findById方法中,当Optional为空时调用orElseGet(updateTodo::get)会触发空指针异常。

分步修复方案

1. 修复前端表单与复选框关联

修改home.html的待办列表和Done按钮表单部分,确保传递正确的todo ID:

<h2>LIST</h2>
<!-- 遍历展示未完成的待办项 -->
<div th:each="todoItem : ${list}" th:if="${!todoItem.isCompleted}">
    <input type="checkbox" name="todoIds" th:value="${todoItem.id}" id="todo-[[${todoItem.id}]]">
    <label th:for="todo-[[${todoItem.id}]]" th:text="${todoItem.title}"></label>
    <span th:text="${todoItem.description}"></span>
    <span th:text="${todoItem.dueDate}"></span>
</div>

<!-- Done按钮表单:提交选中的todo ID集合 -->
<form method="post" th:action="@{/todo/update/{userId}(userId=${id})}">
    <input type="submit" value="done">
</form>

2. 修复后端Done接口逻辑

修改TodoController.java的doneTodo方法,支持接收多个选中的todo ID并标记完成:

@PostMapping("/todo/update/{userId}")
public String doneTodo(@RequestParam(name="todoIds") List<Integer> todoIds, @PathVariable("userId") Integer userId) {
    for(Integer todoId : todoIds) {
        Todo updateTodo = todoService.findById(todoId);
        if(updateTodo != null) {
            updateTodo.setIsCompleted(true);
            todoService.addTodo(updateTodo);
        }
    }
    return "redirect:/todo/" + userId;
}

同时修复TodoService.java的findById方法,避免空指针:

public Todo findById(Integer id) {
    return todoRepository.findById(id).orElse(null);
}

3. 修复已完成待办展示逻辑

修改home.html的折叠面板部分,正确遍历待办列表并筛选已完成项:

<div>
    <button type="button" class="btn btn-primary p-3" data-toggle="collapse" data-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample">
        show done todos
    </button>
    <div class="collapse" id="collapseExample">
        <div class="border p-3" th:each="todoItem : ${list}" th:if="${todoItem.isCompleted}">
            <span th:text="${todoItem.title}"></span>
            <span th:text="${todoItem.description}"></span>
            <span th:text="${todoItem.dueDate}"></span>
            <!-- 可选:添加删除已完成项的表单 -->
            <form method="post" th:action="@{/todo/delete/{userId}(userId=${id})}">
                <input type="hidden" name="todoId" th:value="${todoItem.id}">
                <input type="submit" value="delete">
            </form>
        </div>
    </div>
</div>

4. 修复主页控制器的模型属性

修改TodoController.java的home方法,传递空的Todo对象用于表单绑定,避免传递未初始化的无效值:

@GetMapping("/todo/{id}")
public String home(Model model, User user, @PathVariable("id")Integer id) {
    Integer userId = user.getId();
    if(userId == null) {
        return "redirect:/todo/" + id;
    }
   
    List<Todo> list = todoRepository.find(userId);
    model.addAttribute("list", list);
    // 传递空的Todo对象,仅初始化userId
    Todo emptyTodo = new Todo();
    emptyTodo.setUserId(userId);
    model.addAttribute("todo", emptyTodo);
    return "home";
}

功能验证

  1. 勾选待办事项的复选框,点击Done按钮,选中的todo会被标记为已完成(isCompleted=true)
  2. 点击"show done todos"按钮,折叠面板会展示所有已完成的待办事项

内容的提问来源于stack exchange,提问作者funeral of tech

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:31:20