# 對接后臺
在C層中成功的獲取到我們想到的數據以后,接下來我們開始對接后臺。
## API
API是個再熟悉不對的單詞,英文全稱為:Application Interface,即應用程序接口。在前后分離的情況下,API文檔便是前臺與后臺溝通的核心文檔。在我們當前的項目中,API便指前臺與后臺進行數據交互的規范。在API中規定了數據交互的地址、方法以及數據格式等。
前臺在后臺發起請求時,要嚴格的按照后臺的API文檔進行操作,以最大限度的規避在與后臺對接中可能發生的問題。
當前新建教師的API如下:
API請求地址為:`http://angular.api.codedemo.club:81`
```http
POST /teacher
```
上述信息傳遞了兩條信息:
- 請求的方法為 POST
- 請求的實際地址為`API請求地址 + /teacher`,即:`http://angular.api.codedemo.club:81/teacher`
同時在請求方法為POST的前提下,后臺還為我們提供了如下信息:
| **類型Type** | **名稱Name** | **描述Description** | **類型Schema** |
| ------------ | ------------ | ------------------- | ------------------------------------------------------------ |
| Body | teacher | 新建教師 | `{username: string, name: string, email: string, sex: boolean}` |
| Response | 成功 | Status Code: 201 | `{id: number, username: string, name: string, email: string, sex: boolean}` |
上述信息向我們傳遞了:
- POST請求時需要將請求的教師以`{username: string, name: string, email: string, sex: boolean}`的JSON形式傳遞給后臺。
- 當后臺處理成功時,將返回狀態碼為201的響應信息,響應信息中將回傳`{id: number, username: string, name: string, email: string, sex: boolean}`格式的教師。
## 依賴注入
發起HTTP請求,則需要HttpClient的幫助,我們在組件的構造函數中聲明對其的依賴如下:
```
- constructor() {
+ constructor(private httpClient: HttpClient) {
}
```
此時,`ng t`將得到一些錯誤,請參考2.2小節解決該錯誤繼續學習。
## POST請求
post請求與get請求大同小異,不同的是在發起post請求時可以在請求主體中攜帶更多數據。我們在onSubmit方法中補充相關代碼:
```typescript
onSubmit(): void {
console.log(this.teacher);
+ this.httpClient
+ .post('http://angular.api.codedemo.club:81/teacher', this.teacher)
+ .subscribe((result) => {
+ console.log('接收到返回數據', result);
+ });
}
```
- ★ post請求時接收兩個參數 ,第二個參數為向后臺發送的主體數據,在此我們需要發送的數據為預保存的教師。
這已經是我們第二次使用`httpClient`這個對象了,它之所以看起來能夠一直調用個不停,是由于在調用過程大概發生了如下事件:
```javascript
/**
* 擁有subscribe方法的對象
**/
var objectWithSubscribe = {
subscribe: function(cb) {
cb('這里是即將返回的數據');
}
}
/**
* 擁有post方法的httpClient對象
**/
var httpClient = {
// 該對象中擁有post方法
post: function (url, data) {
console.log('url', url);
console.log('data', data);
// 該方法返回了objectWithSubscribe對象
return objectWithSubscribe;
}
}
httpClient
.post('http://angular.api.codedemo.club:81/teacher', {name: '張三'})
.subscribe((result) => {
console.log('接收到返回數據', result);
});
```
執行結果如下:

填寫教師信息后,點擊發起請求按鈕,請求成功并在控制臺獲取到如下信息:

如果你也在使用`wangwu`做為教師的用戶名,則可能會引發用戶名沖突的錯誤,此時變更一個沒有被其它同學占用的用戶名即可。
### 錯誤處理
正常的業務邏輯都會對數據進行邏輯校驗,比如:在一個系統中可能不會有兩個用戶名相同的用戶。所以將我們新建一個用戶名為`zhangsan` 的教師,再次用`zhangsan`來創建新教師時,則應該得到相應的錯誤信息。
做為最佳實踐的Angular當然想到了這一切,當請求后臺發生錯誤時,我們可以使用如下代碼來獲取相應的錯誤信息:
```typescript
this.httpClient.get('url')
.subscribe(() => {}?, () => {}?);
```
- ? 請求成功時,調用本函數
- ? 請求失敗時,調用本函數
我們嘗試將上述方法添加到Add組件的`ngOnInit`方法中,并嘗試請求一個并不存在的地址,以測試請求失敗時是否能夠成功的獲取到錯誤信息。
```typescript
ngOnInit(): void {
+ this.httpClient.get('someUnKnownHost')
+ .subscribe(() => {}, (error) => { console.log('請求失敗', error); });
}
```
對代碼進行格式化后如下:
```typescript
ngOnInit(): void {
this.httpClient.get('someUnKnownHost')
.subscribe(() => {
}, (error) => {
console.log('請求失敗', error);
});
}
```
控制臺如下:

