国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解如何在Angular優雅編寫HTTP請求

2019-11-19 12:24:22
字體:
來源:轉載
供稿:網友

引言

基本上當下的應用都會分為前端與后端,當然這種前端定義不在限于桌面瀏覽器、手機、APP等設備。一個良好的后端會通過一套所有前端都通用的 RESTful API 序列接口作為前后端之間的通信。

這其中對于身份認證都不可能再依賴傳統的Session或Cookie;轉而使用諸如OAuth2、JWT等這種更適合API接口的認證方式。當然本文并不討論如何去構建它們。

一、API 設計

首先雖然并不會討論身份認證的技術,但不管是OAuth2還是JWT本質上身份認證都全靠一個 Token 來維持;因此,下面統一以 token 來表示身份認證所需要的值。

一套合理的API規則,會讓前端編碼更優雅。因此,希望在編寫Angular之前,能與后端相互達成一種“協議”也很有必要。可以嘗試從以下幾點進行考慮。

版本號

可以在URL(例:https://demo.com/v1/)或Header(例:headers: { version: 'v1' } )中體現,相比較我更喜歡前者的直接。

業務節點

以一個節點來表示某個業務,比如:

  • 商品 https://demo.com/v1/product/
  • 商品SKU https://demo.com/v1/product/sku/

動作

由HTTP動詞來表示:

  • GET 請求一個商品 /product/${ID}
  • POST 新建一個商品 /product
  • PUT 修改一個商品 /product/${ID}
  • DELETE 刪除一個商品 /product/${ID}

統一響應

這一點非常重要,特別是當我們新建一個商品時,商品的屬性非常多,但如果我們缺少某個屬性時。可以使用這樣的一種統一的響應格式:

{  "code": 100, // 0 表示成功  "errors": { // 錯誤明細    "title": "商品名稱必填"  }}

其中 code 不管成功與否都會有該屬性。

狀態碼

后端響應一個請求是包括狀態碼和響應內容,而每一種狀態碼又包含著不同的含義。

  • 200 成功返回請求數據
  • 401 無權限
  • 404 無效資源

二、如何訪問Http?

首先,需要導入 HttpClientModule 模塊。

import { HttpClientModule } from '@angular/common/http';@NgModule({  imports: [    HttpClientModule  ]})

然后,在組件類注入 HttpClient。

export class IndexComponent {  constructor(private http: HttpClient) { }}

最后,請求點擊某個按鈕發送一次GET請求。

user: Observable<User>;getUser() {  this.user = this.http.get<User>('/assets/data/user.json');}

打印結果:

{{ user | async | json }}

三個簡單的步驟,就是一個完整的HTTP請求步驟。

然后,現實與實際是有一些距離,比如說身份認證、錯誤處理、狀態碼處理等問題,在上面并無任何體現。

可,上面已經足夠優雅,要讓我破壞這種優雅那么此文就變得無意義了!

因此……

三、攔截器

1、HttpInterceptor 接口

正如其名,我們在不改變上面應用層面的代碼下,允許我們把身份認證、錯誤處理、狀態碼處理問題給解決了!

寫一個攔截器也是非常的優雅,只需要實現 HttpInterceptor 接口即可,而且只有一個 intercept 方法。

@Injectable()export class JWTInterceptor implements HttpInterceptor {  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {    // doing  }}

intercept 方法有兩個參數,它幾乎所當下流行的中間件概念一般,req 表示當前請求數據(包括:url、參數、header等),next 表示調用下一個“中間件”。

2、身份認證

req 有一個 clone 方法,允許對當前的請求參數進行克隆并且這一過程會自行根據一些參數推導,不管如何用它來產生一個新的請求數據,并在這個新數據中加入我們期望的數據,比如:token。

const jwtReq = req.clone({  headers: req.headers.set('token', 'xxxxxxxxxxxxxxxxxxxxx')});

當然,你可以再折騰更多請求前的一些配置。

最后,把新請求參數傳遞給下一個“中間件”。

return next.handle(jwtReq);

等等,都 return 了,說好的狀態碼、異常處理呢?

3、異常處理

仔細再瞧 next.handle 返回的是一個 Observable 類型。看到 Observable 我們會想到什么?mergeMap、catch 等一大堆東西。

因此,我們可以利用這些操作符來改變響應的值。

mergeMap

請求過程中會會有一些過程狀態,比如請求前、上傳進度條、請求結束等,Angular在每一次這類動作中都會觸次 next。因此,我們只需要在返回 Observable 對象加上 mergeMap 來觀察這些值的變更,這樣有非常大的自由空間想象。

return next.handle(jwtReq).mergeMap((event: any) => {    if (event instanceof HttpResponse && event.body.code !== 0) {      return Observable.create(observer => observer.error(event));    }    return Observable.create(observer => observer.next(event));  })

只會在請求成功才會返回一個 HttpResponse 類型,因此,我們可以大膽判斷是否來源于 HttpResponse 來表示HTTP請求已經成功。

這里,統一對業務層級的錯誤 code !== 0 產生一個錯誤信號的 Observable。反之,產生一個成功的信息。

catch

catch 來捕獲非200以外的其他狀態碼的錯誤,比如:401。同時,前面的 mergeMap 所產生的錯誤信號,也會在這里被捕獲到。

.catch((res: HttpResponse<any>) => {  switch (res.status) {    case 401:      // 權限處理      location.href = ''; // 重新登錄      break;    case 200:      // 業務層級錯誤處理      alert('業務錯誤:' + res.body.code);      break;    case 404:      alert('API不存在');      break;  }  return Observable.throw(res);})

4、完整代碼

至此,攔截器所要包括的身份認證token、統一響應處理、異常處理都解決了。

@Injectable()export class JWTInterceptor implements HttpInterceptor {  constructor(private notifySrv: NotifyService) {}  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {    console.log('interceptor')    const jwtReq = req.clone({      headers: req.headers.set('token', 'asdf')    });    return next      .handle(jwtReq)      .mergeMap((event: any) => {        if (event instanceof HttpResponse && event.body.code !== 0) {          return Observable.create(observer => observer.error(event));        }        return Observable.create(observer => observer.next(event));      })      .catch((res: HttpResponse<any>) => {        switch (res.status) {          case 401:            // 權限處理            location.href = ''; // 重新登錄            break;          case 200:            // 業務層級錯誤處理            this.notifySrv.error('業務錯誤', `錯誤代碼為:${res.body.code}`);            break;          case 404:            this.notifySrv.error('404', `API不存在`);            break;        }        // 以錯誤的形式結束本次請求        return Observable.throw(res);      })  }}

發現沒有,我們并沒有加一大堆并不認識的事物,單純都只是對數據流的各種操作而已。

NotifyService 是一個無須依賴HTML模板、極簡Angular通知組件。

5、注冊攔截器

攔截器構建后,還需要將其注冊至 HTTP_INTERCEPTORS 標識符中。

import { HttpClientModule } from '@angular/common/http';@NgModule({  imports: [    HttpClientModule  ],  providers: [    { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true}  ]})

以上是攔截器的所有內容,在不改變原有的代碼的情況下,我們只是利用短短幾行的代碼實現了身份認證所需要的TOKEN、業務級統一響應處理、錯誤處理動作。

四、async 管道

一個 Observable 必須被訂閱以后才會真正的開始動作,前面在HTML模板中我們利用了 async 管道簡化了這種訂閱過程。

{{ user | async | json }}

它相當于:

let user: User;get() {  this.http.get<User>('/assets/data/user.json').subscribe(res => {    this.user = res;  });}{{ user | json }}

然而,async 這種簡化,并不代表失去某些自由度,比如說當在獲取數據過程中顯示【加載中……】,怎么辦?

<div *ngIf="user | async as user; else loading">  {{ user | json }}</div><ng-template #loading>加載中……</ng-template>

恩!

五、結論

Angular在HTTP請求過程中使用 Observable 異步數據流控制數據,而利用 rxjs 提供的大量操作符,來改變最終值;從而獲得在應用層面最優雅的編碼風格。

當我們說到優雅使用HTTP這件事時,易測試是一個非常重要,因此,我建議將HTTP從組件類中剝離并將所有請求放到 Service 當中。當對某個組件編寫測試代碼時,如果受到HTTP請求結果的限制會讓測試更困難。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 灵璧县| 怀安县| 双城市| 安远县| 大理市| 错那县| 莱西市| 平顺县| 云南省| 甘孜县| 赣榆县| 教育| 象州县| 广灵县| 宁安市| 兴仁县| 庆阳市| 西贡区| 外汇| 米林县| 乌鲁木齐县| 札达县| 会昌县| 泾源县| 广汉市| 淳安县| 云南省| 藁城市| 绥芬河市| 郎溪县| 浦县| 武胜县| 高陵县| 辰溪县| 江北区| 玛纳斯县| 永昌县| 民勤县| 永嘉县| 大田县| 枣阳市|