Spring MVC @SessionAttributes
来源:原创 发布时间:2015-03-25 归档:springmvc
开发环境 :
JDK 7
Maven 3
Tomcat 7
Spring 4.1.5
Eclipse Luna
@SessionAttributes 注解在类级别上, 表明属性的作用域是 session。
下面用例将展示如何通过 @SessionAttributes 注解将一个属性放入 session 域, 以及放入 session 域后如何来删除这个属性。
示例代码片段 1
下面用例将展示如何通过 @SessionAttributes 注解将一个属性放入 session 域, 以及放入 session 域后如何来删除这个属性。
@Controller @SessionAttributes("randomCode") public class LoginController { private static final String MESSAGE = "message"; private static final String DEFAULT_USER = "游客"; /** * 登录页面 * * @param model * ModelMap * @return */ @RequestMapping(value = "/login", method = GET) public String login(ModelMap model) { model.addAttribute("randomCode", getRandomCode()); return "login"; } /** * 用户登录 * * @param randomCode * 验证码 * @param model * ModelMap * @param session * HttpSession * @param sessionStatus * SessionStatus * @param username * 用户名 * @param inputCode * 用户输入的验证码 * @return */ @RequestMapping(value = "/login", method = POST) public String login(@ModelAttribute("randomCode") String randomCode, ModelMap model, HttpSession session, SessionStatus sessionStatus, String username, String inputCode) { if (!randomCode.equals(inputCode)) { model.addAttribute(MESSAGE, "验证码错误, 请重新登录!"); return "login"; } if (!StringUtils.hasText(username)) { username = DEFAULT_USER; } session.setAttribute("currUser", username); sessionStatus.setComplete(); return "home"; } /** * 生成随机验证码 * * @return */ private String getRandomCode() { StringBuilder randomCode = new StringBuilder(); for (int i = 0; i < 4; i++) { randomCode.append(ThreadLocalRandom.current().nextInt(9)); } return randomCode.toString(); } }login.jsp
<body> <div id="form" align="center"> <div id="message">${message}</div> <form method="post"> <table> <tr> <td><label for="username">用户名 :</label></td> <td><input type="text" name="username" placeholder="给自己取一个名字吧"></td> </tr> <tr> <td><label for="inputCode">验证码 :</label></td> <td><input type="text" name="inputCode" placeholder="请输入右边的验证码"></td> <td><div id="randomCode">${randomCode}</div></td> </tr> <tr> <td> </td> <td><input type="submit" value="登 录"></td> </tr> </table> </form> </div> </body>
GET /login
处理方法 login ( GET ) 被执行, 首先是通过 getRandomCode() 产生一个 4 位数的随机数字作为验证码, 并通过 model.addAttribute("randomCode", getRandomCode()) 将产生的验证码放到模型中, 放到模型中的属性默认是 request 域, 即只在本次请求有效。
这里由于放入模型的 key 与 @SessionAttributes("randomCode") 的 key 一致, 因此 randomCode 属性并不放到 request 域, 而放到 session 域。
若需要将多个属性放到 session 域, 可以这样做, @SessionAttributes({"prop1", "prop2", "prop3"})。
处理方法 login ( GET ) 被执行, 首先是通过 getRandomCode() 产生一个 4 位数的随机数字作为验证码, 并通过 model.addAttribute("randomCode", getRandomCode()) 将产生的验证码放到模型中, 放到模型中的属性默认是 request 域, 即只在本次请求有效。
这里由于放入模型的 key 与 @SessionAttributes("randomCode") 的 key 一致, 因此 randomCode 属性并不放到 request 域, 而放到 session 域。
若需要将多个属性放到 session 域, 可以这样做, @SessionAttributes({"prop1", "prop2", "prop3"})。
用户名不是必须的, 输入验证码, 点登录或回车, 将发起 POST /login 请求。
处理方法 login ( POST ) 执行过程中, @ModelAttribute("randomCode") String randomCode, 是从 session 域中将 randomCode 属性取出。
若验证码正确, 将执行 HttpSession.setAttribute("currUser", username), 来将用户名放入到 HttpSession, 并且执行 SessionStatus.setComplete(), 作用效果如下。
处理方法 login ( POST ) 执行过程中, @ModelAttribute("randomCode") String randomCode, 是从 session 域中将 randomCode 属性取出。
若验证码正确, 将执行 HttpSession.setAttribute("currUser", username), 来将用户名放入到 HttpSession, 并且执行 SessionStatus.setComplete(), 作用效果如下。
<body> <h1>欢迎您, ${currUser}!${randomCode}</h1> <h3><a href="${pageContext.request.contextPath}/user/security">用户账户安全中心</a></h3> </body>
可以看到, ${currUser} 有值, ${randomCode} 没值, 这是因为执行 SessionStatus.setComplete() 后, 通过 @SessionAttributes 放入 session 中的属性清除了, 但不会清除 HttpSession 中的属性。
示例代码片段 2
下面用例将展示 @SessionAttributes 属性未初始化时, 如何防止 500 异常。
@Controller @RequestMapping("/user/security") @SessionAttributes("smsCode") public class UserAccountSecurity { @RequestMapping public String input() { return "input"; } /** * 发送短信验证码 * * @param model * ModelMap * @return */ @RequestMapping(value = "/sendSmsCode", method = GET) public String sendSmsCode(ModelMap model) { String smsCode = getRandomCode(); model.addAttribute("smsCode", smsCode); model.addAttribute("message", "验证码已发送, 请查收[" + smsCode + "]"); return "input"; } /** * 验证手机号码 * * @param smsCode * 系统产生的验证码 * @param model * ModelMap * @param sessionStatus * SessionStatus * @param inputCode * SessionStatus * @return */ @RequestMapping(value = "/verifyPhone", method = POST) public String verifyPhone(@ModelAttribute("smsCode") String smsCode, ModelMap model, SessionStatus sessionStatus, String inputCode) { if (!StringUtils.hasText(inputCode)) { model.addAttribute("message", "请输入验证码!"); } else { if (inputCode.equals(smsCode)) { sessionStatus.setComplete(); model.addAttribute("message", "绑定手机成功!"); } else { model.addAttribute("message", "验证码错误, 请重新输入!"); } } return "input"; } /** * 验证手机号码 * * @param model * ModelMap * @param sessionStatus * SessionStatus * @param inputCode * 用户输入的验证码 * @return */ @RequestMapping(value = "/verifyMobile", method = POST) public String verifyMobile(ModelMap model, SessionStatus sessionStatus, String inputCode) { if (model.containsAttribute("smsCode")) { String smsCode = (String) model.get("smsCode"); if (smsCode.equals(inputCode)) { sessionStatus.setComplete(); model.addAttribute("message", "绑定手机成功!"); } else { model.addAttribute("message", "验证码错误, 请重新输入!"); } } else { model.addAttribute("message", "请发送验证码到你的手机以完成验证!"); } return "input"; } /** * 产生随机数 * * @return */ private String getRandomCode() { StringBuilder randomCode = new StringBuilder(); for (int i = 0; i < 6; i++) { randomCode.append(ThreadLocalRandom.current().nextInt(9)); } return randomCode.toString(); } }input.jsp
<body> <div id="form" align="center"> <div id="message">${message}</div> <%-- <form action="${pageContext.request.contextPath}/user/security/verifyMobile" method="post"> --%> <form action="${pageContext.request.contextPath}/user/security/verifyPhone" method="post"> <input type="text" name="inputCode" placeholder="输入手机收到的验证码"> <span class="smsCode" onclick="javascript:window.location.href='${pageContext.request.contextPath}/user/security/sendSmsCode'"> 发送验证码</span> <br><input type="submit" value="提 交"> </form> </div> </body>
GET /user/security --> 页面如下
点击发送验证码, 再输入验证码提交。这是一个正常的流程。
下面来执行这样一个流程, 首先重启服务器 ( 必须重启 ), 不点击发送验证, 随便输入内容或不输入内容直接提交, 这时候报 HTTP Status 500 - Expected session attribute 'smsCode'
这是因为, 处理方法 verifyPhone 中的 @ModelAttribute("smsCode") String smsCode 引起的。
当不点击发送验证码时, 处理方法 sendSmsCode 没有执行, 那么这时候, @SessionAttributes("smsCode") smsCode 属性尚未被初始化, 这时候又期望通过 @ModelAttribute("smsCode") 从 session 中取得 smsCode 属性的值, 这样就会出问题。
将 input.jsp 中的 <%-- <form action="${pageContext.request.contextPath}/user/security/verifyMobile" method="post"> --%> 注释取消, 将下一行注释起来。再走一次同样的流程, 无需重启服务器, 发起 GET /user/security 请求, 不点击发送验证, 随便输入内容或不输入内容直接提交, 走 verifyMobile 处理方法, 这时候流程回归正常。
这里主要是通过 ModelMap 来处理, 除了 request 域, session 域里的属性, ModelMap 一样可以访问的到, 这样就可以防止 @SessionAttributes 空值引发的 500 异常。
点击发送验证码, 再输入验证码提交。这是一个正常的流程。
下面来执行这样一个流程, 首先重启服务器 ( 必须重启 ), 不点击发送验证, 随便输入内容或不输入内容直接提交, 这时候报 HTTP Status 500 - Expected session attribute 'smsCode'
这是因为, 处理方法 verifyPhone 中的 @ModelAttribute("smsCode") String smsCode 引起的。
当不点击发送验证码时, 处理方法 sendSmsCode 没有执行, 那么这时候, @SessionAttributes("smsCode") smsCode 属性尚未被初始化, 这时候又期望通过 @ModelAttribute("smsCode") 从 session 中取得 smsCode 属性的值, 这样就会出问题。
将 input.jsp 中的 <%-- <form action="${pageContext.request.contextPath}/user/security/verifyMobile" method="post"> --%> 注释取消, 将下一行注释起来。再走一次同样的流程, 无需重启服务器, 发起 GET /user/security 请求, 不点击发送验证, 随便输入内容或不输入内容直接提交, 走 verifyMobile 处理方法, 这时候流程回归正常。
这里主要是通过 ModelMap 来处理, 除了 request 域, session 域里的属性, ModelMap 一样可以访问的到, 这样就可以防止 @SessionAttributes 空值引发的 500 异常。
示例代码下载