【发布时间】:2015-06-29 20:41:19
【问题描述】:
我在 JBoss 7.2 上托管的 Spring MVC 4.0.6 遇到了一个非常奇怪的问题。更新现有用户时,提交的信息有时会传输到 POST RequestMapping 控制器方法(下面的 validateUpdate 方法)。
UserController.java
@Controller
@RequestMapping(value = "/security/user")
@SessionAttributes(value = {"userForm", "user"})
public class UserController {
private final String CREATE_VIEW = "user/createOrUpdate";
private final String READ_VIEW = "user/readOrDelete";
private final String UPDATE_VIEW = CREATE_VIEW;
private final String DELETE_VIEW = READ_VIEW;
@Autowired
private LocationService locationService;
@Autowired
private SecurityService securityService;
@Autowired
private UserValidator userValidator;
@InitBinder("userForm")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(userValidator);
binder.setDisallowedFields("strId");
}
@ModelAttribute("regions")
public List<Region> populateRegions() {
Locale locale = LocaleContextHolder.getLocale();
List<Region> regions = this.locationService.lstRegions(locale.getLanguage());
return regions;
}
@RequestMapping(value = "/update/{intUserId}", method = RequestMethod.GET)
public String updateUser(@PathVariable Integer intUserId, Model model) {
User user = this.securityService.findUserByPk(intUserId);
if (user != null) {
UserForm userForm = new UserForm();
userForm.setUser(user);
model.addAttribute(userForm);
}
return UPDATE_VIEW;
}
@RequestMapping(value = "/update/validate", method = RequestMethod.POST)
public String validateUpdate(@Valid @ModelAttribute("userForm") UserForm userForm,
BindingResult result,
Model model,
RedirectAttributes redirectAttributes,
SessionStatus status) {
return this.performCreateOrUpdateOperation(userForm, result, model, redirectAttributes, status);
}
private String performCreateOrUpdateOperation(
UserForm userForm,
BindingResult result,
Model model,
SessionStatus status) {
if(result.hasErrors()) {
return UPDATE_VIEW;
} else {
User user = userForm.getUser();
this.securityService.validateCreateOrUpdateUser(result, user);
if (result.hasErrors() == false) {
if (userForm.isNew()) {
this.securityService.addUser(user);
} else {
this.securityService.updateUser(user);
}
model.addAttribute(user);
status.setComplete();
return "user/success";
} else {
return UPDATE_VIEW;
}
}
}
}
表单豆
public class UserForm {
private String strUserIdToSearch = "";
private String strId = "0";
private String strUserId = "";
private String strFirstName = "";
private String strLastName = "";
private String strEmail = "";
private String strRegionId = "0";
private boolean booArchived = false;
public User getUser() {
User user = new User();
user.setIntId(Integer.valueOf(this.strId));
user.setStrUserId(this.strUserId);
user.setStrFirstName(this.strFirstName);
user.setStrLastName(this.strLastName);
user.setStrEmail(this.strEmail);
Region region = new Region(Integer.valueOf(this.strRegionId));
user.setRegion(region);
user.setBooArchived(this.booArchived);
return user;
}
public void setUser(User user) {
this.strUserIdToSearch = user.getStrUserId();
this.strId = String.valueOf(user.getIntId());
this.strUserId = user.getStrUserId();
this.strFirstName = user.getStrFirstName();
this.strLastName = user.getStrLastName();
this.strEmail = user.getStrEmail();
this.strRegionId = String.valueOf(user.getRegion().getIntId());
this.booArchived = user.getBooArchived();
}
...getters and setters...
}
JSP(为清晰起见删除了样式) crudMethod 是一个 JSP 标记,根据 ${requestScope['javax.servlet.forward.request_uri']}
返回“create”、“read”、“update”或“delete”<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<spring:url var="userFormAction" value="/rest/security/user/${crudMethod}/validate" />
<form:form id="userForm" modelAttribute="userForm" action="${userFormAction}" method="POST">
<form:hidden path="strId" />
<form:hidden path="strUserId" id="strUserId" />
<form:hidden path="strLastName" id="strLastName" />
<form:hidden path="strFirstName" id="strFirstName" />
<form:hidden path="strEmail" id="strEmail" />
<form:select id="strRegionId" path="strRegionId">
<form:option value="0" label="${strSelectRegionLabel}" />
<form:options items="${regions}" itemValue="intId" itemLabel="${strRegionLabel}" />
</form:select>
</form:form>
因此,当我提交表单时,例如,将区域更改为列表中的另一个 ID(比如说从 1 到 6)。有时有效,有时无效。通过“作品”,我的意思是我点击了成功页面,然后我再次返回查看用户。有时它保持在 1,有时它变为 6。
我找到了一种模式/解决方法,可以一直重现该问题:
- 加载更新表单(UserController > updateUser)
- 将区域从 1 更改为 6
- 单击保存。表单提交,因此正在调用 UserController.validateUser 方法。
- 获得成功页面。在那个成功页面上,我得到了一个指向用户读取操作的链接。单击该链接,我意识到该区域没有改变(主要问题)。阅读页面上有更新用户的链接。
- 重新执行与第 2 步完全相同的更改。
- 单击保存。表单提交,我得到了成功的页面视图。
- 再次单击读取超链接,现在我看到更改有效。
问题是:为什么?我错过了什么吗??
注意:与业务层无关。我已经测试过了,它很稳定。这肯定与我使用 Spring MVC 框架有关。
浏览器是IE11。
感谢您的帮助。
* 2015-06-29 更新 *
又搜索了几遍,我发现:
- 不工作时,请求Content-Length头值为0。
- 当它工作时,有一个值(例如:146)。
-
请求消息体总是正确的,像这样:
strId=THE_ID&strUserId=THE_USERID&strLastName=THE_LASTNAME&strFirstName=THE_FIRSTNAME&strEmail=THE_EMAILADDRESS&strRegionId=THE_REGIONID&booArchived=false
请注意,“THE_REGIONID”每次都很好。
相关资源
【问题讨论】:
-
它与您的浏览器有关,与缓存有关。我强烈建议使用 post-redirect-get 模式。更新后重定向到成功页面而不是渲染它。所以不要返回
user/success,而是返回redirect:/url/to/success.jsp-page。成功页面的控制器应该再次从数据库中检索用户(而不是将其添加到模型中),如果您真的不想将用户置于 flash 范围内,那么它将在重定向中存活。 -
我先使用了flash属性,但是如果用户出于某种原因刷新成功页面,内容正在被清除,所以这不是一个选项。将尝试实现 post-redirect-get 模式,然后按照您的建议从数据库中重新加载元素。谢谢。
-
最后,我怀疑它与缓存有关。我在方法“validateUpdate”中添加了一个断点,以便立即查看提交的值。即使在这一点上,该地区的价值也保持不变。如果它工作但没有正确显示,它会坚持你的缓存想法......但它甚至没有被正确提交......
-
如果它没有更新,您的绑定或 UserForm 中的映射一定有问题(为此我建议使用映射框架而不是手工劳动或直接使用用户)。
-
能否详细介绍一下“映射框架”?谢谢你的帮助。我已经查看了整个日志,我没有看到 Spring 有任何看起来异常的东西。我的印象是它与过滤器有关,甚至可能是最糟糕的......我的服务器(预身份验证)......但话又说回来,我在日志中没有看到任何关于它的痕迹......服务器在调试模式。
标签: spring spring-mvc httpsession