前端使用thymeleaf模板引擎,后端使用Springboot,暂时没有加入数据库,使用map模拟数据库中的数据。此处关注点是前后端如何进行数据传输,暂时没有使用AJAX的方式。
1.项目结构(spring-boot-04-web-restfulcrud):
component包 :包括一个自定义的登录拦截器和国际化版本处理器
config包:存放配置类,在配置类中注入了一个自定义的处理器映射器和注入了国际化版本处理器
Controller层:用于处理请求;
dao层 :用于存放数据访问的方法;
entity包 :存放实体类
2.先看componet和config中的内容
下面是LoginHandlerInterceptor,要实现HandlerInterceptor 接口,这里是从session域中获取是否有登陆信息,以此判断用户是否登录:
public class LoginHandlerInterceptor implements HandlerInterceptor {
//目标方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if(user==null){
System.out.println("======进入前置处理器======");
//没有登录
request.setAttribute("msg","没有权限请先登录");//添加提示信息
//转发
request.getRequestDispatcher("/index.html").forward(request,response);
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 {
}
}
然后是MyLocaleResolver ,需要实现LocaleResolver 接口,这里是从请求链接中查看是否有携带 l 参数,l 参数携带的需要转换的国际化信息:
public class MyLocaleResolver implements LocaleResolver {
//解析区域信息
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取'l'参数
String l = request.getParameter("l");
//获取系统当前的语言环境(如果人为选择中语言版本,则返回人为选择的;否则返回系统默认的)
Locale locale = Locale.getDefault();
//l不为空
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
下面是一个登录表单,最下面切换语言的链接中携带了 ’ l ’ 参数:
<form class="form-signin" action="dashboard.html" th:action="@{/user/login}" method="post">
<img class="mb-4" src="asserts/img/bootstrap-solid.svg" th:src="@{/asserts/img/bootstrap-solid.svg}" alt=""
width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
<!--判断是否有返回msg(通过调用thymeleaf的内置对象方法判断msg是否为空)-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
<label class="sr-only" th:text="#{login.username}">Username</label>
<input type="text" name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}"
required="" autofocus="">
<br/>
<label class="sr-only" th:text="#{login.password}">Password</label>
<input type="password" name="password" class="form-control" placeholder="Password"
th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> [[#{login.remember}]]
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
</form>
通过一个配置类,将我们自定义的处理对象注入到容器中:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//注入对MVC的配置
@Bean
public WebMvcConfigurer webMvcConfigurer() {
WebMvcConfigurer configurer = new WebMvcConfigurer() {
//视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
registry.addViewController("/main.html").setViewName("dashboard");
}
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//已经做好了静态资源的映射(指定需要拦截的路径和不需要拦截的路径)
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").
excludePathPatterns("/index.html","/","/user/login","/asserts/**","/webjars/**");
}
};
return configurer;
}
//注入国际化组件
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
3.Controller和前端的数据交互
登录逻辑处理:
@Controller
public class LoginController {
//验证登录(此处只是模拟登录,用户名不限 ,只要密码为123456即视为合法用户)
@PostMapping(value = "/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String,Object> map, HttpSession session){
if(!StringUtils.isEmpty(username)&&"123456".equals(password)){
//用户合法,将用户添加到session域中
session.setAttribute("loginUser",username);
//使用重定向防止表单重复提交
return "redirect:/main.html";
}else {
//登录失败
map.put("msg","用户名密码错误");
return "login";
}
}
}
此处使用的Restful风格的请求方式,定义如下:
增删改查控制器代码:
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
//查询全部用户
@GetMapping(value = "/emps")
public String list(Model model){
//查出数据
Collection<Employee> employees = employeeDao.getAll();
//放在请求域中
model.addAttribute("emps",employees);
return "list";
}
//查出部门信息(用于回显)并且来到员工添加页面
@GetMapping("/emp")
public String toAddPage(Model model){
//来到添加页面,查出所有的部门,在页面显示
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
return "emp/add";
}
//员工添加
//SpringMVC自动将请求参数和入参对象的属性进行一一绑定;要求请求参数的名字和javaBean入参的对象里面的属性名是一样的
@PostMapping("/emp")
public String addEmp(Employee employee){
//来到员工列表页面
System.out.println("保存的员工信息:"+employee);
//保存员工
employeeDao.save(employee);
// redirect: 表示重定向到一个地址(再发一次请求) /代表当前项目路径
return "redirect:/emps";
}
//来到修改页面,查出当前员工,在页面回显
@GetMapping("/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id, Model model){
Employee employee = employeeDao.get(id);
model.addAttribute("emp",employee);
//页面要显示所有的部门列表
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts",departments);
//回到修改页面(add是一个修改添加二合一的页面);
return "emp/add";
}
//员工修改和添加二合一(有id则为修改;没有则为添加,系统会自动生成id)
@PutMapping("/emp")
public String updateEmployee(Employee employee){
System.out.println("修改的员工数据:"+employee);
employeeDao.save(employee);
return "redirect:/emps";
}
//员工删除
@DeleteMapping("/emp/{id}")
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
}
展示数据的前端表格:
<table class="table table-striped table-sm">
<!--表头-->
<thead>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birthday</th>
<th>操作</th>
</tr>
</thead>
<!--表数据-->
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.id}"></td>
<!--行内表达式取得数据-->
<td>[[${emp.lastName}]]</td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender}==0?'女':'男'"></td>
<td th:text="${emp.department}"></td>
<td th:text="${#dates.format(emp.birth,'yyyy-MM-dd HH:mm')}"></td>
<td>
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
<!--这种方式如果有多个删除就要写多个表单-->
<!--<form th:action="@{/emp/}+${emp.id}" method="post">
<input type="hidden" name="_method" value="delete">
<button type="submit" class="btn btn-sm btn-danger">删除</button>
</form>-->
<!--通过th:attr自定义属性-->
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>
</td>
</tr>
</tbody>
</table>
因为是restful风格,编辑和删除请求的uri是一样的,不同的地方在请求方式。编辑是get请求,删除是delete请求,a标签默认提交get请求。所以我们需要将删除的请求方式修改为delete方式的请求。如下:
主要思路是给按钮添加自定义属性:
th:attr="del_uri=@{/emp/}+${emp.id}"
前面的代码已经给出,同时给这个按钮绑定一个方法,当点击按钮时触发上面的代码:获取到一个定义好的表单(表单中已经修改了请求方式为delete),然后给表单添加action属性,值是我们之前在按钮中定义的属性值,然后提交该表单。