更新數(shù)據(jù)時(shí),我們需要給后臺(tái)提供兩項(xiàng)信息:1. 我們要更新哪個(gè)教師;2. 更新后的教師的數(shù)據(jù)應(yīng)該是什么值。
# RESTful web service
在前后臺(tái)的交互過程中,我們前面規(guī)定了使用`GET`方法請(qǐng)求`/Teacher`地址來獲取教師的全部數(shù)據(jù);規(guī)定了使用`GET`方法請(qǐng)求`/Teacher/1`地址來獲取教師ID為`1`的數(shù)據(jù);規(guī)定了使用`POST`方法請(qǐng)求`/Teacher`地址來新增教師。其實(shí)這些都是在遵循`REST`規(guī)范。維基百科如是說:
*****
**Representational state transfer**(**REST**) is a [software architectural](https://en.wikipedia.org/wiki/Software_architecture "Software architecture") style that defines a set of constraints to be used for creating [Web services](https://en.wikipedia.org/wiki/Web_service "Web service"). Web services that conform to the REST architectural style, called *RESTful* Web services, provide interoperability between computer systems on the [Internet](https://en.wikipedia.org/wiki/Internet "Internet"). RESTful Web services allow the requesting systems to access and manipulate textual representations of [Web resources](https://en.wikipedia.org/wiki/Web_resource "Web resource")by using a uniform and predefined set of [stateless](https://en.wikipedia.org/wiki/Stateless_protocol "Stateless protocol")operations. Other kinds of Web services, such as [SOAP](https://en.wikipedia.org/wiki/SOAP "SOAP")Web services, expose their own arbitrary sets of operations.[\[1\]](https://en.wikipedia.org/wiki/Representational_state_transfer#cite_note-1)
*****
上面大概是說REST是一種軟件開發(fā)的風(fēng)格(style),而符合這個(gè)風(fēng)格的web服務(wù)呢,就是`RESTful Web services`。而我們前面進(jìn)行前后臺(tái)交互時(shí)的地址恰恰是按照該風(fēng)格來制定的。該風(fēng)格同時(shí)規(guī)定,進(jìn)行數(shù)據(jù)的全部更新時(shí),應(yīng)該使用`put`方法,并在路徑中傳入要更新的`id`值以及在請(qǐng)求的數(shù)據(jù)中傳入更新數(shù)據(jù)。所以我們制定接口規(guī)范如下:
```
PUT /Teacher/{id}
```
| Type | Name | Description | Schema |
| ---- | ---- | ---- | ---- |
| Path | id | 更新的教師ID | Long |
| Body | teacher | 更新教師數(shù)據(jù) | Teacher |
上述接口描述清晰的說明了請(qǐng)求的方法為`PUT`,請(qǐng)求的地址為`/Teacher/{id}` ,該地址中包括有路徑變量`id` ,該值代表`更新的教師ID` 類型為 `Long`。同時(shí)接收請(qǐng)求主體(`Body`),類型為`Teacher`,代表`更新教師數(shù)據(jù)`。
# 更新數(shù)據(jù)
我們前面已經(jīng)掌握了獲取路徑ID值的方法,又知道V層數(shù)據(jù)變更新將會(huì)實(shí)時(shí)的傳遞給C層,那么代碼就簡單了。
```
constructor(private route: ActivatedRoute, private httpClient: HttpClient, private appComponent: AppComponent ①) {
}
/**
* 提交表單更新數(shù)據(jù)
*/
onSubmit(): void {
const id = this.route.snapshot.paramMap.get('id');
const url = 'http://localhost:8080/Teacher/' + id;
this.httpClient.put(url, this.teacher ?) ?
.subscribe(() => {
console.log('更新成功');
this.appComponent.ngOnInit();
},
() => {
console.error(`更新數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤,url:${url}`);
});
}
```
* ? PUT方法與POST方法使用相同。第個(gè)參數(shù)傳入U(xiǎn)RL信息,第二個(gè)參數(shù)傳入請(qǐng)求主體。
* ? 由于this.teacher的值會(huì)隨著用戶在表單中的輸入而實(shí)時(shí)變化,所以我們?cè)诖酥苯訉his.teacher傳給后臺(tái)。
> 如果此時(shí)你心里不大清楚this.teacher對(duì)象的值,那么可以在使用它之前使用`console.log(this.teacher)`來將其打印到控制臺(tái)來查看。
# 代碼重構(gòu)
代碼重構(gòu)是軟件開發(fā)中非常重要的一環(huán),可以說沒有代碼重構(gòu)就沒有優(yōu)秀的項(xiàng)目。本著**不造重復(fù)的輪子**的原則,當(dāng)相同的代碼塊、字符串在項(xiàng)目中出現(xiàn)了多次的時(shí)候,就需要思索此處代碼是否需要重構(gòu)了。當(dāng)前`TeacherEditComponent`的代碼如下:
```js
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {HttpClient} from '@angular/common/http';
import {AppComponent} from './app.component';
@Component({
templateUrl: './teacher-edit.component.html'
})
export class TeacherEditComponent implements OnInit {
public teacher: any = {};
constructor(private route: ActivatedRoute, private httpClient: HttpClient, private appComponent: AppComponent) {
}
ngOnInit(): void {
const id = this.route.snapshot.paramMap.get('id'); ?
const url = 'http://localhost:8080/Teacher/' + id; ?
this.httpClient.get(url)
.subscribe((data) => {
this.teacher = data;
}, () => {
console.log(`請(qǐng)求 ${url} 時(shí)發(fā)生錯(cuò)誤`);
});
}
/**
* 提交表單
*/
onSubmit(): void {
const id = this.route.snapshot.paramMap.get('id'); ?
const url = 'http://localhost:8080/Teacher/' + id; ?
this.httpClient.put(url, this.teacher)
.subscribe(() => {
console.log('更新成功');
this.appComponent.ngOnInit();
},
() => {
console.error(`更新數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤,url:${url}`);
});
}
}
```
* ? 在一個(gè)類中,相同的代碼塊出現(xiàn)了兩次。
## 方法一
** 當(dāng)在一個(gè)類中,相同的代碼碼出現(xiàn)了多次的時(shí)候,可以把該代碼塊抽離為該類的一個(gè)新方法。**
TeacherEditComponent
```
/**
* 獲取與后臺(tái)對(duì)接的URL
*/
getUrl(): string {
const id = this.route.snapshot.paramMap.get('id');
return 'http://localhost:8080/Teacher/' + id;
}
```
然后在原方法中刪除對(duì)應(yīng)的代碼段并調(diào)用新抽離的方法來獲取需要的值。
```
/**
* 獲取與后臺(tái)對(duì)接的URL
*/
getUrl(): string {
const id = this.route.snapshot.paramMap.get('id');
return 'http://localhost:8080/Teacher/' + id;
}
ngOnInit(): void {
this.httpClient.get(this.getUrl()) ?
.subscribe((data) => {
this.teacher = data;
}, () => {
console.log(`請(qǐng)求 ${this.getUrl()?} 時(shí)發(fā)生錯(cuò)誤`);
});
}
/**
* 提交表單
*/
onSubmit(): void {
this.httpClient.put(this.getUrl()?, this.teacher)
.subscribe(() => {
console.log('更新成功');
this.appComponent.ngOnInit();
},
() => {
console.error(`更新數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤,url:${this.getUrl()?}`);
});
}
```
* ? 調(diào)用新方法`getUrl()`來直接獲取請(qǐng)求地址的值。
## 方法二
方法一中`getUrl()`被執(zhí)行了兩次,且每次執(zhí)行返回的結(jié)果必然相同。這種時(shí)候當(dāng)該方法執(zhí)行的邏輯簡單、運(yùn)算量不大、沒有資源請(qǐng)求的時(shí)候是并沒太大的問題的,但如果其邏輯復(fù)雜、運(yùn)算量大或是有資源請(qǐng)求時(shí),就會(huì)帶來不必要的開銷。所以:**當(dāng)某個(gè)方法在多次調(diào)用的結(jié)果都是確定值時(shí),應(yīng)該保證該方法的運(yùn)算只執(zhí)行一次**,此時(shí)我們需要一個(gè)變量來緩存方法運(yùn)算的值。
TeacherEditComponent
```
private url: string; ?
/**
* 獲取與后臺(tái)對(duì)接的URL
*/
getUrl(): string {
if (this.url === undefined) { ?
const id = this.route.snapshot.paramMap.get('id');
this.url = 'http://localhost:8080/Teacher/' + id; ?
}
return this.url;
}
```
* ? 在類中定義私有變量`url`,設(shè)置類型為`string`。
* ? 當(dāng)`this.url`沒有被初始化時(shí),說明該方法未經(jīng)過運(yùn)算。
* ? 將運(yùn)算的結(jié)果緩存到`this.url`中。
此時(shí),當(dāng)該方法被第1次調(diào)用時(shí)將計(jì)算`url`值;當(dāng)該方法被第二次調(diào)用時(shí),將直接返回第1次調(diào)用時(shí)計(jì)算得到的值,不再重新計(jì)算。
# 測(cè)試
代碼重構(gòu)后進(jìn)行測(cè)試,查看是否由于重構(gòu)給項(xiàng)目帶來的新的BUG。
# 參考文檔
| 名稱 | 鏈接 | 預(yù)計(jì)學(xué)習(xí)時(shí)長(分) |
| --- | --- | --- |
| 發(fā)起PUT請(qǐng)求 | [https://www.angular.cn/guide/http#making-a-put-request](https://www.angular.cn/guide/http#making-a-put-request) | 5 |
| 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.4.4](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.4.4) | - |
- 序言
- 第一章:Hello World
- 第一節(jié):Angular準(zhǔn)備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節(jié):Hello Angular
- 第三節(jié):Spring Boot準(zhǔn)備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節(jié):Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國內(nèi)源配置
- 4 package與import
- 第五節(jié):Hello Spring Boot + Angular
- 1 依賴注入【前】
- 2 HttpClient獲取數(shù)據(jù)【前】
- 3 數(shù)據(jù)綁定【前】
- 4 回調(diào)函數(shù)【選學(xué)】
- 第二章 教師管理
- 第一節(jié) 數(shù)據(jù)庫初始化
- 第二節(jié) CRUD之R查數(shù)據(jù)
- 1 原型初始化【前】
- 2 連接數(shù)據(jù)庫【后】
- 3 使用JDBC讀取數(shù)據(jù)【后】
- 4 前后臺(tái)對(duì)接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節(jié) CRUD之C增數(shù)據(jù)
- 1 新建組件并映射路由【前】
- 2 模板驅(qū)動(dòng)表單【前】
- 3 httpClient post請(qǐng)求【前】
- 4 保存數(shù)據(jù)【后】
- 5 組件間調(diào)用【前】
- 第四節(jié) CRUD之U改數(shù)據(jù)
- 1 路由參數(shù)【前】
- 2 請(qǐng)求映射【后】
- 3 前后臺(tái)對(duì)接【前】
- 4 更新數(shù)據(jù)【前】
- 5 更新某個(gè)教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節(jié) CRUD之D刪數(shù)據(jù)
- 1 綁定到用戶輸入事件【前】
- 2 刪除某個(gè)教師【后】
- 第六節(jié) 代碼重構(gòu)
- 1 文件夾化【前】
- 2 優(yōu)化交互體驗(yàn)【前】
- 3 相對(duì)與絕對(duì)地址【前】
- 第三章 班級(jí)管理
- 第一節(jié) JPA初始化數(shù)據(jù)表
- 第二節(jié) 班級(jí)列表
- 1 新建模塊【前】
- 2 初識(shí)單元測(cè)試【前】
- 3 初始化原型【前】
- 4 面向?qū)ο蟆厩啊?/a>
- 5 測(cè)試HTTP請(qǐng)求【前】
- 6 測(cè)試INPUT【前】
- 7 測(cè)試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺(tái)對(duì)接【前】
- 第三節(jié) 新增班級(jí)
- 1 初始化【前】
- 2 響應(yīng)式表單【前】
- 3 測(cè)試POST請(qǐng)求【前】
- 4 JPA插入數(shù)據(jù)【后】
- 5 單元測(cè)試【后】
- 6 惰性加載【前】
- 7 對(duì)接【前】
- 第四節(jié) 編輯班級(jí)
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務(wù)【前】
- 4 測(cè)試間諜spy【前】
- 5 使用JPA更新數(shù)據(jù)【后】
- 6 分層開發(fā)【后】
- 7 前后臺(tái)對(duì)接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節(jié) 選擇教師組件
- 1 初始化【前】
- 2 動(dòng)態(tài)數(shù)據(jù)綁定【前】
- 3 初識(shí)泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識(shí)單元測(cè)試【前】
- 7 其它問題
- 第六節(jié) 刪除班級(jí)
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺(tái)對(duì)接
- 第四章 學(xué)生管理
- 第一節(jié) 引入Bootstrap【前】
- 第二節(jié) NAV導(dǎo)航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節(jié) footer組件【前】
- 第四節(jié) 歡迎界面【前】
- 第五節(jié) 新增學(xué)生
- 1 初始化【前】
- 2 選擇班級(jí)組件【前】
- 3 復(fù)用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗(yàn)【后】
- 7 唯一性校驗(yàn)【后】
- 8 @PrePersist【后】
- 9 CM層開發(fā)【后】
- 10 集成測(cè)試
- 第六節(jié) 學(xué)生列表
- 1 分頁【后】
- 2 HashMap與LinkedHashMap
- 3 初識(shí)綜合查詢【后】
- 4 綜合查詢進(jìn)階【后】
- 5 小試綜合查詢【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測(cè)試與分頁【前】
- 9 單選與多選【前】
- 10 集成測(cè)試
- 第七節(jié) 編輯學(xué)生
- 1 初始化【前】
- 2 嵌套組件測(cè)試【前】
- 3 功能開發(fā)【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測(cè)試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗(yàn)證【前】
- 第八節(jié) 刪除學(xué)生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開發(fā)與測(cè)試【后】
- 4 集成測(cè)試
- 5 定制提示框【前】
- 6 引入圖標(biāo)庫【前】
- 第九節(jié) 集成測(cè)試
- 第五章 登錄與注銷
- 第一節(jié):普通登錄
- 1 原型【前】
- 2 功能設(shè)計(jì)【前】
- 3 功能設(shè)計(jì)【后】
- 4 應(yīng)用登錄組件【前】
- 5 注銷【前】
- 6 保留登錄狀態(tài)【前】
- 第二節(jié):你是誰
- 1 過濾器【后】
- 2 令牌機(jī)制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶登錄與注銷【后】
- 7 個(gè)人中心【前】
- 8 攔截器【后】
- 9 集成測(cè)試
- 10 單例模式
- 第六章 課程管理
- 第一節(jié) 新增課程
- 1 初始化【前】
- 2 嵌套組件測(cè)試【前】
- 3 async管道【前】
- 4 優(yōu)雅的測(cè)試【前】
- 5 功能開發(fā)【前】
- 6 實(shí)體監(jiān)聽器【后】
- 7 @ManyToMany【后】
- 8 集成測(cè)試【前】
- 9 異步驗(yàn)證器【前】
- 10 詳解CORS【前】
- 第二節(jié) 課程列表
- 第三節(jié) 果斷
- 1 初始化【前】
- 2 分頁組件【前】
- 2 分頁組件【前】
- 3 綜合查詢【前】
- 4 綜合查詢【后】
- 4 綜合查詢【后】
- 第節(jié) 班級(jí)列表
- 第節(jié) 教師列表
- 第節(jié) 編輯課程
- TODO返回機(jī)制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節(jié) 刪除課程
- 第七章 權(quán)限管理
- 第一節(jié) AOP
- 總結(jié)
- 開發(fā)規(guī)范
- 備用