文章目錄
- 1. REST 簡介
- 2. RESTful 入門案例
- 3. RESTful 快速開發
- 4. 基于 RESTful 的頁面資料互動
-
- 4.1 需求分析
- 4.2 背景接口開發
- 4.2 頁面通路處理
1. REST 簡介
REST(Representational State Transfer),表現形式狀态轉換,它是一種軟體架構風格。
當要表示一個網絡資源的時候,可以使用兩種方式:
- 傳統風格資源描述形式
查詢 id 為 1 的使用者資訊http://localhost/user/getById?id=1
儲存使用者資訊http://localhost/user/saveUser
- REST風格描述形式
查詢 id 為 1 的使用者資訊http://localhost/user/1
儲存使用者資訊http://localhost/user
傳統方式一般是一個請求 url 對應一種操作,這樣做不僅麻煩,也不安全,因為會程式的人讀了請求 url 位址,就大概知道該 url 實作的是什麼操作。
檢視 REST 風格的描述,會發現請求位址變簡單了,并且隻看請求 URL 并不能輕易猜出該 URL 的具體功能。
是以 REST 的優點有:
- 隐藏資源的通路行為,無法通過位址得知對資源是何種操作。
- 書寫簡化。
但是問題也随之而來,一個相同的 url 位址既可以是新增也可以是修改或者查詢,該如何區分該請求到底是什麼操作呢?
按照 REST 風格通路資源時使用請求動作區分對資源進行了何種操作:
請求的方式比較多,但是比較常用的就 4 種,分别是 GET、POST、PUT、DELETE。不同的請求方式代表不同的操作類型:
- 發送 GET 請求是用來做查詢
- 發送 POST 請求是用來做新增
- 發送 PUT 請求是用來做修改
- 發送 DELETE 請求是用來做删除
上述行為是約定方式,約定不是規範,可以打破,是以稱REST風格,而不是REST規範。可以不這樣做,但不建議。
描述子產品的名稱通常使用複數,也就是加 “s”。這樣表示此類資源,而非單個資源,例如:users、books、accounts…
根據 REST 風格對資源進行通路稱為 RESTful。
在開發過程中,大多都遵從 REST 風格來通路背景服務,是以可以說以後都是基于 RESTful 來進行開發。
2. RESTful 入門案例
@PathVariable
注解綁定路徑參數與處理器方法形參間的關系,要求路徑參數名與形參名一一對應。
public class BookController {
//用postman發post請求,請求路徑:http://localhost/books
@RequestMapping(value = "/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
//用postman發delete請求,請求路徑:http://localhost/books/1
//請求路徑中的“1”傳給id
@RequestMapping(value = "/books/{id}",method = RequestMethod.DELETE)
@ResponseBody
//@PathVariable:把請求路徑中的變量值傳給形參
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//用postman發put請求,請求路徑:http://localhost/books
@RequestMapping(value = "/books",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
//用postman發get請求,請求路徑:http://localhost/books/1
@RequestMapping(value = "/books/{id}",method = RequestMethod.GET)
@ResponseBody
//@PathVariable:把請求路徑中的變量值傳給形參
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
//用postman發get請求,請求路徑:http://localhost/books
@RequestMapping(value = "/books",method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
@RequestBody、@RequestParam、@PathVariable 三個注解之間的差別和應用分别是什麼?
差別:
- @RequestParam 用于接收 url 位址傳參或表單傳參。
- @RequestBody 用于接收 json 資料。
- @PathVariable 用于接收路徑參數,使用 {參數名稱} 描述路徑參數。
應用:
- 後期開發中,發送請求參數超過 1 個時,以 json 格式為主,@RequestBody 應用較廣。
- 如果發送非 json 格式資料,選用 @RequestParam 接收請求參數。
- 采用 RESTful 進行開發,當參數數量較少時,例如 1 個,可以采用 @PathVariable 接收請求路徑變量,通常用于傳遞 id 值。
3. RESTful 快速開發
在前面基礎上,
- 可以把相同的路徑字首寫在類上,即:把
提到類上。@RequestMapping("/books")
- 由于之前每個方法上都有
注解,是以可以統一寫在類上。@ResponseBody
得到如下代碼:
@Controller
@ResponseBody
@RequestMapping("/books")
public class BookController {
//用postman發post請求,請求路徑:http://localhost/books
@RequestMapping(method = RequestMethod.POST)
public String save(@RequestBody Book book){
System.out.println("book save..."+ book);
return "{'module':'book save'}";
}
//用postman發delete請求,請求路徑:http://localhost/books/1
//請求路徑中的“1”傳給id
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
//@PathVariable:把請求路徑中的變量值傳給形參
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//用postman發put請求,請求路徑:http://localhost/books
@RequestMapping(method = RequestMethod.PUT)
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
//用postman發get請求,請求路徑:http://localhost/books/1
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
//@PathVariable:把請求路徑中的變量值傳給形參
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
//用postman發get請求,請求路徑:http://localhost/books
@RequestMapping(method = RequestMethod.GET)
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
在上面代碼的基礎上,
@Controller
和
@ResponseBody
注解可以用一個注解
@RestController
替代。
請求動作也可以用更簡單的注解表示。
// @Controller
// @ResponseBody
@RestController
@RequestMapping("/books")
public class BookController {
//用postman發post請求,請求路徑:http://localhost/books
// @RequestMapping(method = RequestMethod.POST)
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save..."+ book);
return "{'module':'book save'}";
}
//用postman發delete請求,請求路徑:http://localhost/books/1
//請求路徑中的“1”傳給id
// @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@DeleteMapping("/{id}")
//@PathVariable:把請求路徑中的變量值傳給形參
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//用postman發put請求,請求路徑:http://localhost/books
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
//用postman發get請求,請求路徑:http://localhost/books/1
// @RequestMapping(value = "/{id}",method = RequestMethod.GET)
@GetMapping("/{id}")
//@PathVariable:把請求路徑中的變量值傳給形參
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
//用postman發get請求,請求路徑:http://localhost/books
// @RequestMapping(method = RequestMethod.GET)
@GetMapping
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
上面的代碼去掉注釋後:
@RestController
@RequestMapping("/books")
public class BookController {
//用postman發post請求,請求路徑:http://localhost/books
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save..."+ book);
return "{'module':'book save'}";
}
//用postman發delete請求,請求路徑:http://localhost/books/1
//請求路徑中的“1”傳給id
@DeleteMapping("/{id}")
//@PathVariable:把請求路徑中的變量值傳給形參
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//用postman發put請求,請求路徑:http://localhost/books
@PutMapping
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
//用postman發get請求,請求路徑:http://localhost/books/1
@GetMapping("/{id}")
//@PathVariable:把請求路徑中的變量值傳給形參
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
//用postman發get請求,請求路徑:http://localhost/books
@GetMapping
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
4. 基于 RESTful 的頁面資料互動
4.1 需求分析
需求一:圖檔清單查詢,從背景傳回資料,将資料展示在頁面上。
需求二:新增圖書,将新增圖書的資料傳遞到背景,并在控制台列印。
說明:此次案例的重點是在 SpringMVC 中使用 RESTful 實作前背景互動,是以并沒有和資料庫進行互動,所有資料使用假資料來完成開發。
4.2 背景接口開發
@RestController
@RequestMapping("/books")
public class BookController {
//儲存圖書
@PostMapping
public String save(@RequestBody Book book){
System.out.println("儲存圖書:"+book);
return "{'module':'save book success'}";
}
//查詢所有圖書
@GetMapping
public List<Book> getAll(){
System.out.println("查詢所有圖書...");
List<Book> bookList = new ArrayList<>();
Book book1 = new Book();
book1.setType("計算機");
book1.setName("SpringMVC入門教程");
book1.setDescription("小試牛刀");
bookList.add(book1);
Book book2 = new Book();
book2.setType("計算機");
book2.setName("SpringMVC實戰教程");
book2.setDescription("一代宗師");
bookList.add(book2);
return bookList;
}
}
postman 測試:
① 測試新增
② 測試查詢
4.2 頁面通路處理
(1) 拷貝靜态頁面:将資料\功能頁面下的所有内容拷貝到項目的 webapp 目錄下。
(2) 通路 pages 目錄下的 books.html
打開浏覽器輸入
http://localhost/pages/books.html
為什麼會出現錯誤?
SpringMVC 攔截了靜态資源,根據
/pages/books.html
去 controller 找對應的方法,找不到是以會報 404 的錯誤。
SpringMVC 為什麼會攔截靜态資源呢?
解決方案:SpringMVC 需要将靜态資源放行。
//設定靜态資源通路過濾,目前類需要設定為配置類,并被掃描加載
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 當通路/pages/...的時候,從/pages目錄下查找内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
該配置類在 config 目錄下,SpringMVC 目前掃描的是 controller 包,是以該配置類還未生效,要想生效需要修改 SpringMvcConfig 配置類的掃描範圍。
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc//開啟将JSON轉換成對象的功能
public class SpringMvcConfig {
}
//或者
@Configuration
@ComponentScan("com.itheima")
@EnableWebMvc
public class SpringMvcConfig {
}
(3) 修改 books.html 頁面
頁面完整代碼:
<!DOCTYPE html>
<html>
<head>
<!-- 頁面meta -->
<meta charset="utf-8">
<title>SpringMVC案例</title>
<!-- 引入樣式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>圖書管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="圖書名稱" style="width: 200px;" class="filter-item"></el-input>
<el-button class="dalfBut">查詢</el-button>
<el-button type="primary" class="butT" @click="openSave()">建立</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="圖書類别" align="center"></el-table-column>
<el-table-column prop="name" label="圖書名稱" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini">編輯</el-button>
<el-button size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
<!-- 新增标簽彈層 -->
<div class="add-form">
<el-dialog title="新增圖書" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="圖書類别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="圖書名稱" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="saveBook()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入元件庫 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//目前頁要展示的分頁清單資料
formData: {},//表單資料
dialogFormVisible: false,//增加表單是否可見
dialogFormVisible4Edit:false,//編輯表單是否可見
pagination: {},//分頁模型資料,暫時棄用
},
//鈎子函數,VUE對象初始化完成後自動執行
created() {
this.getAll();
},
methods: {
// 重置表單
resetForm() {
//清空輸入框
this.formData = {};
},
// 彈出添加視窗
openSave() {
this.dialogFormVisible = true;
this.resetForm();
},
//添加
saveBook () {
axios.post("/books",this.formData).then((res)=>{
});
},
//首頁清單查詢
getAll() {
axios.get("/books").then((res)=>{
this.dataList = res.data;
});
},
}
})
</script>
</html>