【Spring Boot】6.web開發2

簡介

前面咱們已經進行了一些前置的操做,好比配置本地化、登陸驗證等。這一節將會作一些稍微有點難度的操做。javascript

6.1 擬定需求

咱們接下來要作的要求以下:要知足RestFul開發風格,以Hppt方式區分對資源的CRUD操做:URI: /資源名稱/資源表示 css

普通CURD(uri區別操做) RestfulCRUD
查詢 getEmp emp---GET
添加 addEmp emp---post
修改 updateEmp?id=xx&xxx=xx emp/{id}---PUT
刪除 deleteEmp?id=xx emp/{id}---delete

什麼是Restful風格? REST 指的是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是 RESTful。 Web 應用程序最重要的 REST 原則是,客戶端和服務器之間的交互在請求之間是無狀態的。從客戶端到服務器的每一個請求都必須包含理解請求所必需的信息。若是服務器在請求之間的任什麼時候間點重啓,客戶端不會獲得通知。此外,無狀態請求能夠由任何可用服務器回答,這十分適合雲計算之類的環境。客戶端能夠緩存數據以改進性能。html

由此咱們能夠這樣作: ||請求URI|請求方式| |-|-|-| |查詢全部員工|emps|GET| |查詢某個員工(來到修改頁面)|emp/{id}|GET| |來到添加頁面|emp|GET| |添加員工|emp|POST| |來到修改頁面(查出員工信息進行信息回顯)|emp/{id}|GET| |修改員工|emp|PUT| |刪除員工|emp/{id}|DELETE| 按照此表進行以下的開發流程。java

6.2 員工列表

  1. 準備 建立員工類:
package com.zhaoyi.springboot.restweb.entities;

import java.util.Date;

public class Employee {

	private Integer id;
    private String lastName;

    private String email;
    //1 male, 0 female
    private Integer gender;
    private Department department;
    private Date birth;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }
    public Employee(Integer id, String lastName, String email, Integer gender,
                    Department department) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        this.birth = new Date();
    }

    public Employee() {
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                ", birth=" + birth +
                '}';
    }
	
	
}

關聯的部門類jquery

package com.zhaoyi.springboot.restweb.entities;

public class Department {

	private Integer id;
	private String departmentName;

	public Department() {
	}
	
	public Department(int i, String string) {
		this.id = i;
		this.departmentName = string;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getDepartmentName() {
		return departmentName;
	}

	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}

	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName + "]";
	}
	
}

部門數據倉庫web

package com.zhaoyi.springboot.restweb.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.zhaoyi.springboot.restweb.entities.Department;
import org.springframework.stereotype.Repository;



@Repository
public class DepartmentDao {

	private static Map<Integer, Department> departments = null;
	
	static{
		departments = new HashMap<Integer, Department>();
		
		departments.put(101, new Department(101, "D-AA"));
		departments.put(102, new Department(102, "D-BB"));
		departments.put(103, new Department(103, "D-CC"));
		departments.put(104, new Department(104, "D-DD"));
		departments.put(105, new Department(105, "D-EE"));
	}
	
	public Collection<Department> getDepartments(){
		return departments.values();
	}
	
	public Department getDepartment(Integer id){
		return departments.get(id);
	}
	
}

員工數據倉庫spring

package com.zhaoyi.springboot.restweb.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.zhaoyi.springboot.restweb.entities.Department;
import com.zhaoyi.springboot.restweb.entities.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;


@Repository
public class EmployeeDao {

	private static Map<Integer, Employee> employees = null;
	
	@Autowired
	private DepartmentDao departmentDao;
	
	static{
		employees = new HashMap<Integer, Employee>();

		employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
		employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
		employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
		employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
		employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
	}
	
	private static Integer initId = 1006;
	
	public void save(Employee employee){
		if(employee.getId() == null){
			employee.setId(initId++);
		}

		employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
		employees.put(employee.getId(), employee);
	}
	
	public Collection<Employee> getAll(){
		return employees.values();
	}
	
	public Employee get(Integer id){
		return employees.get(id);
	}
	
	public void delete(Integer id){
		employees.remove(id);
	}
}

@Repository將倉庫類註冊到容器中,咱們須要用的時候使用@Autowired注入就能夠了。bootstrap

  1. 修改index.hmtl的菜單欄標籤請求地址,根據上表,咱們知道應該改成/emps請求地址:
<a class="nav-link" th:href="@{/emps}">
  1. 編寫restful風格的響應方法。

