使用Thymeleaf传递Select选项的Skill对象到Controller报错求助
问题描述
我尝试通过Thymeleaf将select组件选中的Skill对象传递到Controller,但提交表单时出现400错误,相关代码及错误信息如下:
实体类Skill
@Entity @Table(name = "Skills") @Builder public class Skill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; @Column(name = "title", nullable = false, length = 40) private String title; @Column(name = "category", nullable = false, length = 30) @Enumerated(EnumType.STRING) private Category category; }
传入技能列表的IndexController
@Controller @RequestMapping("/") @RequiredArgsConstructor public class IndexController { private final SkillService skillService; @GetMapping("/manager-profile") public String managerProfile(Model model) { model.addAttribute("skillList", skillService.findAllSkills()); return "manager-profile"; } }
视图表单代码
<form th:action="@{/getEmployeeBySkill}" method="post" id="skill-form"> <div class="select-box manager"> <select class="select_box manager" id="select-skill" name="skill"> <option value="">Select</option> <option th:each="skill : ${skillList}" th:attr="data-category=${skill.category}" th:value="${skill}" th:name="skill" th:text="${skill.title}"></option> </select> <input type="number" class="form-control" id="experience" placeholder="Experience"> <button type="submit" class="btn manager-select_box">Search</button> </div> </form>
接收请求的EmployeeController
@Controller @RequestMapping("/") public class EmployeeController { private final EmployeeService employeeService; @PostMapping("/getEmployeeBySkill") String getEmployeesBySkill (Model model, @ModelAttribute("skill") Skill skill) { // do stuff return "candidate-list"; } }
错误信息
There was an unexpected error (type=Bad Request, status=400). Failed to convert value of type 'java.lang.String' to required type 'com.neusta.domain.Skill'; Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'com.neusta.domain.Skill@bb0c873' org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.neusta.domain.Skill'; Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'com.neusta.domain.Skill@bb0c873'
错误原因
核心问题出在<option>标签的th:value="${skill}"上:当你直接将Skill对象赋值给value属性时,Thymeleaf会默认调用对象的toString()方法,最终提交到后台的是类似com.neusta.domain.Skill@bb0c873的字符串。而Spring无法将这个无意义的字符串直接转换为Skill对象,因此抛出类型转换异常。
修复方案
有两种常用的解决方式:
方式一:传递Skill的ID,后台通过ID查询对象
这是最简单直接的方案:
- 修改视图中的
<option>标签,将th:value改为Skill的id,并移除重复的th:name="skill"(父标签<select>已设置name="skill"):
<option th:each="skill : ${skillList}" th:attr="data-category=${skill.category}" th:value="${skill.id}" th:text="${skill.title}"></option>
- 修改Controller方法,接收id后查询对应的Skill对象:
@PostMapping("/getEmployeeBySkill") String getEmployeesBySkill (Model model, @RequestParam("skill") Long skillId) { Skill skill = skillService.findSkillById(skillId); // 后续业务逻辑 return "candidate-list"; }
方式二:自定义Converter实现自动转换(适合复杂场景)
如果希望保持Controller接收Skill对象的写法,可以自定义Spring Converter,让框架知道如何将提交的ID字符串转换为Skill对象:
- 实现
Converter<String, Skill>接口:
@Component public class StringToSkillConverter implements Converter<String, Skill> { private final SkillService skillService; public StringToSkillConverter(SkillService skillService) { this.skillService = skillService; } @Override public Skill convert(String source) { if (source == null || source.isEmpty()) { return null; } return skillService.findSkillById(Long.parseLong(source)); } }
- 视图部分同方式一(传递Skill的id),Controller可以保持原来的写法直接接收Skill对象:
@PostMapping("/getEmployeeBySkill") String getEmployeesBySkill (Model model, @ModelAttribute("skill") Skill skill) { // do stuff return "candidate-list"; }
内容的提问来源于stack exchange,提问作者Greta
相关产品推荐
相关产品推荐