在控制臺的打印信息中,我們點擊如上圖圓圈所示的箭頭,可以查看到具體的錯誤的詳細信息。在錯誤信息中,我們主要觀察`status`字段,在大多數時候我們都可以根據該`status`字段快速的獲知錯誤的類型。比如上述錯誤代碼為404,表示`所要請求的地址不存在`。
接下來我們恢復剛剛在`ngOnInit()`方法中添加的測試代碼,同時在`onSubmit`方法中添加數據請求錯誤的相關代碼:
```typescript
?? --- a/first-app/src/app/add/add.component.ts
?? +++ b/first-app/src/app/add/add.component.ts
?? @@ -18,11 +18,6 @@ export class AddComponent implements OnInit {
}
ngOnInit(): void {
- this.httpClient.get('someUnKnownHost')
- .subscribe(() => {
- }, (error) => {
- console.log('請求失敗', error);
- });
}
onSubmit(): void {
?? @@ -31,6 +26,8 @@ export class AddComponent implements OnInit {
.post('http://angular.api.codedemo.club:81/teacher', this.teacher)
.subscribe((result) => {
console.log('接收到返回數據', result);
+ }, (error) => {
+ console.log('請求失敗', error);
});
}
}
```
- 被標識為 ?? 的為對比文件前后變化的**輔助信息**,起查看的輔助作用。在進行代碼查看時,重點仍然為被標識為`+`或`-`的代碼行。
然后向后臺再次以`wangwu`為用戶名發起請求:

