<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                在繼續學習如何在前臺獲取后臺返回的token以前,先帶領大家了解下RxJS操作符。 可能你還不清楚什么是RxJS,但實際上你早早的就已經用上它了。每寫一次`this.httpClient().subscribe()`,你便使用了一次RxJS。 至于RxJS具體是什么,又有著什么樣的功能。怕是需要一個單獨的教程來使用較長的篇幅來介紹它。而我們在此需要清楚的是RxJS是一種編程的模式(思想),具體來講是一種面向數據流的編程思想。 # 生活中的數據流 筆者所在的院校至今還保持著訂閱報刊的習慣,如果對某些報刊有需求可以選擇在開學初向教務員提出訂閱申請,在預算允許的情況下。此后每個月(月刊)或每半個月(半月刊)都會收到在開學初訂閱的報刊。不僅如何,有時候還會收到一些意想不到的增刊(臨時增加印刷的)。而這個報刊訂閱便像極了RxJS的數據流。 對筆者而言,教務員便是數據流的來源;而教務員是不提供發行報刊的服務的,所以其訂閱報刊時需要找到校級部門,而報刊最終也是由校級部門進行分發;同樣的,校級部門向上可以需要找郵局,依次累推最終達到雜志社。下發報刊的過程也相同,雜志社將報刊向下交付,然后逐級的交到筆者的手中。而最終交付的報刊就是剛剛提到的**數據**,多份報刊不定期的由雜志社向讀者傳遞,便是**數據流**。 如果RxJS中的數據流和現實生活中的報刊訂閱是一個概念的話,那么我猜想它一定具有以下的性質: 1. 在進行報刊訂閱時,由于是逐級向上訂閱的,所以訂閱過程可能失敗(比如領導自認為該報刊不適合訂閱或經費不足);那么RxJS的數據訂閱過程也必然會面臨失敗的情況。 2. 報刊發行后,可能由于其內容過于吸引人,可能會被校級或院級教務員扣留,待其閱讀后再分發給筆者;那么RxJS的數據發行,也必然可能出現被中間的轉發者暫緩扣留而未得到及時轉發的可能。 3. 報刊發行后,由于校級教務員的疏忽,可能導致忘記分發給筆者了;那么RxJS也必然存在在發行過程中,數據中斷的情況。 4. 基于3,當教務員收到下一期的報刊時,突然發現上一期的還在這呢,此時一并發給筆者;那么RxJS也必然存在將兩份數據員合并后發送給讀者的情況。 5. 筆者訂閱的兩份報刊總是先后隔一天送達,教務員感覺兩天送兩次太難受,所以每次都是等兩份報刊到齊了以后再統一發送給筆者;那么RxJS也必然存在訂閱了兩個數據源后,只有當滿足兩個數據源全部返回后才下發數據的情況。 6. 報刊發行后,教務員提前瀏覽的時候,不小心將報刊損壞或不經意的在報刊的內容上進行了批注,然后將損壞或批注的報刊給了筆者;那么RxJS中在進行數據傳遞的過程中,必然會出現數據被修改后才下發的情況。 7. 報刊發行后,教務員對自己感興趣的報刊進行閱讀后再下發給筆者,而對不感興趣的報刊選擇直接下發給筆者;那么RxJS中必然可以設置某些條件,當滿足條件時對數據進行處理,而當不滿足條件時直接下發數據。 沒錯,只要在現實生活中能想到的報刊訂閱的情況RxJS均可滿足!RxJS中提供了豐富的**操作符**來操作返回的數據流,每個操作符都有著其特殊的功能。此外如果RxJS中提供的操作符不能滿足一些對數據流處理的要求,我們還可以自定義自己的操作符。 # 在響應header中獲取auth-token 攔截器的本質正是生活中的"教務員",它接收其它訂閱者的訂閱,并將此后的數據按訂閱的情況分發給對應的訂閱者,對于"院教務員"而言,報刊訂閱反饋到代碼中如下: src/app/core/auth-token-interceptor.ts ```javascript intercept(req: HttpRequest<any> ?, next: HttpHandler ?): Observable<HttpEvent<any>> ? { return? next.handle(reqClone) ? } ``` * ? 向其發起訂閱的筆者 * ? 用以發起訂閱的校教務員 * ? 訂閱的報刊種類 * ? 向校教務員發起訂閱 * ? 將校教務員送達的報刊發送給筆者 # pipe() 在RxJS中,可以使用pipe()方法處理一些分發的數據。pipe方法接收一個或多個參數,每個參數都是一個**操作符**,被分發的數據在轉發以前,依次通過pipe中規定的幾個**操作符**。比如: ```javascript return next.handle(reqClone).pipe(操作符1(傳入的數據0) => {處理后的數據1}, 操作符2(傳入的數據1) => {處理后的數據2}) ``` ![](https://img.kancloud.cn/e9/bf/e9bfd21cd06593527c8a110d8d72bfca_558x90.png) ## demo 訂閱、發送報刊與訂閱、發送數字是一個道理,在此簡單展示下RxJS是如何發送由數字組成的數據流以及如何使用pipe操作即將下發的數據流的。 情景:每1秒種發送一個0-100的隨機數字,如果生成的是數字尾數是1則終止發送。有三個操作符來影響它,第一個操作符:將得到的數字進行平方,再將平方以后的數字向后發送;第二個操作符:對得到的數據與10進行模運算,將取模后的值向后發送;第三個操作符:如果得到的數據是奇數,則向后發送,否則不發送。則示例代碼如下: src/app/core/auth-token-interceptor.ts ```javascript import {Observable, Subject} from 'rxjs'; import {filter, map} from 'rxjs/operators'; ... intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const sendNumbers = new Subject<number>(); ? const pipeNumber = sendNumbers.pipe?(map?((x) => { console.log('1接收到的數字為 ' + x); return x * x; }), map(x => { console.log('2接收到的數字為 ' + x); return x % 10; }), filter?(x => { console.log('3接收到的數字為 ' + x); return x % 2 === 0; ? })); pipeNumber.subscribe((value) => { console.log('接收到了 ' + value); }); const sendNumber = () => setTimeout(() => { ? const n = Math.floor(Math.random() * 100); console.log('生成的數字是' + n.toString()); sendNumbers.next(n); ? if (n % 10 !== 1) { sendNumber(); } }, 1000); sendNumber(); ? const reqClone = req.clone({ setHeaders: {'auth-token': 'af1c0c77-67d0-4ec2-8321-2f88e32f76af'} }); return next.handle(reqClone); } ``` * ? 定義了一個可以發送數字的數據源 * ? 使用pipe對接收到的數據進行**操作** * ? map是一個**操作符**,它的作用時:接收數據、處理數字、轉發處理后的數據 * ? filter也是一個**操作符**,它的作用時:當接收到的值滿足其中規定的條件時,則轉發數據。否則不轉發。 * ? 此條件為true時?轉發數據,為false時不轉發數據。 * ? 每1秒鐘發送1個數據 * ? 發送數據 運行結果如下: ``` auth-token-interceptor.ts:28 生成的數字是42 auth-token-interceptor.ts:12 1接收到的數字為 42 auth-token-interceptor.ts:15 2接收到的數字為 1764 auth-token-interceptor.ts:18 3接收到的數字為 4 auth-token-interceptor.ts:23 接收到了 4 auth-token-interceptor.ts:28 生成的數字是65 auth-token-interceptor.ts:12 1接收到的數字為 65 auth-token-interceptor.ts:15 2接收到的數字為 4225 auth-token-interceptor.ts:18 3接收到的數字為 5 auth-token-interceptor.ts:28 生成的數字是74 auth-token-interceptor.ts:12 1接收到的數字為 74 auth-token-interceptor.ts:15 2接收到的數字為 5476 auth-token-interceptor.ts:18 3接收到的數字為 6 auth-token-interceptor.ts:23 接收到了 6 auth-token-interceptor.ts:28 生成的數字是95 auth-token-interceptor.ts:12 1接收到的數字為 95 auth-token-interceptor.ts:15 2接收到的數字為 9025 auth-token-interceptor.ts:18 3接收到的數字為 5 auth-token-interceptor.ts:28 生成的數字是91 auth-token-interceptor.ts:12 1接收到的數字為 91 auth-token-interceptor.ts:15 2接收到的數字為 8281 auth-token-interceptor.ts:18 3接收到的數字為 1 ``` # 獲取auth-token 終于到了使用pipe來獲取token的時候了: src/app/core/auth-token-interceptor.ts ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': 'af1c0c77-67d0-4ec2-8321-2f88e32f76af'} }); return next.handle(reqClone).pipe?(map?((httpEvent) => { console.log(httpEvent); ? return httpEvent; })); } ``` * ? 使用pipe設置操作數據的操作符 * ? map操作符的作用的是:將return的結果進行轉發 * ? 打印個日志看看 控制臺顯示該變量的值類型為HttpResponse,對應的基本信息如下: ``` { "headers": { "normalizedNames": {}, "lazyUpdate": null }, "status": 200, "statusText": "OK", "url": "http://localhost:8080/Teacher/", "ok": true, "type": 4, "body": [ { "id": 1, "name": "panjie", "sex": false, "username": "yqac", "email": "3792535@qq.com", "createTime": 0, "updateTime": 0 } ] } ``` 近一步進行類型轉化來獲取auth-token的值。 src/app/core/auth-token-interceptor.ts ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': 'af1c0c77-67d0-4ec2-8321-2f88e32f76af'} }); return next.handle(reqClone).pipe(map((httpEvent) => { if (httpEvent instanceof HttpResponse) { ? const httpResponse = httpEvent as HttpResponse<any>; ? const authToken = httpResponse.headers.get('auth-token'); console.log('獲取到的authToken為' + authToken); } return httpEvent; })); } ``` * ? 使用if進行類型判斷,以防止異常(HttpEvent不止HttpResponse一種類型) * ? 類型轉換 * ? 獲取authToken 測試: ![](https://img.kancloud.cn/ec/aa/ecaa06331f1e34cd57c4e0109eb0423c_313x71.png) 很失望,竟然獲取到了null,查看網絡確認是否真的返回了auth-token呢? ![](https://img.kancloud.cn/c0/21/c02115ded23e1568ffb0809c9d9070f9_468x312.png) 詭異的事情發生了,在控制臺中明明返回了auth-token,但為什么獲取不到呢?莫非`httpResponse.headers.get('auth-token');`這個寫法不正確嗎?答案并不是這樣的。`httpResponse.headers.get('auth-token');`這個寫法完全正確,之所以在此時獲取不到`auth-token`是由瀏覽器的安全策略決定的。為了某些安全方面的問題,瀏覽器僅允許js獲`Cache-Control`,`Content-Language`,`Content-Type`,`Expires`,`Last-Modified`以及`Pragma`幾個header信息。而若要獲取其它的header的信息,則需要由在header中返回`Access-Control-Expose-Headers 訪問控制允許暴露的headers`來指定。但當前的后臺并未指定允許暴露的任何header信息,所以此時獲取`auth-token`是不被瀏覽器所允許的。 解決的方法是打開后臺,設置`Access-Control-Expose-Headers`并將`auth-token`加入其中。 config/WebConfig.java ```java public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:4200") .allowedMethods("PUT", "DELETE", "POST", "GET", "PATCH") .exposedHeaders("auth-token"); ? } ``` * ? 設置允許前臺獲取的額外header信息 重新啟動后臺后再次測試: ![](https://img.kancloud.cn/c1/c0/c1c0800f5fe22144e67d105dcfa7f183_529x110.png) # 緩存auth-token 行百里者,半九十。最后建立緩存服務把獲取到的token信息緩存起來,以便在下起發起請求的時候,加入此auth-token信息。這樣便能夠達到在非首次請求中使用首次請求獲取到的auth-token的目的。 ``` panjiedeMac-Pro:web-app panjie$ cd src/app/service/ panjiedeMac-Pro:service panjie$ ng g s cache CREATE src/app/service/cache.service.spec.ts (328 bytes) CREATE src/app/service/cache.service.ts (134 bytes) ``` 初始化如下: src/app/service/cache.service.ts ```javascript import {Injectable} from '@angular/core'; /** * 緩存 */ @Injectable({ providedIn: 'root' }) export class CacheService { /** 認證令牌 */ private static authToken: string = undefined; constructor() { } static setAuthToken(token: string) { CacheService.authToken = token; } static getAuthToken() { if (CacheService.authToken === undefined) { return ''; } return CacheService.authToken; } } ``` 在攔截器中使用緩存 src/app/core/auth-token-interceptor.ts ```javascript intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const reqClone = req.clone({ setHeaders: {'auth-token': CacheService.getAuthToken()?} }); return next.handle(reqClone).pipe(map((httpEvent) => { if (httpEvent instanceof HttpResponse) { const httpResponse = httpEvent as HttpResponse<any>; const authToken = httpResponse.headers.get('auth-token'); CacheService.setAuthToken(authToken)?; } return httpEvent; })); } ``` 先后點擊次教師管理進行測試: ![](https://img.kancloud.cn/fc/df/fcdfc9685189147b27eea0824cf1952d_765x561.gif) 如上圖所示: 首次訪問時傳入了空的auth-token,接收到了后臺分發的auth-token: `70fb8229-7cf9-4326-b5a6-dbe911bf8e51`。進行第二次訪問時,前臺自動攜帶了該auth-token向后臺發起了請求,同時后臺在響應將發起的請示的token原值返回。這符合我們設定的令牌認證邏輯: ![](https://img.kancloud.cn/2a/cd/2acdc94ee4440132f4a062dbf7232b9b_809x471.png) # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.5](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.5) | - | | Access-Control-Expose-Headers | [https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) | - | |RxJS 庫 | [https://angular.cn/guide/rx-library](https://angular.cn/guide/rx-library) | 15 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看