Spring Boot+Thymeleaf环境下@ModelAttribute绑定对象取值为null求助
HTTP协议本身是无状态的,afterClickingSearch方法中绑定的searchObject仅在当次请求返回的booking_confirmation页面渲染时有效,当后续发起/navUserRegisterPage、/reserveUser请求时,之前的Hotel对象数据没有被持久化传递,Spring MVC会自动创建一个空的Hotel对象注入到reserveUserHandler的参数中,因此调用getHotel()返回null。
方案1:通过@SessionAttributes实现跨请求共享(最适配多步预订流程)
在Controller类上添加@SessionAttributes("searchObject")注解,标注该模型属性会自动同步到Session中,跨请求复用:
@Controller @SessionAttributes("searchObject") // 新增这行注解 public class HotelController { // 原有类代码保持不变 }
使用完成后可在最终确认方法中添加SessionStatus参数清理会话数据,避免无效数据占用:
@PostMapping("/reserveUser") public String reserveUserHandler(@ModelAttribute("userObj") User user, @ModelAttribute("searchObject") Hotel hotel, Model model, SessionStatus sessionStatus) { // 新增SessionStatus参数 // 原有业务逻辑不变 sessionStatus.setComplete(); // 业务处理完成后清理Session中存储的searchObject return "confirmation_page"; }
同时调整navUserRegisterPage方法,确保跳转注册页时模型中存在searchObject:
@GetMapping("/navUserRegisterPage") public String userRegisterPage(Model model,@ModelAttribute("searchObject") Hotel hotel){ User user = new User(); model.addAttribute("userObj", user); model.addAttribute("searchObject",hotel); return "user_registerForm"; }
方案2:通过隐藏域传递参数(适合敏感程度低、字段少的场景)
在用户注册表单user_registerForm中添加隐藏输入框,把Hotel对象需要的属性随用户注册表单一起提交,Thymeleaf示例代码如下:
<form th:action="@{/reserveUser}" th:object="${userObj}" method="post"> <!-- 原有用户信息输入框代码保持不变 --> <!-- 新增Hotel属性隐藏域 --> <input type="hidden" name="city" th:value="${searchObject.city}"> <input type="hidden" name="hotel" th:value="${searchObject.hotel}"> <!-- 其他需要的Hotel属性都按照该格式添加隐藏域 --> <button type="submit">提交</button> </form>
该方案无需占用Session资源,但是要注意后端需要对提交的Hotel参数做合法性校验,避免用户篡改数据。
方案3:使用FlashAttribute传递(仅适用于重定向场景的一次性参数传递)
如果/availabilityResult返回后是重定向到/navUserRegisterPage,可以通过RedirectAttributes的addFlashAttribute方法传递Hotel对象,FlashAttribute的参数只会存在一次,跳转后自动销毁。
内容的提问来源于stack exchange,提问作者havegudday

