SpringBoot2.0(4)wep开发(2)-----实现增删改查+登陆拦截+页面国际化功能
(1)默认登陆首页
首先要想访问首页,两种实现方式
a.使用@Controller的方式,这也是最简单的方式实现页面访问
@Controller public class loginController { // // @GetMapping(value = {"/"}) // @PutMapping // @DeleteMapping //@RequestMapping(value = "/user/login",method = RequestMethod.POST) @RequestMapping(value = {"/"}) public String login(){ return "login"; } }
b.写配置类,其实就是通过
addViewControllers添加跳转映射
@Configuration public class webMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/main.html").setViewName("dashboard"); } }
(2)登陆页面的国际化功能
具体实现
a、
编写国际化配置文件,抽取页面需要显示的国际化消息
因为
SpringBoot
自动配置好了管理国际化资源文件的组件,下面看一下源码了解下具体是怎么配置的
@ConfigurationProperties(prefix = "spring.messages") public class MessageSourceAutoConfiguration { /** * Comma‐separated list of basenames (essentially a fully‐qualified classpath * location), each following the ResourceBundle convention with relaxed support for * slash based locations. If it doesn't contain a package qualifier (such as * "org.mypackage"), it will be resolved from the classpath root. */ private String basename = "messages"; //我们的配置文件可以直接放在类路径下叫messages.properties; @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(this.basename)) { //设置国际化资源文件的基础名(去掉语言国家代码的) messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(this.basename))); } if (this.encoding != null) { messageSource.setDefaultEncoding(this.encoding.name()); } messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale); messageSource.setCacheSeconds(this.cacheSeconds); messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat); return messageSource; }
b、 去前端页面抽取国际化的值,SpringBoot已经添加了Thymeleaf解析,
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Signin Template for Bootstrap</title> <!-- Bootstrap core CSS --> <!--把webjars的bootstrap框架引进来解析Thymeleaf模板--> <link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.1.0/css/bootstrap.css}" rel="stylesheet"> <!-- Custom styles for this template --> <!--使用Thymeleaf模板引擎把自定义的界面样式引进来--> <link href="asserts/css/signin.css" th:href="@{asserts/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <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.tips}">Please sign in</h1> <p style="color: red;" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p> <label class="sr-only" th:text="#{login.username}">Username</label> <!--作为可与服务器交互数据的HTML元素的服务器端的标示,比如input、select、textarea、 和button等。我们可以在服务器端根据其Name通过Request.Params取得元素提交的值。--> <input type="text" name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus=""> <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="@{/login.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a> </form> </body> </html>
c 、 根据浏览器语言设置的信息切换国际化;
通过国际化Locale(区域信息对象)、LocaleResolver(获取区域信息对象)来实现
自己不用做任何配置
@Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") public LocaleResolver localeResolver() { if (this.mvcProperties .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver(this.mvcProperties.getLocale()); } AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); return localeResolver; } 默认的就是根据请求头带来的区域信息获取Locale进行国际化
d 、 点击链接切换页面显示语言
在登陆页面中设置了如下
<a class="btn btn-sm" th:href="@{/login.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/login.html(l='en_US')}">English</a>
所以此时的“中文”“English”两个显示其实是带了链接的
/** * 在链接上带上区域信息 */ public class myLocaleResolver implements LocaleResolver{ @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String l = httpServletRequest.getParameter("l"); Locale locale = Locale.getDefault(); if(!StringUtils.isEmpty(l)){ String[] split = l.split("_"); locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
然后在配置类文件中调用该方法,并添加到容器中
@Bean //添加到容器中 public LocaleResolver localeResolver() { return new myLocaleResolver(); }
(3)登陆功能
开发期间模板引擎页面修改以后,要实时生效
a、禁用模板引擎的缓存,在application.properties文件中做如下配置
#禁用缓存 spring.thymeleaf.cache=false
b、前端页面修改完成以后ctrl+f9:重新编译;
这样就不用总是重新启动项目
然后就是登陆的控制器
@Controller public class loginController { //@RequestMapping(value={"/user/login"},method = RequestMethod.POST) @PostMapping(value={"/user/login"}) //@RequestParam明确是从请求参数中获取值 //httpSession是存放登录过的用户信息,用户登录过以后就在session中存在 public String login(@RequestParam("username") String username, @RequestParam("password") String password, Map<String,Object> map, HttpSession httpSession){ if(!StringUtils.isEmpty(username)&& "111111".equals(password)){ httpSession.setAttribute("loginUser",username); return "redirect:/main.html"; }else { map.put("msg","用户名密码错误"); return "login"; } } }
登陆错误消息的显示,前端
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
注意“msg”要前端和后端对应
4)登陆拦截
首先编写拦截器的配置
package com.springbootdemo.springbootdemo04webrestfulcrud.conponent; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 拦截器登陆检查 */ 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){ request.setAttribute("msg","没有权限请登录"); request.getRequestDispatcher("/").forward(request,response); //转到请求转发器并得到请求和响应 这个地方的请求转发只能写映射的url,不能直接写登录页 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 { } }
然后在配置类的文件中注册拦截器
/** * 拦截器 * @param registry */ //@Bean @Override public void addInterceptors(InterceptorRegistry registry) { //在SpringBoot中已经做好了静态资源映射了 registry.addInterceptor(new loginHandlerInterceptor()).addPathPatterns("/**") .excludePathPatterns("/login.html ", "/", "/user/login","login.html?l=zh_CN","login.html?l=en_US"); }
(5)增删改查
a、thymeleaf公共页面元素抽取
这个时候增删改查页面会有重复的元素,就使用thymeleaf来抽取公共页面元素,方法:
1、抽取公共片段 <div th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </div> 2、引入公共片段 <div th:insert="~{footer :: copy}"></div> ~{templatename::selector}:模板名::选择器 ~{templatename::fragmentname}:模板名::片段名 3、默认效果: insert的公共片段在div标签中 如果使用th:insert等属性进行引入,可以不用写~{}: 行内写法可以加上:[[~{}]];[(~{})];三种引入公共片段的th属性:
th:insert:将公共片段整个插入到声明引入的元素中
th:replace:将声明引入的元素替换为公共片段
th:include:将被引入的片段的内容包含进这个标签中
员工的添加,前端
<div th:replace="commons/bar::topbar"></div> <div class="container-fluid"> <div class="row"> <!--引入侧边栏--> <div th:replace="commons/bar::#sidebar(activeUri='emps')"></div> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <!--需要区分是员工修改还是添加;--> <form th:action="@{/emp}" method="post"> <!--发送put请求修改员工数据--> <!-- 1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的) 2、页面创建一个post表单 3、创建一个input项,name="_method";值就是我们指定的请求方式 --> <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/> <input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}"> <div class="form-group"> <label>LastName</label> <input name="lastName" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${emp.lastName}"> </div> <div class="form-group"> <label>Email</label> <input name="email" type="email" class="form-control" placeholder="[email protected]" th:value="${emp!=null}?${emp.email}"> </div> <div class="form-group"> <label>Gender</label><br/> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender==1}"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender==0}"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label>department</label> <!--提交的是部门的id--> <select class="form-control" name="department.id"> <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option> </select> </div> <div class="form-group"> <label>Birth</label> <input name="birth" type="text" class="form-control" placeholder="zhangsan" th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"> </div> <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添加</button> </form> </main> </div> </div>
编辑和删除
<body> <!--引入抽取的topbar--> <div th:replace="commons/bar::topbar"></div> <div class="container-fluid"> <div class="row"> <!--引入侧边栏--> <div th:replace="commons/bar::#sidebar(activeUri = 'emps')"></div> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2> <!--a标签下的超链接默认的方法是GET方式--> <a class="btn btn-sm btn-success" href="/emp" th:href="@{/emp}">员工添加</a> </h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>#</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>department</th> <th>birth</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}"></td> <td th:text="${emp.department.departmentName} == 0?'女':'男'"></td> <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td> <td> <!--小号的primary样式的按钮 primary是bootstrap的样式--> <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a> <button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button> </td> </tr> </tbody> </table> </div> </main> <form id="deleteEmpForm" method="post"> <input type="hidden" name="_method" value="delete"/> </form> </div> </div>
后端
package com.springbootdemo.springbootdemo04webrestfulcrud.controller; import com.springbootdemo.springbootdemo04webrestfulcrud.dao.DepartmentDao; import com.springbootdemo.springbootdemo04webrestfulcrud.dao.EmployeeDao; import com.springbootdemo.springbootdemo04webrestfulcrud.entities.Department; import com.springbootdemo.springbootdemo04webrestfulcrud.entities.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.jws.WebParam; import java.util.Collection; @Controller public class employeeController { @Autowired EmployeeDao employeeDao; @Autowired DepartmentDao departmentDao; //查询所有员工返回列表页面 @GetMapping("/emps") //@RequestMapping(value = "/emps",method = RequestMethod.GET) public String list(Model model){ //获取所有的员工信息 Collection<Employee> employees =employeeDao.getAll(); //将员工信息放在请求域中共享 model.addAttribute("emps",employees); //thymeleaf默认拼串 //默认找到classpath:/templates/xxx.html return "emp/list"; } //跳转到员工添加页面 @GetMapping("/emp") //与html中的 th:href="@{/emp}"映射相对应 public String toAddPage(Model model){ //来到员工添加页面,查询员工部门信息显示在页面上 //获取部门并添加到页面中 Collection<Department> departments =departmentDao.getDepartments(); model.addAttribute("depts",departments); return "emp/add"; } //实现员工添加功能 //SpringMVC自动将请求参数和入参对象的属性进行一一绑定,要求请求参数的名称和JavaBean入参的对象里面的属性名称一一对应 //就是通过Bean添加到容器中的对象的名称与前端页面中的name属性里面的参数名称相同 @PostMapping("/emp") public String addEmp(Employee employee){ //redirect 是重定向 /指的是当前项目下的路径 //forward 是转发到某个地址 //保存员工 employeeDao.save(employee); 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; @PutMapping("/emp") public String updateEmployee(Employee employee){ employeeDao.save(employee); return "redirect:/emps"; } //员工删除 @DeleteMapping("/emp/{id}") public String deleteEmployee(@PathVariable("id") Integer id){ employeeDao.delete(id); return "redirect:/emps"; } }其实增删改查的过程中注意前后端的对应即可