Подтвердить что ты не робот

Angular2 Query Params подписывает огонь дважды

Попытка обработать сценарий входа в OAuth, где, если пользователь приземляется на странице с authorization_code в строке запроса, мы обрабатываем токен и продолжаем или, если они приземляются на странице без этого, мы проверяем локальное хранилище на наличие существующего токена, убедитесь, что он по-прежнему действителен и либо перенаправлен на логин, либо продолжен, исходя из его действительности.

Проблема заключается в том, что если мы проверяем существование параметра строки запроса authorization_code, подписка срабатывает дважды. В первый раз он пуст, второй раз он имеет правильное значение в словаре.

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).

Вопросы

  • Есть ли что-то, что я делаю неправильно, чтобы сделать его дважды?
  • Я не могу просто игнорировать пустой объект с условным, потому что сценарий, в котором нам нужно проверить существующий токен аутентификации - есть ли другой способ приблизиться к этому, что не является полным взломом?
4b9b3361

Ответ 1

Наблюдаемые на маршрутизаторе (как упоминается еще один ответ) являются BehaviorSubject субъектами, они отличаются от обычного RxJS Subject или Angular 2 EventEmitter тем, что они имеют начальное значение, перенесенное в последовательность (пустой объект в случае queryParams).

Как правило, желательна возможность подписки на логику инициализации.

Начальное значение можно пропустить с помощью оператора skip.

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

Но более естественным способом справиться с этим является отфильтровать все нерелевантные параметры (начальная params попадает в эту категорию). Дублирующие значения authorization_code также могут быть отфильтрованы с помощью оператора distinctUntilChanged, чтобы избежать ненужных вызовов на сервер.

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

Обратите внимание, что Angular 2 импортирует ограниченное количество операторов RxJS (не менее map в случае @angular/router). Если полный пакет rxjs/Rx не используется, может потребоваться импортировать дополнительные операторы (filter, distinctUntilChanged), которые используются с import 'rxjs/add/operator/<operator_name>'.

Ответ 2

Лучший способ преодолеть это - подписывать события маршрутизатора и обрабатывать параметры запроса только после того, как маршрут отмечен как 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;
  }

Ответ 3

Я думаю, что по дизайну.

queryParams - BehaviorSubject

Как вы можете видеть в docs

Одним из вариантов объектов является объект BehaviorSubject, который имеет понятие "текущее значение". В нем хранится последнее значение, испускаемое его потребителей, и всякий раз, когда подписывается новый Наблюдатель, он будет немедленно получите "текущее значение" из объекта BehaviorSubject.

В качестве обходного пути вы можете использовать оператор debounceTime следующим образом:

import 'rxjs/add/operator/debounceTime';

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

Ответ 4

если вы перейдете по этой ссылке 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 Обычно я использую skip (1)

Ответ 5

Просто используйте класс Location, чтобы получить начальный url, UrlSerializer класс для разбора url, UrlTree, чтобы получить параметры запроса.