2016-10-04 90 views
13

嘗試處理OAuth登錄情景,如果用戶在查詢字符串中登錄頁面authorization_code,我們將處理該令牌並繼續,如果它們沒有登陸頁面,我們檢查本地存儲的現有令牌,確保它仍然有效,並根據其有效性重定向到登錄或繼續。Angular2查詢參數訂閱觸發兩次

問題是,在我們檢查authorization_code查詢字符串param的存在的情況下,預訂會觸發兩次。第一次它是空的,第二次它在字典中有正確的值。

app.component.ts

export class App implements OnInit { 
    constructor(private _router: ActivatedRoute) { 
    } 

    public ngOnInit(): void { 
     console.log('INIT'); 
     this._route.queryParams.subscribe(params => { 
      console.log(params); 
     }); 
    } 
} 

此代碼輸出: output

Plunker(您需要將其彈出到一個新的窗口,並添加查詢字符串?test=test)。

問題

  1. 有什麼我做錯了,使之火兩次?
  2. 我不能忽略帶有條件的空對象,因爲這是我們需要驗證現有身份驗證令牌的場景 - 是否有另一種方法來處理這不是完整的黑客?
+0

一個有趣的轉折......當未提供查詢字符串,認購僅觸發一次。 – lukiffer

+1

我測試了它[https://embed.plnkr.co/XgCauzZdTSqqokCdFWCi/?test=test](https://embed.plnkr.co/XgCauzZdTSqqokCdFWCi/?test=test),沒有任何意外的事情發生! –

+0

@A_Singh你測試了錯誤的URL,所以你只能得到第一個空值 – yurzui

回答

12

路由器可觀測量(如另一種答案提及)are BehaviorSubject subjects,它們與常規RxJS在於它們具有的初始值推到序列Subject或角2 EventEmitter(在queryParams的情況下的空的對象)是不同的。

一般情況下,訂閱初始化邏輯的可能性是可取的。

初始值可以用skip運算符跳過。

this._route.queryParams 
.skip(1) 
.subscribe(params => ...); 

但更自然地處理這種方式是過濾掉所有無關PARAMS(初始params屬於這一類)。重複的authorization_code值也可以使用distinctUntilChanged運算符進行過濾以避免不必要的後端調用。

this._route.queryParams 
.filter(params => 'authorization_code' in params) 
.map(params => params.authorization_code) 
.distinctUntilChanged() 
.subscribe(authCode => ...); 

注意,角度2出口RxJS運營商的有限數量的(至少在map@angular/router的情況下)。如果未使用完整rxjs/Rx捆綁包,則可能需要導入與import 'rxjs/add/operator/<operator_name>'一起使用的額外運算符(filter,distinctUntilChanged)。

+0

+1以瞭解並解釋問題的根源。但是答案沒有解決這個問題,因爲缺少authorization_code需要在另一個代碼路徑上發送它。 – lukiffer

+0

這個問題並不清楚你期望如何處理重定向。無論如何,我相信第一個代碼片段覆蓋了RxJS最常用的方式。 BehaviorSubject會導致額外的值並跳過(1)將其消除。 – estus

+1

在第一個示例中,如果沒有queryParams,則第一個「空」對象將被跳過,並且該訂閱塊將永遠不會被調用。 – Failpunk

2

我想這是設計。

queryParamsBehaviorSubject

正如你可以在docs

一個主題的變體是BehaviorSubject,其中有一個 概念「當前值」的看。它存儲發送給其消費者的最新值 ,並且每當新Observer訂閱時,它將立即從BehaviorSubject接收「當前值」。

至於解決方法,您可以使用debounceTime操作如下:

import 'rxjs/add/operator/debounceTime'; 

this._route.queryParams 
    .debounceTime(200) 
    .subscribe(params => { 
    console.log(params); 
    }); 
+0

這在大部分時間都有效,但不喜歡超過debounceTime超時的可能性。無論他們登陸哪個頁面,都需要處理這個查詢參數,並且隨着加載時間的變化,這不起作用。 – lukiffer

+0

這不是一個好的解決方案,因爲它依賴任意的時間值來減慢應用程序的運行速度,或者如果時間不滿足,就會完全中斷它。 – Doughy

-1

只需使用Location類來獲得初始URL,UrlSerializer類來解析URL,UrlTree獲得查詢參數。

+0

雖然這並沒有解決我用'Observable '解決的技術問題,它確實解決了我們的特定用例。 – lukiffer

+1

您能舉例 – TeodorKolev

+2

這是被接受的答案嗎? – Riscie

0

如果你去這個鏈接https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil

1)複製粘貼在代碼編輯器的代碼。

/* since queryParams is a BehaviorSubject */ 
var queryParams = new Rx.BehaviorSubject();//this will AUTOMATICALLY alert 'undefined' 

var subscription = queryParams.subscribe(
    function (x) { 
     alert(x); 
    }, 
    function (err) { 
     alert(err); 
    }, 
    function() { 
     alert('Completed'); 
    }); 
queryParams.onNext('yay');//this will cause to alert 'yay' 

2)打運行按鈕

你會看到,你會提醒兩次,一次直接訂閱,最後一行的第二BCZ。

目前的結果是沒有錯的,那的Rx背後的哲學「運營商做的事情發生」,你可以查找這個決策樹看到運營商你正在尋找http://reactivex.io/documentation/operators.html#tree 我通常使用跳過(1)

4

的克服這個最好的辦法是訂閱路由器事件,以及處理查詢參數的路徑被選中到navigated狀態後,才:

public doSomethingWithQueryParams(): Observable<any> { 
     let observer: Observer<any>; 
     const observable = new Observable(obs => observer = obs); 

     this.router.events.subscribe(evt => { 
     // this is an injected Router instance 
     if (this.router.navigated) { 
      Observable.from(this.activatedRoute.queryParams) 
      // some more processing here 
      .subscribe(json => { 
       observer.next(json); 
       observer.complete(); 
      }); 
     } 
     }); 
     return observable; 
    }