Spring表单未设置creationDate字段提交出现400错误,如何解决?
解决Spring MVC表单提交400错误的问题
你的400错误不是因为creationDate没在表单中设置,而是因为Spring MVC在绑定请求参数到BlogPost对象时遇到了两个核心问题:
问题根源分析
- Category字段绑定失败:表单的
<form:select>传递的是PostCategory的id(整数类型),但BlogPost中的category字段是PostCategory对象类型。Spring默认无法自动将整数转换为PostCategory实例,这会触发参数绑定异常,直接返回400错误。 - CreationDate的潜在更新问题:虽然你在新增时手动设置了
creationDate,但修改操作时,由于表单没有传递该字段,绑定后的BlogPost对象中creationDate会是null,执行updateBlogPost时会把数据库中已有的创建日期覆盖为null,这是一个隐藏bug。
解决方案
方案1:注册类型转换器(推荐,更优雅)
实现Spring的Converter接口,让Spring自动将整数(Category ID)转换为PostCategory对象:
- 创建转换器类:
import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; @Component public class IntegerToPostCategoryConverter implements Converter<Integer, PostCategory> { private final PostCategoryServiceImpl postCategoryService; // 构造注入Service public IntegerToPostCategoryConverter(PostCategoryServiceImpl postCategoryService) { this.postCategoryService = postCategoryService; } @Override public PostCategory convert(Integer source) { if (source == null) { return null; } return postCategoryService.getPostCategory(source); } }
- 配置转换器(如果使用Spring Boot,会自动扫描
@Component标记的转换器,无需额外配置;如果是XML配置,需要在<mvc:annotation-driven>节点中注册该转换器)。
这样修改后,Spring会自动将表单传递的Category ID转换为PostCategory对象,你可以删除控制器中blogPost.setCategory(...)这行代码,简化逻辑。
方案2:添加中间字段(快速解决)
在BlogPost实体中添加一个categoryId字段(不需要映射到数据库),用于表单绑定:
@Entity @Table(name="BlogPost") public class BlogPost implements Serializable { // 原有字段不变... // 新增中间字段,不映射数据库 @Transient private Integer categoryId; // getter和setter public Integer getCategoryId() { return categoryId; } public void setCategoryId(Integer categoryId) { this.categoryId = categoryId; } }
然后修改表单的<form:select>的path为categoryId:
<form:select path="categoryId"> <c:forEach items="${allCats}" var="cat" > <form:option value="${cat.id}"> ${cat.title} </form:option> </c:forEach> </form:select>
最后在控制器中处理:
@RequestMapping(value = "/saveBlogPost", method = RequestMethod.POST) public ModelAndView saveBlogPost(@ModelAttribute BlogPost blogPost) { if (blogPost.getId() == 0) { // 新增时设置分类和创建日期 blogPost.setCategory(postCategoryServiceImpl.getPostCategory(blogPost.getCategoryId())); blogPost.setCreationDate(new Date()); blogPostServiceImpl.addBlogPost(blogPost); } else { // 修改时先从数据库获取原有对象,避免覆盖创建日期 BlogPost existingPost = blogPostServiceImpl.getBlogPost(blogPost.getId()); // 复制表单提交的字段 existingPost.setTitle(blogPost.getTitle()); existingPost.setText(blogPost.getText()); existingPost.setCategory(postCategoryServiceImpl.getPostCategory(blogPost.getCategoryId())); // 保留原有的创建日期 blogPostServiceImpl.updateBlogPost(existingPost); } return new ModelAndView("redirect:/"); }
额外修复:CreationDate的更新问题
无论用哪种方案,修改操作时都不要直接使用表单绑定的BlogPost对象执行更新,因为表单没有传递creationDate,会导致该字段被设为null覆盖数据库中的值。正确的做法是先从数据库查询出原对象,再将表单中的字段复制到原对象上,保留原有的creationDate(如方案2的修改逻辑)。
这样处理后,不仅能解决400绑定错误,还能避免创建日期被意外覆盖的问题。
内容的提问来源于stack exchange,提问作者Majid
相关产品推荐
相关产品推荐