注意將list.html放進/resource/templates/emp/下。如前面所說,默認狀況下模板框架會對classpath:/templates/emp/xx.html進行渲染,其中xx便是咱們返回的字符串視圖信息;緩存

  1. 抽取thymeleaf的公共片斷 咱們改了index.html頁面的鏈接,可是當咱們跳轉到list.html頁面時,發現該頁面的一樣部位仍是老樣子,這就須要咱們開發其餘語言時所用到的模板概念了,所幸thymeleaf支持模板。也就是fragment相關知識。
  • 抽取公共片斷
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
  • 引入公共片斷
<body>
...
<div th:insert="~{footer :: copy}"></div>
</body

方式1:~{templatename::selector}模板名:選擇器springboot

方式2:~{templatename::fragmentname} 模板名:片斷名 顯然這裏是第二種寫法。

其中,模板名會使用thymeleaf的先後綴配置規則進行解析。

三種引入功能片斷的th:xx屬性:

  • th:insert
  • th:replace
  • th:include
<body>
...
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
</body>

結果:

<body>
...
<div>
<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>
</div>

<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

<div>
&copy; 2011 The Good Thymes Virtual Grocery
</div>
</body

所以能夠總結以下:

  • th:insert 公共片斷插入到引入標籤聲明元素(<div></div>)的內部;
  • th:replace 公共片斷替換掉引入標籤聲明元素<div></div>
  • th:include 將被引入的公共片斷的內容包含進標籤中,即被引入的公共片斷標籤被捨去(<foooter>)

若是使用th:insert等屬性進行引入,能夠不用寫~{...},而行內寫法須要加上:[[~{}]][(~{})]

這裏最好配置官方文檔食用:8 Template Layout

按照上述知識就能夠將咱們的頁面的公共部分抽去了。

  • 抽取navar,頂部欄
<nav th:fragment="topbar" class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">

去到重複的頁面,將此處使用模板語法取代:

<nav th:replace="index::topbar">
		</nav>
  • 上面一種方法是用~{templatename::fragmentname} 模板名:片斷名的方法替換了頂部欄的模板,接下來咱們對菜單欄(左導航)使用~{templatename::selector} 模板名:選擇器的方式進行操做:

提取

<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">

直接代表ID便可,無需任何th:fragment標籤;

替換

<div th:replace="index::#sidebar"></div>

注意ID選擇器的關鍵字#,不要寫漏了喲。

抽取公共部分到common文件夾中

在實際開發中,咱們通常是不會像以前那樣作,即使使用了模板功能,你也會發現,太過繁瑣,頁面交互影響,很是難爲維護,所以,咱們須要將公共部分抽離出來,放在一個公共文件夾,這樣方便管理。 因而,咱們將全部的公共模板抽去出來,放在commons文件夾中,以下所示:

<!-- commons/bar.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!-- 頂部模塊 -->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" id="topbar">
...
</nav>

<!-- 導航模塊 -->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" id="sidebar">
    ...
</nav>
</body>
</html>

此處我已經將其修改成相同的模式,即只須要用ID引入便可。省去了th代碼塊。

同時,刪除index.html和list.html中的相關部分,都加入導入模塊代碼,他們應該由

<div th:replace="index::#sidebar"></div>

修改成

<div th:replace="commons/bar::#sidebar"></div>

這樣的模式。

其中commons/bar 恰好對應於commons下的bar文件,sidebar對應引用對應模塊的id。

菜單高亮參數化

咱們還發現有一個問題,那就是菜單欄的菜單高亮問題,好比咱們點擊首頁,應該dashboard菜單欄高亮,點擊員工列表,員工列表高亮,咱們觀察html樣式發現,其實就是他們是否具有active這個class與否的問題,這就須要咱們作一件事,那就是在進行片斷引入的時候,進行參數傳遞,告訴當前片斷我是什麼頁面進行了引入,其實就是一個傳遞參數的過程,讓當前片斷根據參數的值生成具體的片斷行爲,查看thymeleaf官方文檔瞭解相應的傳參方式:

<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

在看看具體的片斷(bar.html中的片斷)接收參數的方式:

<div th:fragment="frag (onevar,twovar)">
<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>

另外,還能夠不用特定的去寫接受參數的方式,能夠直接在片斷裏面直接引用變量便可。所以,咱們能夠這樣寫:

index.html

<div th:replace="commons/bar::#sidebar(activeUri='index')"></div>

list.html

<div th:replace="commons/bar::#sidebar(activeUri='emps')"></div>

bar.html

...
<a class="nav-link active" th:class="${activeUri=='index'? 'nav-link active':'nav-link'}" th:href="@{/index}">
...

<a class="nav-link" th:class="${activeUri=='emps'? 'nav-link active':'nav-link'}" th:href="@{/emps}">

這樣,就能夠保證傳遞根據咱們的不一樣頁面,高亮對應的菜單模塊了。

完善員工列表顯示

