内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

詳解如何在Angular優(yōu)雅編寫HTTP請(qǐng)求

引言

創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比騰沖網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式騰沖網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋騰沖地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴。

基本上當(dāng)下的應(yīng)用都會(huì)分為前端與后端,當(dāng)然這種前端定義不在限于桌面瀏覽器、手機(jī)、APP等設(shè)備。一個(gè)良好的后端會(huì)通過(guò)一套所有前端都通用的 RESTful API 序列接口作為前后端之間的通信。

這其中對(duì)于身份認(rèn)證都不可能再依賴傳統(tǒng)的Session或Cookie;轉(zhuǎn)而使用諸如OAuth3、JWT等這種更適合API接口的認(rèn)證方式。當(dāng)然本文并不討論如何去構(gòu)建它們。

一、API 設(shè)計(jì)

首先雖然并不會(huì)討論身份認(rèn)證的技術(shù),但不管是OAuth3還是JWT本質(zhì)上身份認(rèn)證都全靠一個(gè) Token 來(lái)維持;因此,下面統(tǒng)一以 token 來(lái)表示身份認(rèn)證所需要的值。

一套合理的API規(guī)則,會(huì)讓前端編碼更優(yōu)雅。因此,希望在編寫Angular之前,能與后端相互達(dá)成一種“協(xié)議”也很有必要??梢試L試從以下幾點(diǎn)進(jìn)行考慮。

版本號(hào)

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

業(yè)務(wù)節(jié)點(diǎn)

以一個(gè)節(jié)點(diǎn)來(lái)表示某個(gè)業(yè)務(wù),比如:

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

動(dòng)作

由HTTP動(dòng)詞來(lái)表示:

  • GET 請(qǐng)求一個(gè)商品 /product/${ID}
  • POST 新建一個(gè)商品 /product
  • PUT 修改一個(gè)商品 /product/${ID}
  • DELETE 刪除一個(gè)商品 /product/${ID}

統(tǒng)一響應(yīng)

這一點(diǎn)非常重要,特別是當(dāng)我們新建一個(gè)商品時(shí),商品的屬性非常多,但如果我們?nèi)鄙倌硞€(gè)屬性時(shí)??梢允褂眠@樣的一種統(tǒng)一的響應(yīng)格式:

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

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

狀態(tài)碼

后端響應(yīng)一個(gè)請(qǐng)求是包括狀態(tài)碼和響應(yīng)內(nèi)容,而每一種狀態(tài)碼又包含著不同的含義。

  • 200 成功返回請(qǐng)求數(shù)據(jù)
  • 401 無(wú)權(quán)限
  • 404 無(wú)效資源

二、如何訪問(wèn)Http?

首先,需要導(dǎo)入 HttpClientModule 模塊。

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule
  ]
})

然后,在組件類注入 HttpClient。

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

最后,請(qǐng)求點(diǎn)擊某個(gè)按鈕發(fā)送一次GET請(qǐng)求。

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

打印結(jié)果:

{{ user | async | json }}

三個(gè)簡(jiǎn)單的步驟,就是一個(gè)完整的HTTP請(qǐng)求步驟。

然后,現(xiàn)實(shí)與實(shí)際是有一些距離,比如說(shuō)身份認(rèn)證、錯(cuò)誤處理、狀態(tài)碼處理等問(wèn)題,在上面并無(wú)任何體現(xiàn)。

可,上面已經(jīng)足夠優(yōu)雅,要讓我破壞這種優(yōu)雅那么此文就變得無(wú)意義了!

因此……

三、攔截器

1、HttpInterceptor 接口

正如其名,我們?cè)诓桓淖兩厦鎽?yīng)用層面的代碼下,允許我們把身份認(rèn)證、錯(cuò)誤處理、狀態(tài)碼處理問(wèn)題給解決了!

寫一個(gè)攔截器也是非常的優(yōu)雅,只需要實(shí)現(xiàn) HttpInterceptor 接口即可,而且只有一個(gè) intercept 方法。

@Injectable()
export class JWTInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
    // doing
  }

}

intercept 方法有兩個(gè)參數(shù),它幾乎所當(dāng)下流行的中間件概念一般,req 表示當(dāng)前請(qǐng)求數(shù)據(jù)(包括:url、參數(shù)、header等),next 表示調(diào)用下一個(gè)“中間件”。

2、身份認(rèn)證

req 有一個(gè) clone 方法,允許對(duì)當(dāng)前的請(qǐng)求參數(shù)進(jìn)行克隆并且這一過(guò)程會(huì)自行根據(jù)一些參數(shù)推導(dǎo),不管如何用它來(lái)產(chǎn)生一個(gè)新的請(qǐng)求數(shù)據(jù),并在這個(gè)新數(shù)據(jù)中加入我們期望的數(shù)據(jù),比如:token。

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

當(dāng)然,你可以再折騰更多請(qǐng)求前的一些配置。

最后,把新請(qǐng)求參數(shù)傳遞給下一個(gè)“中間件”。

return next.handle(jwtReq);

等等,都 return 了,說(shuō)好的狀態(tài)碼、異常處理呢?

3、異常處理