此時我們將得到一個狀態碼為`409`的錯誤響應信息。該狀態碼由開發此小伙伴的同學定義,原則上他可以定義任何狀態,此時狀態碼`409`表示:請求發生沖突,在此意為:當前已存在用戶名為`wangwu`的教師。在定義狀態碼時,為了減少溝通成本,一般情況我們都會遵守這些國際規則,
- 2xx 請求成
- 3xx 請求的資源已轉移
- 4xx 用戶發起了不合乎規則的數據
- 5xx 服務器發生了異常
## 小結
我們在本章中學習了如果查看后臺定義的api規范,學會如何獲取發起請求時的錯誤信息,接觸了文件前后對比的更標準的方法以及初步的認識了請求狀態碼。
## 本節作業
1. 我們在展示文件前后的異同時使用了一個叫做`git`的偉大軟件,請了解該軟件的產生背景及作用。
2. 請搜索在文件的前后對比展中出現的`@@ -18,11 +18,6 @@`輔助信息具體代碼的含義。
3. `httpClient().subscribe`除可以同時接收`成功`,`失敗`的回調函數外,還可以接收第三個參數,其類型仍然為`回調函數`,請測試在請求成功、請求失敗時第三個函數的功能。
| 名稱 | 地址 | 備注 |
| ------------------------ | ------------------------------------------------------------ | ---- |
| Http狀態碼 | [https://www.runoob.com/http/http-status-codes.html](https://www.runoob.com/http/http-status-codes.html) | |
| HTTP 方法:GET 對比 POST | [https://www.runoob.com/tags/html-httpmethods.html](https://www.runoob.com/tags/html-httpmethods.html) | |
| RxJS Observer | [https://rxjs-dev.firebaseapp.com/guide/observer](https://rxjs-dev.firebaseapp.com/guide/observer) | |
| 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step2.3.3.zip](https://github.com/mengyunzhi/angular11-guild/archive/step2.3.3.zip) | |
- 序言
- 第一章 Hello World
- 1.1 環境安裝
- 1.2 Hello Angular
- 1.3 Hello World!
- 第二章 教師管理
- 2.1 教師列表
- 2.1.1 初始化原型
- 2.1.2 組件生命周期之初始化
- 2.1.3 ngFor
- 2.1.4 ngIf、ngTemplate
- 2.1.5 引用 Bootstrap
- 2.2 請求后臺數據
- 2.2.1 HttpClient
- 2.2.2 請求數據
- 2.2.3 模塊與依賴注入
- 2.2.4 異步與回調函數
- 2.2.5 集成測試
- 2.2.6 本章小節
- 2.3 新增教師
- 2.3.1 組件初始化
- 2.3.2 [(ngModel)]
- 2.3.3 對接后臺
- 2.3.4 路由
- 2.4 編輯教師
- 2.4.1 組件初始化
- 2.4.2 獲取路由參數
- 2.4.3 插值與模板表達式
- 2.4.4 初識泛型
- 2.4.5 更新教師
- 2.4.6 測試中的路由
- 2.5 刪除教師
- 2.6 收尾工作
- 2.6.1 RouterLink
- 2.6.2 fontawesome圖標庫
- 2.6.3 firefox
- 2.7 總結
- 第三章 用戶登錄
- 3.1 初識單元測試
- 3.2 http概述
- 3.3 Basic access authentication
- 3.4 著陸組件
- 3.5 @Output
- 3.6 TypeScript 類
- 3.7 瀏覽器緩存
- 3.8 總結
- 第四章 個人中心
- 4.1 原型
- 4.2 管道
- 4.3 對接后臺
- 4.4 x-auth-token認證
- 4.5 攔截器
- 4.6 小結
- 第五章 系統菜單
- 5.1 延遲及測試
- 5.2 手動創建組件
- 5.3 隱藏測試信息
- 5.4 規劃路由
- 5.5 定義菜單
- 5.6 注銷
- 5.7 小結
- 第六章 班級管理
- 6.1 新增班級
- 6.1.1 組件初始化
- 6.1.2 MockApi 新建班級
- 6.1.3 ApiInterceptor
- 6.1.4 數據驗證
- 6.1.5 教師選擇列表
- 6.1.6 MockApi 教師列表
- 6.1.7 代碼重構
- 6.1.8 小結
- 6.2 教師列表組件
- 6.2.1 初始化
- 6.2.2 響應式表單
- 6.2.3 getTestScheduler()
- 6.2.4 應用組件
- 6.2.5 小結
- 6.3 班級列表
- 6.3.1 原型設計
- 6.3.2 初始化分頁
- 6.3.3 MockApi
- 6.3.4 靜態分頁
- 6.3.5 動態分頁
- 6.3.6 @Input()
- 6.4 編輯班級
- 6.4.1 測試模塊
- 6.4.2 響應式表單驗證
- 6.4.3 @Input()
- 6.4.4 FormGroup
- 6.4.5 自定義FormControl
- 6.4.6 代碼重構
- 6.4.7 小結
- 6.5 刪除班級
- 6.6 集成測試
- 6.6.1 惰性加載
- 6.6.2 API攔截器
- 6.6.3 路由與跳轉
- 6.6.4 ngStyle
- 6.7 初識Service
- 6.7.1 catchError
- 6.7.2 單例服務
- 6.7.3 單元測試
- 6.8 小結
- 第七章 學生管理
- 7.1 班級列表組件
- 7.2 新增學生
- 7.2.1 exports
- 7.2.2 自定義驗證器
- 7.2.3 異步驗證器
- 7.2.4 再識DI
- 7.2.5 屬性型指令
- 7.2.6 完成功能
- 7.2.7 小結
- 7.3 單元測試進階
- 7.4 學生列表
- 7.4.1 JSON對象與對象
- 7.4.2 單元測試
- 7.4.3 分頁模塊
- 7.4.4 子組件測試
- 7.4.5 重構分頁
- 7.5 刪除學生
- 7.5.1 第三方dialog
- 7.5.2 批量刪除
- 7.5.3 面向對象
- 7.6 集成測試
- 7.7 編輯學生
- 7.7.1 初始化
- 7.7.2 自定義provider
- 7.7.3 更新學生
- 7.7.4 集成測試
- 7.7.5 可訂閱的路由參數
- 7.7.6 小結
- 7.8 總結
- 第八章 其它
- 8.1 打包構建
- 8.2 發布部署
- 第九章 總結