SpringMVC_Thymeleaf综合示例2
1. 登录
跳转至登录页面代码:
UserController.java
@RequestMapping("/")
public String showLogin(User user) {
return "login";
}
登录页面login.html:
<!DOCTYPE html>
<html th:replace="~{layout::standard('用户登录',~{::div})}"
xmlns:th="http://www.thymeleaf.org">
<head></head>
<body>
<div align="center">
<form th:action="@{/login}" method="post" th:object="${user}">
<label th:text="${session.loginMsg}"></label>
<table id="hor-minimalist-b">
<tbody>
<tr>
<td>用户名</td>
<td><input th:field="*{name}"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" th:field="*{password}"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登录"><input
type="button" value="注册"
th:onclick="|javascript:location.href='@{/register}';|"></td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
其中th:onclick
中可以使用一对|
对普通字符串与thymeleaf表达式进行拼接。否则我们要写成如下形式:
<input type="button" value="注册" th:onclick="'javascript:location.href=\''+@{/register}+'\';'">
这样会让整个表达式变得非常不清晰。
登录逻辑:
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String checkLogin(User user, HttpSession session) {
boolean flag = userService.checkLogin(user);
if (flag) {
session.setAttribute("loginUser", user);
session.removeAttribute("loginMsg");
return "redirect:/findAllUser";
} else {
session.setAttribute("loginMsg", "用户名密码错误");
return "redirect:/";
}
}
2. 注册新用户
首先增加跳转至注册页面的controller逻辑
public final static Map<String, String> map = new HashMap<>();
static {
map.put("swim", "游泳");
map.put("game", "电竞游戏");
map.put("run", "跑步");
map.put("travel", "旅游");
}
@ModelAttribute("hobbyMap")
public Map<String, String> showHobbies() {
return map;
}
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegister(User user) {
user.setGender(true);
return "register";
}
上述代码初始化了一个代表爱好的HashMap
集合,并使用@ModelAttribute("hobbyMap")
将该对象绑定到了模型对象中,方便在页面进行获取。
由于注册新用户的表单和更新用户信息的表单,从结构上来说都是一样的,我们可以利用Thymeleaf进行代码复用,首先在common.html
中定义用于提交注册或更新的代码段:
<div th:fragment="user(actionPath)" align="center">
<form th:action="${actionPath}" method="post" th:object="${user}">
<input type="hidden" th:field="*{id}">
<table id="hor-minimalist-b">
<tbody>
<tr>
<td>用户名</td>
<td><input th:field="*{name}"><span
th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" th:field="*{password}"><span
th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span></td>
</tr>
<tr>
<td>生日</td>
<td><input th:field="*{birthday}"><span
th:if="${#fields.hasErrors('birthday')}" th:errors="*{birthday}"></span></td>
</tr>
<tr>
<td>性别</td>
<td><input type="radio" th:field="*{gender}" th:value="1">男
<input type="radio" th:field="*{gender}" th:value="0">女</td>
</tr>
<tr>
<td>爱好</td>
<td><span th:each="kv:${hobbyMap}"><input
type="checkbox" th:field="*{hobbies}" th:value="${kv.key}">
<label th:for="${#ids.prev('hobbies')}" th:text="${kv.value}"></label>
</span></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="提交"></td>
</tr>
</tbody>
</table>
</form>
</div>
然后,在register.html中,对模板进行调用:
<!DOCTYPE html>
<html
th:replace="~{layout::standard('用户注册',~{common::user(@{/register})})}"
xmlns:th="http://www.thymeleaf.org">
<head></head>
<body>
</body>
</html>
actionPath
用来传入form表单的提交路径。th:object="${user}
用来声明表单所绑定的模型对象*{属性名}
用来获取表单绑定模型对象的属性值th:if="${#fields.hasErrors('属性名')}"
用来判断对应的属性是否存在错误消息th:errors="*{属性名}"
用来获取对应属性的错误消息
最后我们再来看用来生成多选框的这些代码:
<tr>
<td>爱好</td>
<td>
<span th:each="kv:${hobbyMap}">
<input type="checkbox" th:field="*{hobbies}" th:value="${kv.key}">
<label th:for="${#ids.prev('hobbies')}" th:text="${kv.value}"></label>
</span>
</td>
</tr>
th:each="kv:${hobbyMap}"
用来循环后台准备好的爱好集合,${hobbyMap}
为循环集合,kv
为循环变量。th:each
循环HashMap时,得到的循环单元为key-value
对(entry
)。- 由于多选框多半是由两个html标签组成的(一个
input
,一个文本),因此,代码中整个label
标签是为了给前面这个多选框生成后面显示的文本的。th:for="${#ids.prev('hobbies')}"
表示它将会依据id
查找某一个组件。id
值为多少,取决于${#ids.prev('hobbies')}
。${#ids.prev('hobbies')}
表示为上一个hobbies生成的id
值。其中#ids
为模板引擎的id生成器(内置对象),在th:each
循环运行后,它会为每一个html
组件(多选框)创建一个id值,分别为hobbies1
、hobbies2
…以此类推。我们调用prev()
方法来获取为hobbies
生成的上一个id值(#ids
相应的还有next()
方法,用来获取下一个生成的id值)。
User上的JSR303
校验:
public class User {
private Integer id;
@NotNull
@Size(min = 6, max = 8, message = "{name.size}")
private String name;
@NotNull
@Size(min = 6, max = 8, message = "{password.size}")
private String password;
private Boolean gender;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private String hobby;
private String[] hobbies;
// getter & setter
}
注册逻辑:
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(@Valid User user, Errors errors) {
if (errors.hasErrors()) {
return "register";
}
user.setHobby(ArrayUtils.toString(user.getHobbies()));
userService.save(user);
return "redirect:/findAllUser";
}
3. 更新用户信息
首先在listplus.html
页面增加更新操作按钮:
<input type="button" value="更新"
th:onclick="|javascript:location.href='@{/update?id=}${user.id}';|">
跳转至更新页面:
@RequestMapping(value = "/update", method = RequestMethod.GET)
public String showUpdate(int id, Model model) {
User user = userService.findById(id);
user.setHobbies(user.getHobby().split(","));
model.addAttribute(user);
return "update";
}
执行更新逻辑:
@RequestMapping(value = "/update", method = RequestMethod.POST)
public String update(@Valid User user, Errors errors) {
if (errors.hasErrors()) {
return "update";
}
user.setHobby(ArrayUtils.toString(user.getHobbies()));
userService.update(user);
return "redirect:/findAllUser";
}
4. 删除
在listplus.html
页面添加删除操作按钮:
<input type="button" value="删除"
th:onclick="|javascript:location.href='@{/delete?id=}${user.id}';|">
删除逻辑:
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String delete(int id) {
userService.delete(id);
return "redirect:/findAllUser";
}
5. 登录拦截
为了防止用户在未登录的情况,访问内部页面,我们可以使用Spring提供的拦截机制。
首先编写拦截逻辑:
LoginInterceptor.java
public class LoginInterceptors implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if (user == null) {
response.sendRedirect(request.getServletContext().getContextPath());
return false;//拦截请求
} else {
return true;//放行请求
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
然后在WebConfig中注册拦截器:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptors())
//拦截路径
.addPathPatterns("/**")
//不拦截路径
.excludePathPatterns("/", "/login");
}
er, Exception ex)
throws Exception {
}
}
然后在WebConfig中注册拦截器:
```java
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptors())
//拦截路径
.addPathPatterns("/**")
//不拦截路径
.excludePathPatterns("/", "/login");
}