仔細(xì)再瞧 next.handle 返回的是一個(gè) Observable 類型??吹?Observable 我們會(huì)想到什么?mergeMap、catch 等一大堆東西。

因此,我們可以利用這些操作符來(lái)改變響應(yīng)的值。

mergeMap

請(qǐng)求過(guò)程中會(huì)會(huì)有一些過(guò)程狀態(tài),比如請(qǐng)求前、上傳進(jìn)度條、請(qǐng)求結(jié)束等,Angular在每一次這類動(dòng)作中都會(huì)觸次 next。因此,我們只需要在返回 Observable 對(duì)象加上 mergeMap 來(lái)觀察這些值的變更,這樣有非常大的自由空間想象。

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));
  })

只會(huì)在請(qǐng)求成功才會(huì)返回一個(gè) HttpResponse 類型,因此,我們可以大膽判斷是否來(lái)源于 HttpResponse 來(lái)表示HTTP請(qǐng)求已經(jīng)成功。

這里,統(tǒng)一對(duì)業(yè)務(wù)層級(jí)的錯(cuò)誤 code !== 0 產(chǎn)生一個(gè)錯(cuò)誤信號(hào)的 Observable。反之,產(chǎn)生一個(gè)成功的信息。

catch

catch 來(lái)捕獲非200以外的其他狀態(tài)碼的錯(cuò)誤,比如:401。同時(shí),前面的 mergeMap 所產(chǎn)生的錯(cuò)誤信號(hào),也會(huì)在這里被捕獲到。

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

4、完整代碼

至此,攔截器所要包括的身份認(rèn)證token、統(tǒng)一響應(yīng)處理、異常處理都解決了。

@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:
            // 權(quán)限處理
            location.href = ''; // 重新登錄
            break;
          case 200:
            // 業(yè)務(wù)層級(jí)錯(cuò)誤處理
            this.notifySrv.error('業(yè)務(wù)錯(cuò)誤', `錯(cuò)誤代碼為:${res.body.code}`);
            break;
          case 404:
            this.notifySrv.error('404', `API不存在`);
            break;
        }
        // 以錯(cuò)誤的形式結(jié)束本次請(qǐng)求
        return Observable.throw(res);
      })
  }
}

發(fā)現(xiàn)沒(méi)有,我們并沒(méi)有加一大堆并不認(rèn)識(shí)的事物,單純都只是對(duì)數(shù)據(jù)流的各種操作而已。

NotifyService 是一個(gè)無(wú)須依賴HTML模板、極簡(jiǎn)Angular通知組件。

5、注冊(cè)攔截器

攔截器構(gòu)建后,還需要將其注冊(cè)至 HTTP_INTERCEPTORS 標(biāo)識(shí)符中。

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true}
  ]
})

以上是攔截器的所有內(nèi)容,在不改變?cè)械拇a的情況下,我們只是利用短短幾行的代碼實(shí)現(xiàn)了身份認(rèn)證所需要的TOKEN、業(yè)務(wù)級(jí)統(tǒng)一響應(yīng)處理、錯(cuò)誤處理動(dòng)作。

四、async 管道

一個(gè) Observable 必須被訂閱以后才會(huì)真正的開始動(dòng)作,前面在HTML模板中我們利用了 async 管道簡(jiǎn)化了這種訂閱過(guò)程。

{{ user | async | json }}

它相當(dāng)于:

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

然而,async 這種簡(jiǎn)化,并不代表失去某些自由度,比如說(shuō)當(dāng)在獲取數(shù)據(jù)過(guò)程中顯示【加載中……】,怎么辦?

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

恩!

五、結(jié)論

Angular在HTTP請(qǐng)求過(guò)程中使用 Observable 異步數(shù)據(jù)流控制數(shù)據(jù),而利用 rxjs 提供的大量操作符,來(lái)改變最終值;從而獲得在應(yīng)用層面最優(yōu)雅的編碼風(fēng)格。

當(dāng)我們說(shuō)到優(yōu)雅使用HTTP這件事時(shí),易測(cè)試是一個(gè)非常重要,因此,我建議將HTTP從組件類中剝離并將所有請(qǐng)求放到 Service 當(dāng)中。當(dāng)對(duì)某個(gè)組件編寫測(cè)試代碼時(shí),如果受到HTTP請(qǐng)求結(jié)果的限制會(huì)讓測(cè)試更困難。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享文章:詳解如何在Angular優(yōu)雅編寫HTTP請(qǐng)求
URL地址:http://m.rwnh.cn/article14/jejgde.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、微信公眾號(hào)、、企業(yè)網(wǎng)站制作、用戶體驗(yàn)、網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)
平乡县| 中阳县| 冀州市| 米泉市| 阿瓦提县| 儋州市| 定兴县| 化德县| 贵溪市| 华池县| 保康县| 安陆市| 开鲁县| 彭州市| 陕西省| 丘北县| 银川市| 永康市| 榕江县| 驻马店市| 宝山区| 政和县| 高清| 磐安县| 阿克苏市| 会同县| 攀枝花市| 满洲里市| 济源市| 新竹市| 诸城市| 水城县| 定安县| 峨眉山市| 黔西| 安泽县| 溧水县| 宽甸| 瑞安市| 临城县| 江陵县|