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的已完成待办事项
错误原因分析
- 前端参数传递错误:
done-todo表单的隐藏字段绑定了控制器中新增的空Todo对象(model.addAttribute("todo", new Todo(...))),该对象的id为null,导致提交时后端接收到的id参数为空。 - 复选框关联逻辑错误:复选框绑定全局
todo对象的isCompleted属性,而非当前遍历的待办项,无法关联到要标记完成的具体todo。 - 已完成事项展示逻辑错误:折叠面板中循环单个
todo对象而非完整的待办列表,无法遍历展示所有已完成项。 - 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"; }
功能验证
- 勾选待办事项的复选框,点击Done按钮,选中的todo会被标记为已完成(
isCompleted=true) - 点击"show done todos"按钮,折叠面板会展示所有已完成的待办事项
内容的提问来源于stack exchange,提问作者funeral of tech
相关产品推荐
相关产品推荐

