浅谈Angular网络请求
在Angular網絡請求是一個最常見的應用之一,下列我將以 ng-alain 項目為基礎描述 Angular 網絡請求。
注:示例中代碼都以簡化的形式出現。寫在前面
Angular發起一個請求再簡單不過即使用 HttpClient 類的各種方法,然在開始之前我們應退一小步,先從如何構建一個 Restful API 開始,后端的API設計將很大程度決定前后端如何更優雅的開發有著非常大的關鍵性作用。
一、RESTful API 設計
私以為API的設計分為請求與輸出兩個部分。而連接二者是依靠URL,關于URL如何更合理的設計可以參考阮一峰-RESTful API 設計指南。
這一部分要談另一個可能大家容易忽略的細節,請求體與返回體規范。這一點淘寶開放平臺是一個非常好的典范,例如所有異常返回體:
{"sub_msg":"非法參數","code":50,"sub_code":"isv.invalid-parameter","msg":"Remote service error" }所有這些規則可以由內部自行決議,再比如我們中后臺經常使用的是一種方式,所有返回體不管成功與否都包含以下對象:
{"msg": "ok","data": null }以 msg 來判斷 ok 值表示成功,對于其他值表示允許直接顯示給用戶錯誤文本異常文本。
對于提交 POST 請求體的數據格式(content-type)主要兩種比較常見:表單格式和JSON格式,二者也可能根據不同場景情況使用特別是文件上傳動作;當然對于大部分場景而言 JSON 格式最優先的形式,不管你是使用 Angular 表單的HTML模板或響應式驅動表單都是直接跟JSON打交道。
二、請求流程
在 ng-alain 中,一個完整的 Angular 應用從前端 UI 交互到服務端處理流程是這樣的:
1、首次啟動 Angular 執行 APP_INITIALIZER;
2、UI 組件交互操作;
3、使用 HttpClient 發送請求;
4、觸發用戶認證攔截器 @delon/auth,統一加入 token 參數;
5、觸發默認攔截器,統一處理前綴等信息;
6、獲取服務端返回;
7、觸發默認攔截器,統一處理請求異常、業務異常等;
8、數據更新,并刷新 UI。
1、APP_INITIALIZER
應用初始化是在應用啟動過程中有且只執行一次,一般來講我們需要在應用一啟動時加載一些數據:應用信息、通用數據字典、用戶數據等。
只需要向 APP_INITIALIZER 注冊一個帶有 Promise 返回值即可;例如:
{provide: APP_INITIALIZER,useValue: () => new Promise(() => {}),multi: true }正因為是一個 Promise 異步,我們就可以在這里利用 HttpClient 做網絡請求,從而實現在 Angular 啟動之前通過網絡請求獲取一個啟用后一開始就需要的數據。
注:當然在這里發起的網絡請求攔截器依然有效,若攔截器包含一些用戶 Token 的有效性校驗而導致跳轉至登錄頁時,可能要小心處理了。
但不管如何最終你想啟動 Angular 都必須確保 Promise 正確的調用 resolve()。
2、HttpClient
HttpClient 是 Angular 封裝了一個簡化的 API 來實現 HTTP 客戶端功能,例如一個 get 請求:
constructor(http: HttpClient) {http.get('/user/1').subscribe((user) => {console.log(user);}); }另一個 post 請求:
constructor(http: HttpClient) {http.post('/user/1', { a: 1 }).subscribe((user) => {console.log(user);}); }所有請求類型返回的結果都是 Observable<any> 類型,意味著不管如果你都必須調用 subscribe 才會真正的發起請求。大多數情況下你可能會覺得很麻煩,但當你需要一些節流或數據轉換時就顯得 rxjs 的魅力,有關更多細節自行Google rxjs。
3、攔截器
攔截網絡請求或響應,用于統一處理請求或響應結果數據。并且可以運用多個攔截器且按順序執行,類似于 Node 中間件。
一個簡單示例
只需要簡單實現 HttpInterceptor 接口即可:
export class SimpleInterceptor implements HttpInterceptor {intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {const newReq = req.clone();return next.handle(newReq).pipe()} }攔截器返回的結果是一個 Observable 值,這意味著同一個攔截器代碼包含著請求和響應兩個部分的處理,所有在 Angular 攔截器里并沒有明確區分請求和響應處理,這也是 rxjs 的魅力。
使用 req.clone() 克隆一些新的請求體,當然請求體包含著所有 HttpClient 發起數據及參數。例如給所有請求體的 headers 加入用戶 Token 值。
const newReq = req.clone({setHeaders: { Authorization: `Bearer ${this.token}` }, });當響應體網絡狀態碼非 401 時,打算跳轉至登錄頁,則:
return next.handle(newReq).pipe(catchError(err => {if (err.status === 401) {this.injector.get(Router).navigateByUrl('/login');}}))最后,在模塊里注冊,若你希望在整個應用有效可以在根模塊里注冊:
{ provide: HTTP_INTERCEPTORS, useClass: SimpleInterceptor, multi: true },攔截器順序
攔截器可以注冊在任何模塊里,而一個網絡請求所經過攔截器從模塊向上查找至根模塊,若一個模塊包含多個攔截器時按代碼順序執行。
三、ng-alain 請求處理
ng-alain 默認裝載了兩個攔截器:@delon/auth 用戶認證和默認攔截器。
1、用戶認證
本身是為 ng-alain 腳手架提供的一個用戶認證模塊,包含主流的 JWT(Json Web Token)和一個相對通用 Simple Web Token,而其核心是對認證過程進一步處理。而通常其核心在于用戶 Token 的獲取、使用環節。同時,@delon/auth 并不會關心用戶界面是怎么樣,只需要當登錄成功后將后端返回的數據交給 ITokenService,它會幫你存儲在 localStorage(默認) 當中;當發起一個網絡請求時,它會在自動在 header(默認) 當中加入相應的 token 信息。
因此,@delon/auth 不限于 ng-alain 腳手架,任何 Angular 項目都可以使用它。
默認裝載了 SimpleInterceptor 攔截器,意味者一開始使用 ng-alain 為什么會無緣無故無法正確請求,而是直接拋出異常。
ng-alain 是一個完整且可直接運用項目的腳手架,因此所有默認配置都盡可能生產環境中代碼,其實理解這一點很重要,因為大部分一開始總希望使用一個 Hello World 請求來決定是不是真的可以使用。
有關更多細節請參考文檔。
2、默認攔截器
DefaultInterceptor 攔截器,它是一個默認攔截器示例代碼,包含請求體和響應體的處理。
例如當我們統一響應體如下:
{"msg": "ok","data": { id: 1, name: "cipchk" } }對于 subscribe 結果來說只需要關心 data 部分,因此可以在攔截器進一步轉化:
return of(new HttpResponse(Object.assign(event, { body: body.data })));使在訂閱結果時給保持一個最簡單有效數據:
http.get('/user/1').subscribe(user => console.log(user)); // output: { id: 1, name: "cipchk" }更多做法,例如:統一處理異常消息等,可以參考 default.interceptor.ts 的寫法。
總結
Angular 網絡請求看起來就像一個簡化版的 Web 服務,發起的請求經過一道道關卡后,接收響應結果時又經過原先經過的一道道關卡最后交給用戶。
當然這一切的本質還是 rxjs 帶來的。曾經有人提過為什么 ng-alain 不采用 Redux 形式,但我實在找不到有什么理由要這么做,大部分中后臺都以網絡請求來完成大部分事務,而 Angular 網絡請求又那么清晰。
(完)
總結
以上是生活随笔為你收集整理的浅谈Angular网络请求的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 带你一起撸一遍 nodejs 常用核心模
- 下一篇: Java 集合系列(三)Collecti