------web 开发登录功能------
修改login.html文件:注意加粗部分为 msg 字符串不为空时候 才进行显示
<!DOCTYPE html> <!-- saved from url=(0050)http://getbootstrap.com/docs/4.0/examples/sign-in/ --> <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=""> <link rel="icon" href="http://getbootstrap.com/favicon.ico"> <title>Signin Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link href="/asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link href="/asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <form class="form-signin" 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> <p style="color: red;" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p> <label for="username" class="sr-only" th:text="#{login.username}">Email address</label> <input name="username" type="email" id="username" th:placeholder="#{login.username}" class="form-control" placeholder="Email address" required="" autofocus=""> <label for="inputPassword" th:text="#{login.password}" class="sr-only">Password</label> <input name="password" type="password" th:placeholder="#{login.password}" id="inputPassword" class="form-control" placeholder="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> </body></html>
添加一个controller 对应页面跳转
package com.lixuchun.springboot.controller; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.Map; @Controller public class LoginController { // @RequestMapping(value = "/user/login", method = RequestMethod.POST) @PostMapping(value = "/user/login") public String login(@RequestParam String username, @RequestParam String password ,Map<String, Object> map) { if (!StringUtils.isEmpty(username) && "123456".equals(password)) { // 登录成功 return "dashboard"; } else { map.put("msg", "用户登录失败!"); // 登录失败 return "login"; } } }
实现效果:填写正确情况下跳转到 dashboard.html页面中,错误情况下 返回login页面 并且显示 用户登录失败!
登录 开发期间模板引擎页面修改以后要实时生效
禁用模板引擎缓存:spring.thymeleaf.cache=false
修改页面完成以后 ctrl + f9 重新编译
当页面位于 dashboard 页面 按 F5 会有是否表单再次提交提示 (表单重复提交)
可以进行重定向防止重复提交
if (!StringUtils.isEmpty(username) && "123456".equals(password)) { // 登录成功 // 防止表单从夫提交 可以从定向导主页 return "redirect:/main.html"; } else { map.put("msg", "用户登录失败!"); // 登录失败 return "login"; }
修改Mvc View视图解析器
@Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter() { WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/index.html").setViewName("login"); registry.addViewController("/main.html").setViewName("dashboard"); } }; return adapter; }
如果这样修改了 直接访问 localhost:8080/main.html 就可以直接访问 登录功能没起到作用 那么引入拦截器
拦截器 编写拦截器controller 必须实现 HandlerInterceptor
pre 在登录之前进行检查:
package com.lixuchun.springboot.component; import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 进行登录检查 */ public class LoginHandlerIntercepter 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", "没有用户权限 请先登录");
// 没有登录跳转到 /index.html 页面 request.getRequestDispatcher("/index.html").forward(request, response); return false; } else { // 已经登录 放行请求 return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
Mvc 配置文件添加 Interceptor 组件 进行注册:
@Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter() { WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @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) { // 拦截任意请求 // 已经做好了静态资源的映射 *.css *.js 访问等 不需要做处理 registry.addInterceptor(new LoginHandlerIntercepter()).addPathPatterns("/**") .excludePathPatterns("/index.html", "/", "/user/login"); } }; return adapter; }
访问效果:在没有登录的情况下 访问 localhost:8080/main.html
登录后再次访问:左侧上面 test@test.com 为登录名称 dashboard 页面 [[${session.loginUser}]] 行内表达式取值
CRUD-员工列表
实验要求:
1) RestfulCRUD: CRUD 满足Rest风格
URI : /资源名称/资源标识 HTTP请求方式区分对资源的CRUD操作
/emp/{id}
2). 实验的请求架构
3).员工列表
抽取公共元素片段
<div th:fragment="copy">© 2011 thymes Virtual Grocery</div>
引入公共元素片段
<div th:insert="~{footer::copy}"></div>
~{templatename::selector} :模板名 :: 选择器
~{templatename::fragmentname}:模板名::片段名
三种引入公共片段th属性:
th:insert :将公共的片段整个插入到声明引用的div中
th:replace: 将声明引入的元素替换为公共片段
th:include:被引入的片段内容包含近这个标签中
如果使用th:insert 等属性进行引入 可以不用写 ~{}
行内写法加上 [[~{}]] , [(~{})] (转义不转义)
dashboard 页面定义公共片段头 th:fragment="topbar"
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a> </li> </ul> </nav>
list 页面头取法
<!-- 引入抽取的topbar -->
<!-- 模板名称:使用thymeleaf的配置规则进行解析 -->
<div th:replace="~{dashboard::topbar}"></div>
dashboard 页面定义公共片段侧边栏 id="sideBar"
<nav id="sideBar" class="col-md-2 d-none d-md-block bg-light sidebar" style="margin-top: 50px;"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> Dashboard <span class="sr-only">(current)</span> </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg> Orders </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg> Products </a> </li> <li class="nav-item"> <a class="nav-link" href="#" th:href="@{/emps}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> 员工管理 </a> </li> </ul> </div> </nav>
list 页面侧边栏取法
<!-- 引入侧边栏 --> <div th:replace="~{dashboard::#sideBar}"></div>
也可以单独提出一个bar文件 然后在各个页面进行调用
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- top bar --> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a> </li> </ul> </nav> <!-- side bar --> <nav id="sideBar" class="col-md-2 d-none d-md-block bg-light sidebar" style="margin-top: 50px;"> <div class="sidebar-sticky"> <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg> Products </a> </li> <li class="nav-item"> <a class="nav-link" href="#" th:href="@{/emps}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> 员工管理 </a> </li> <li class="nav-item"> <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg> Integrations </a> </li> </ul> </div> </nav> </body> </html>
页面进行调用 list 页面 效果和之前的效果一样
<!-- 引入抽取的topbar --> <!-- 模板名称:使用thymeleaf的配置规则进行解析 --> <div th:replace="commons/bar::topbar"></div>
<!-- 引入侧边栏 --> <div th:replace="commons/bar::#sideBar"></div>
引入片段也可以传入参数:
在 dashboard.html 页面引入左侧栏时候 传入参数
<div th:replace="commons/bar::#sideBar(activeUri='main.html')"></div>
在 bar.html 页面设置左侧栏时候 通过传过来的参数设置 是否高亮
<li class="nav-item"> <a class="nav-link active" href="#" th:href="@{/main.html}" th:class="${activeUri=='main.html'?'nav-link active':'nav-link'}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> Dashboard <span class="sr-only">(current)</span> </a> </li>
员工列表做数据展示:
list.html 页面编写
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
<div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;1000000px;height:1000000px;left:0;top:0"></div>
</div>
<div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;200%;height:200%;left:0; top:0">
</div>
</div>
</div> <h2><button class="btn btn-sm btn-success">员工添加</button></h2> <h2>Section title</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>options</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?'girl':'boy'"></td> <td th:text="${emp.department.departmentName}"></td> <td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td> <td> <button class="btn btn-sm btn-primary">编辑员工</button> <button class="btn btn-sm btn-danger">删除员工</button> </td> </tr> </tbody> </table> </div> </main>
做一个伪处理 employeeDao
package com.lixuchun.springboot.dao; import com.lixuchun.springboot.entities.Department; import com.lixuchun.springboot.entities.Employee; import org.springframework.stereotype.Repository; import java.util.ArrayList; import java.util.Date; import java.util.List; @Repository public class EmployeeDao { public Employee selectEmpById(Integer id) { return new Employee(); } public List<Employee> selectEmps() { List<Employee> empList = new ArrayList<Employee>(); for (int i = 0; i < 6; i++) { Employee emp = new Employee(i, "emp" + i, i + "emp@emp.com", 0, new Department(i, "dep"+ i), new Date()); empList.add(emp); } return empList; } }
employee / department
department private Integer id; private String departmentName; employee id lastName email gender department birth
EmpController 编写
@Autowired EmployeeDao employeeDao; // 查询所有员工返回列表页面 @GetMapping("/emps") public String list(Model model) { List<Employee> employees = employeeDao.selectEmps(); model.addAttribute("emps", employees); // classpath:/templates/xxx.html return "emp/list"; }
最后页面效果:
员工添加功能:
list 添加按钮 <a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">员工添加</a>
emp get 请求到员工添加页面
emp post 添加员工
其中部门为后台查出来的,前后台处理
<select class="form-control"> <option th:value="${dept.id}" th:each="dept:${depts}" th:text="dept.departmentName"></option> </select>
点击添加按钮 post 到 emp为添加功能
<form ah:action="@{/emp}" method="post">
员工添加controller
修改日期格式:spring.mvc.date-format=yyyy-MM-dd
员工修改:emp/id ------->get 方式
<a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${emp.id}">编辑员工</a>
页面需要回显:
th:value="${emp!=null}?${emp.lastName}">
th:checked="${emp!=null}?${emp.gender==1}" / gender==0
th:selected="${emp!=null}?${dept.id==emp.department.id}"
如何发送put请求,到员工修改controller
删除:delete请求
出现了按钮换行 因为 form的添加 所以进行修改
设置按钮del_uri
button 设置 :th:attr="del_uri=@{emp/} + ${emp.id}"
form 设置:id="deleteEmpForm"
最后效果: