I’ve tried to implement TodoList editing into my GetTodoController and it doesn’t work.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create Todo</title>
<link rel="stylesheet" th:href="@{/styles/todo/operation.css}">
</head>
<body>
<h2>Edit your Todo!</h2>
<form th:method="PUT" th:action="@{/todo/edit/{id}(id=${list.id})}" th:object="${list}">
<label for="name">Name: </label>
<input type="text" th:field="*{name}" id="name">
<span style="color: #ba6a65" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<br>
<label for="description">Description: </label>
<textarea th:field="*{description}" id="description">Enter text here...</textarea>
<span style="color: #ba6a65" th:if="${#fields.hasErrors('description')}" th:errors="*{description}"></span>
<br>
<label for="deadline">Deadline: </label>
<input type="datetime-local" th:field="*{deadline}" id="deadline">
<span style="color: #ba6a65" th:if="${#fields.hasErrors('deadline')}" th:errors="*{description}"></span>
<br>
<input type="submit">
</form>
</body>
</html>
@RequestMapping("/todo/edit/{id}")
@Controller
public class UpdateTodoController {
private final TodoListRepository todoListRepository;
@Autowired
public UpdateTodoController(TodoListRepository todoListRepository) {
this.todoListRepository = todoListRepository;
}
@ModelAttribute(name = "list")
public TodoList todoList(@PathVariable Long id) {
return todoListRepository.findById(id).orElse(null);
}
@GetMapping
public String getUpdateTodo(@PathVariable Long id) {
return "todo/update";
}
@PutMapping
public String putUpdateTodo(@PathVariable Long id, @ModelAttribute(name = "list") @Valid TodoList todoList, BindingResult bindingResult) {
if (bindingResult.hasErrors()) return "todo/update";
System.out.println(todoList);
todoListRepository.update(todoList);
return "redirect:/todo";
}
}
I’ve changed @PutMapping to @PostMapping and th:method="PUT" to th:method="POST" and everything has worked, but it is conceptually wrong, cuz of @PostMapping – adding new, @PutMapping – updating
2
Answers
Here are the changes explained:
In your HTML form, I changed
th:method
fromPUT
toPOST
. This is because HTML forms only supportGET
andPOST
methods directly.I added a hidden input field with the name
_method
and valuePUT
. This is a workaround provided by Spring’s HiddenHttpMethodFilter. When the form is submitted, it will be sent as aPOST
request, but the HiddenHttpMethodFilter will see the_method
parameter and treat it as aPUT
request before it reaches your controller.In your Spring controller, I kept
@PutMapping
. With the changes made in the HTML form, Spring’s HiddenHttpMethodFilter will ensure that this method is correctly invoked when the form is submitted.Remember to ensure that HiddenHttpMethodFilter is registered in your Spring Boot application. If you’re using Spring Boot, it auto-configures this filter for you.
For PUT mappings to work with Spring Boot and Thymeleaf you need to set the following property in application.properties:
This will enable the hidden method filter and when used with th:method the filter will allow PUT methods to be processed correctly.