th:action="@{/user/login}"
将表单数据提交至controller层中
@RequestMapping("/user/login")
的方法。
@RequestMapping("/user/login")
public String login(Model model, HttpSession session,
@RequestParam("username") String username ,
@RequestParam("password") String password){
if(username !=null && "123456".equals(password)){
System.out.println("username= " + username);
System.out.println("密码:" + password);
//将值(用户名)传进来
session.setAttribute("loginUser", username);
return "redirect:/main.html";
}else{
model.addAttribute("msg","用户名或密码错误!");
return "index";
}
}
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
如果后端数据判断显示为null或者密码错误。通过上述的语法就可以实现将数据回显出来,而不是一开始就显示数据
其中输入框提示的信息我们使用国际化的技术,能够实现页面的语言自由切换
<form class="form-signin" th:action="@{/user/login}">
<img class="mb-4" src="https://getbootstrap.com/docs/4.0/assets/brand/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的值为空才显示
-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
<label for="inputEmail" class="sr-only">[#{login.username}]</label>
<input type="email" id="inputEmail" class="form-control" th:name="username" th:placeholder="#{login.username}" required autofocus>
<label for="inputPassword" class="sr-only">[#{login.password}]</label>
<input type="password" id="inputPassword" th:name="password" class="form-control" th:placeholder="#{login.password}" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" th:text="#{login.remeber}">
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.tip}"></button>
<p class="mt-5 mb-3 text-muted">© 2022-2023</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>
通过<label for="inputEmail" class="sr-only">[#{login.username}]</label>
显示提示语。
application.properties
配置文件中,通过spring.messages.basename=i18n.login
开启我们配置的国际化配置
//往容器中注入组件
//自定义的国际化组件就会生效
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
config
包下创建MyLocaleResolver
类实现国际化的接口public class MyLocaleResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
//获取请求的语言参数
String language = httpServletRequest.getParameter("l");
Locale locale = Locale.getDefault(); //如果没有就使用
//如果请求的链接携带了国际化的参数
if(!StringUtils.isEmpty(language)){
//zh_CN
String[] split = language.split("_");
//国家 ,地区
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
login.properties
、 login_en_US.properties
、 login_zh_CN.properties
从而实现国际化的要求为了防止恶意的跳转,我们需要在用户未登录时自动拦截页面,使其跳转到登录页面,从而防止用户未登录就操作
config
包下的myConfig
类中添加拦截器配置//添加拦截器配置
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
//登录拦截器 ,拦截哪些选择 ,排除哪些选择
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html",
"/user/login","/","/css/**","/dist/**","/docs.4.0/**","/js/**","/img/**"
,"/index.html(l='zh_CN')","/index.html(l='en_US')","/user/LogOut");
}
config
包下同样创建LoginHandlerInterceptor
类,使其实现拦截器接口。从而实现preHandle方法public class LoginHandlerInterceptor implements HandlerInterceptor {
//登录成功之后将用户的信息session传过来
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功,获取用过户的session
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser == null) {
request.setAttribute("msg", "没有权限,请登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
}
return true;
}
}
<!doctype html >
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta 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" th:href="@{/docs/4.0/assets/img/favicons/favicon.icof}">
<title>Dashboard Template for Bootstrap</title>
<link rel="canonical" href="https://getbootstrap.com/docs/4.0/examples/dashboard/">
<!-- Bootstrap core CSS -->
<link th:href="@{/dist/css/bootstrap.min.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link th:href="@{/docs/4.0/examples/dashboard/dashboard.css}" rel="stylesheet">
</head>
<body>
<!--顶部导航栏-->
<div th:insert="~{common/common::topbar}"></div>
<!--侧边栏-->
<div class="container-fluid">
<div class="row">
<!--侧边栏-->
<div th:insert="~{common/common::sidebar(active='main.html')}"></div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<button class="btn btn-sm btn-outline-secondary">Share</button>
<button class="btn btn-sm btn-outline-secondary">Export</button>
</div>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
<span data-feather="calendar"></span>
This week
</button>
</div>
</div>
<canvas class="my-4" id="myChart" width="900" height="380"></canvas>
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../static/js/vendor/jquery-slim.min.js"></script>')</script>
<script src="../static/js/vendor/popper.min.js"></script>
<script src="../static/dist/js/bootstrap.min.js"></script>
<!-- Icons -->
<script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script>
<script>
feather.replace()
</script>
<!-- Graphs -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.7.1/dist/Chart.min.js"></script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
</script>
</body>
</html>
通过后端登录成功将数据return "redirect:/main.html";
重定向main.html,但是main.html这个地址是我们虚构的首页地址,以防止登录成功将用户信息传入导致用户信息泄露。
只需要在自定义配置中配置即可
@Configuration
public class MyConfig implements WebMvcConfigurer {
/*
配置根目录下的东西可以在这里配置
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
/*将/main.html作为一个跳板,放置用户信息被揭密,*/
registry.addViewController("/main.html").setViewName("dashboard");
}
}
通过registry.addViewController("/main.html").setViewName("dashboard");
调用main.html就可以跳转至dashboard
首页
list页面作为数据显示的页面,它可以将我们后台传过来的数据通过Thymeleaf
语法,将数据显示到后台
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>Section title</h2>
<a class="btn btn-sm btn-success" th:href="@{/to/add}">添加员工</a>
<div class="table-responsive">
<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>birth</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="li : ${list}">
<td th:text="${li.getId()}"></td>
<td th:text="${li.getLastName()}"></td>
<td th:text="${li.getEmail()}"></td>
<td th:text="${li.getGender() == 0 ? '女' : '男'}" ></td>
<td th:text="${li.getDepartment().getDepartmentName()}"></td>
<td th:text="${#dates.format(li.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
<td>
<a class="btn btn-sm btn-primary" th:href="@{'/updateEmp/'+${li.getId()}}">修改</a>
<a class="btn btn-sm btn-danger" th:href="@{'/deleteEmp/'+${li.getId()}}">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</main>
后端通过用户点击员工管理
<li class="nav-item">
<a th:class="${active=='list.html' ? 'nav-link active' :'nav-link'}" th:href="@{/list}">
<span data-feather="users"></span>
员工管理
</a>
</li>
,请求就会跳转至controller
匹配相应的请求映射
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao;
@RequestMapping("/list")
public String list(Model model){
Collection<Employee> list = employeeDao.getAllEmployee();
model.addAttribute("list",list);
return "list";
}
后端通过Model将数据回显给list页面
在list页面。通过Thymeleaf
语法<tr th:each="li : ${list}">
将数据显示出来
<td th:text="${li.getId()}"></td>
<td th:text="${li.getLastName()}"></td>
<td th:text="${li.getEmail()}"></td>
<td th:text="${li.getGender() == 0 ? '女' : '男'}" ></td>
<td th:text="${li.getDepartment().getDepartmentName()}"></td>
<td th:text="${#dates.format(li.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
<td>
<a class="btn btn-sm btn-primary" th:href="@{'/updateEmp/'+${li.getId()}}">修改</a>
<a class="btn btn-sm btn-danger" th:href="@{'/deleteEmp/'+${li.getId()}}">删除</a>
</td>
</tr>
这样,我们请求就能转发过来,用户就能看到数据
同时在list页面,我们也会有实现curd的跳转键
对于delete功能,这里没什么好说的,仅仅通过点击用户后面对应的标签,将要删除的员工的id作为请求参数传到后端即可th:href="@{'/updateEmp/'+${li.getId()}}"
后端通过匹配请求映射将功能实现
@RequestMapping("/deleteEmp/{id}")
public String deleteEmp(@PathVariable("id")Integer id){
employeeDao.deleteEmp(id);
return "redirect:/list";
}
实现update功能相较于delete功能是比较麻烦的
同样需要将要修改的员工id作为请求参数传入后端th:href="@{'/updateEmp/'+${li.getId()}}"
,然后后端通过查询,获取该员工的所有信息,然后通过Model
将数据共享至update页面
@RequestMapping("/updateEmp/{id}")
public String update(Model model,@PathVariable("id") Integer id){
System.out.println("id==" + id);
Employee employee = employeeDao.getEmployeeById(id);
System.out.println("emp == " + employee);
model.addAttribute("emp",employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "/updateEmp";
}
在updateEmp页面,需要将数据回显出来
<!--表单提交-->
<form class="form-horizontal" role="form" th:action="@{/updateSucc}" method="post" >
<input type="hidden" name="id" th:value="${emp.getId()}">
<div class="form-group">
<label for="lastName" class="col-sm-2 control-label">名字</label>
<div class="col-sm-10">
<input th:value="${emp.getLastName()}" type="text" class="form-control" id="lastName" name="lastName"
placeholder="请输入名字">
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input th:value="${emp.getEmail()}" type="text" class="form-control" id="Email" name="Email"
placeholder="请输入邮箱">
</div>
</div>
<div class="form-group">
<label for="gender">性别</label>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==1}" type="radio" class="form-check-input" name="gender" value="1" >
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==0}" type="radio" class="form-check-input" name="gender" value="0" >
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>Department</label>
<select class="form-control" name="department.id">
<option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
</div>
<div class="form-group">
<label for="birth" class="col-sm-2 control-label">birth</label>
<div class="col-sm-10">
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" type="text" class="form-control" id="birth" name="birth"
placeholder="请输入生日">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox"> 请记住我
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">登录</button>
</div>
</div>
</form>
通过隐藏域<input type="hidden" name="id" th:value="${emp.getId()}">
可以将后端传入的信息获取到
因为我们共享数据时是通过model.addAttribute("emp",employee);,所以在获取,然后在<input th:value="
对于复杂的比如department
<select class="form-control" name="department.id">
<option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
可以先通过deparment.id
获取全部的信息,然后通过匹配emp.getDepartment().getId()
获取将表单展示出来。通过th:value="${dept.getId()}"
将我们需要的数据显示到页面
然后通过请求form表单的th:action="@{/updateSucc}"
提交表单至后端.
后端通过请求映射匹配到相应的请求方法
@PostMapping("/updateSucc")
public String updateSuccess(Employee employee){
employeeDao.save(employee);
return "redirect:/list";
}
然后进行修改,最后通过重定向将请求跳转至原来的页面
用户通过点击添加员工将请求发送到后端,后端通过请求映射匹配到相应的请求方法,实现添加功能
<a class="btn btn-sm btn-success" th:href="@{/to/add}">添加员工</a>
@GetMapping("/to/add")
public String addEmp(Model model){
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "addEmp";
}
后台页面与update页面类似