我有一个使用Thymeleaf作为模板引擎的Spring Boot应用程序(库管理器)。
该应用程序包括两个主要的@Component
s,Book
和Author
。一个Book
可以有一个Author
。
我可以成功创建,检索,更新和删除任何Author
,但无法正确创建或更新Book
s(虽然我可以检索并删除它们)。在发出插入Book
的POST请求时(插入已经存在的对象只是更新它)我在浏览器中收到400 Bad Request. Validation failed for object='book'.
错误,并且控制台列出了typeMismatch: Failed to convert property value of type 'java.lang.String' to required type 'com.springboot.demoweb.model.Author'
错误。
我正在检查发送到应用程序的请求,而不是发送一个JSON“节点”(抱歉,我不知道JSON是如何工作的)包含根JSON“节点”Author
中Book
的属性,它只是发送结果Author.toString()
(我过分夸大。不重写这个方法不会改变任何东西)。
这是<select>
片段:
<form action="#" th:action="@{/books}" th:object="${book}" method="post">
...
<select th:field="*{author}">
<option th:each="author : ${authors}"
th:value="${author}"
th:text="${author}"/>
</select>
...
</form>
Author.java
文件:
@Component
public class Author implements Serializable {
private String id;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Calendar dateBorn;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Calendar dateDeath;
... // Getters and setters
}
Book.java
文件:
@Component
public class Book {
private String isbn;
private String title;
private Author author;
... // Getters and setters
}
和BookController.java
:
@Controller
public class BookController {
@GetMapping(value = "/books/edit", params = "isbn")
public String viewEdit(@RequestParam("isbn") String isbn, Model model) {
model.addAttribute("book", bookService.find(isbn));
model.addAttribute("authors", authorService.all());
return "books/edit";
}
// th:action="@{/books}" falls here
@PostMapping("/books")
public RedirectView create(Book book) {
bookService.create(book);
return new RedirectView("/books.html?isbn=" + book.getIsbn());
}
... // Other mappings
}
变量bookService
和authorService
分别对Map<String, Book>
和Map<String, Author>
进行简单的操作。
我可以为Thymeleaf做什么将Author
作为Book
中的对象发送,而不仅仅是它的字符串表示?我是否必须在HTML页面中更改某些内容?控制器?添加Thymeleaf配置?
根据百里香documentation,
表单标记中的th:object属性的值必须是变量表达式($ {...}),仅指定模型属性的名称,不带属性导航。这意味着像$ {seedStarter}这样的表达式是有效的,但$ {seedStarter.data}不会。
您的Thymeleaf表单呈现所选作者的toString值。无法在选项列表中绑定Author对象。因此,更改Book对象添加新属性authorId(如果Book是JPA实体,请使用@Transient注释)。
@Component
public class Book {
private String isbn;
private String title;
private Author author;
// New Transient attribute to bind authorId from select component
private String authorId;
}
现在,可以使用select组件绑定此新属性,因为它符合上述文档。
<form action="#" th:action="@{/books}" th:object="${book}" method="post">
...
<select th:field="*{authorId}">
<option th:each="author : ${authors}"
th:value="${author.id}"
th:text="${author.name}"/>
</select>
...
在您的控制器中,您可以使用book.authorId获取作者对象并将其设置为book对象。
@PostMapping("/books")
public RedirectView create(Book book) {
Author author = authorService.findById(book.authorId);
book.setAuthor(author);
bookService.create(book);
return new RedirectView("/books.html?isbn=" + book.getIsbn());
}