list.html頁面中,將咱們返回的emps列表顯示,代碼以下:

<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 class="btn btn-sm btn-primary">add</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>operate</th>
								</tr>
							</thead>
							<tbody>
								<tr th:each="emp:${emps}">
									<td th:text="${emp.id}"></td>
									<td th:text="${emp.lastName}"></td>
									<td th:text="${emp.email}"></td>
									<td th:text="${emp.gender} == 0? '男':'女'"></td>
									<td th:text="${emp.department.departmentName}"></td>
									<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd:HH:mm')}"></td>
									<td><a class="btn btn-sm btn-default">edit</a>
										<a class="btn btn-sm btn-warning">delete</a>
									</td>
								</tr>
							</tbody>
						</table>
					</div>
				</main>
			</div>
		</div>

其中:

  1. th:each 屬性會由於每一次遍歷生成一次所在標籤;
  2. 咱們最後還添加了一些操做按鈕,爲接下來的下一部分工做展開鋪墊。

6.3 員工添加

來到員工添加頁面

  1. 咱們首先修改跳轉標籤的跳轉屬性,注意要符合restful風格,參考以前的表格,修改以下:
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeDao employeeDao;

    @RequestMapping("/emps")
    @GetMapping
    public String emps(Model model){
        model.addAttribute("emps", employeeDao.getAll());
        return "emp/list";
    }

    /**
     * 接收前往員工添加頁面的請求,並跳轉到添加頁面emp/add.html
     * @return
     */
    @RequestMapping("/emp")
    @GetMapping
    public String toAddPage(){
        return "emp/add";
    }
}
  1. 在emp下編寫add.html,以下所示:
<!DOCTYPE html>
<!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
<html 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>Dashboard Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link th:href="@{asserts/css/bootstrap.min.css}" rel="stylesheet">

		<!-- Custom styles for this template -->
		<link th:href="@{asserts/css/dashboard.css}" rel="stylesheet">
		<style type="text/css">
			/* Chart.js */
			
			@-webkit-keyframes chartjs-render-animation {
				from {
					opacity: 0.99
				}
				to {
					opacity: 1
				}
			}
			
			@keyframes chartjs-render-animation {
				from {
					opacity: 0.99
				}
				to {
					opacity: 1
				}
			}
			
			.chartjs-render-monitor {
				-webkit-animation: chartjs-render-animation 0.001s;
				animation: chartjs-render-animation 0.001s;
			}
		</style>
	</head>

	<body>
		<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>
						<div class="form-group">
							<label>LastName</label>
							<input type="text" class="form-control" placeholder="zhangsan">
						</div>
						<div class="form-group">
							<label>Email</label>
							<input type="email" class="form-control" placeholder="zhangsan@atguigu.com">
						</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">
								<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">
								<label class="form-check-label">女</label>
							</div>
						</div>
						<div class="form-group">
							<label>department</label>
							<select class="form-control">
								<option>1</option>
								<option>2</option>
								<option>3</option>
								<option>4</option>
								<option>5</option>
							</select>
						</div>
						<div class="form-group">
							<label>Birth</label>
							<input type="text" class="form-control" placeholder="zhangsan">
						</div>
						<button type="submit" class="btn btn-primary">添加</button>
					</form>
				</main>
			</div>
		</div>

		<!-- Bootstrap core JavaScript
    ================================================== -->
		<!-- Placed at the end of the document so the pages load faster -->
		<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js"></script>
		<script type="text/javascript" src="asserts/js/popper.min.js"></script>
		<script type="text/javascript" src="asserts/js/bootstrap.min.js"></script>

		<!-- Icons -->
		<script type="text/javascript" src="asserts/js/feather.min.js"></script>
		<script>
			feather.replace()
		</script>

		<!-- Graphs -->
		<script type="text/javascript" src="asserts/js/Chart.min.js"></script>
	</body>

</html>

完成以後運行咱們還發現了一個問題,那就是部門選擇哪一個地方,實際上是來源自咱們後臺的部門信息來生成所以,咱們跳轉到添加員工頁面時,還應該將部門信息所有傳遞過來:

修改事後的Controller:

....
    @Autowired
    private DepartmentDao departmentDao;
	...
    /**
     * 接收前往員工添加頁面的請求,並跳轉到添加頁面emp/add.html
     * @return
     */
    @RequestMapping("/emp")
    @GetMapping
    public String toAddPage(Model model){
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emp/add";
    }
}

修改事後的add.html

<!DOCTYPE html>
<!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
....
						<div class="form-group">
							<label>department</label>
							<select class="form-control">
								<option th:each="department:${departments}" th:value="${department.id}" th:text="${department.departmentName}"></option>
							</select>
						</div>

...
相關文章
相關標籤/搜索