#rxjs #angular-routing
Вопрос:
Я попытался сформулировать название вопроса наиболее общим способом, который применим к моей проблеме.
У меня есть приложение Angular, в котором мне нужно выполнить аутентификацию по внешнему требованию: либо использовать параметр строки запроса token
, который необходимо обменять с сервером на JWT, либо попытаться найти токен обновления JWT в локальном хранилище.
Это:
- Сначала проверьте строку запроса: если есть параметр строки
token
запроса , возьмите токен, удалите любой JWT в локальном хранилище, обменяйте токен через API на два JWT (id_token
иrefresh_token
) - В противном случае перейдите к маркеру обновления: если
refresh_token
в локальном хранилище есть доступный, обменяйте его на JWTid_token
через API - В противном случае, если ни один из них не доступен, пользователь не прошел проверку подлинности, и должно появиться приглашение
Я использовал наблюдаемые почти правильно
this.queryParamMap$.unsubscribe(); this.queryParamMap$ = this.activatedRoute.queryParamMap .subscribe( params =gt; { let token = params.get('token'); ........ if (!!token) { doLoginWithToken(); else if (isJwtRefreshAvailable()) doLoginWithRefreshToken();
В этом подходе есть одна проблема: при первом запуске приложения карта параметров запроса пуста, даже если я перейду по прямой ссылке браузера http://localhost:4200?token=AAAAAAAA
. Я должен wait
сделать это для следующего элемента, который содержит токен.
Это имеет два нежелательных эффекта:
- При первой попытке, будучи токеном
undefined
, приложение немедленно пытается войти в систему с помощью токена обновления - Если я отфильтрую
queryParamMap
наблюдаемое для наличия токена, если токен никогда не присутствует, наблюдаемое никогда не будет излучать, таким образом, не активируя подписку.
Мою проблему можно обобщить/обобщить следующим образом.
У меня есть наблюдаемый, который, как я точно знаю, испускает undefined
с самого первого раза, но либо в кратчайшие сроки он может быть готов с надлежащим значением, либо он не будет испускать новые значения после первоначального undefined
.
Кроме того, в то время как наблюдаемый излучает undefined
, и мой код начинает реагировать на него (например, путем тестирования на токен), новое значение может быть готово к излучению сразу.
Как я могу решить эту проблему в Rxjs? Обратите внимание, что доступ к токену JWT из локального хранилища является синхронной операцией, но его легко создать Observable.of(localStorage.get(KEY))
, который немедленно запускается при наличии токена обновления.
Для этого я не могу реально использовать race
оператор, потому что токен обновления всегда готов и всегда выигрывает гонку.
Как я могу написать асинхронный код, который выполняет действия, подобные тем, которые я описал ранее?
В качестве конечного результата аутентификации an Observablelt;UserProfileDto | undefinedgt;
выдает информацию о пользователе, которая используется для отображения персонализированной информации.
Комментарии:
1. похоже , вы могли бы использовать just
debounceTime()
, и если после этого списания токен останетсяundefined
, вы будете знать, что токен не установлен и не будет присутствовать. Если вы знаете, что все выполняемые вами операции синхронны, вы можете пойти даже наdebounceTime(0)
то, что обработчик подписки будет выполнен в другом фрейме JavaScript без каких-либо задержек.2. Хорошо в качестве ответа. Я попробую и то, и другое
Ответ №1:
У вас здесь 2 проблемы:
- При запуске вы получили значение «неопределенное» (это потому, что, вероятно, под капотом есть какой-то объект поведения, который выдает значение по умолчанию). Чтобы преодолеть это, вы можете добавить оператор RxJS (пропустить(1)), чтобы пропустить это первое значение, но:
- Проблема здесь в том, что если у вас вообще нет значений запроса, вы не достигнете своей функции подписки. Это потому, что queryParamMap выдаст значение только в том случае, если есть изменение (а в этом случае его нет):
Вы можете сделать это без углового, чтобы просто проанализировать URL-адрес с:
const urlParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlParams.entries());
Или с помощью маршрутизатора в угловой:
this.router.events // Wait for the navigation end event (since component is initialized before angular router navigation is done) .pipe(filter(event =gt; event instanceof NavigationEnd)) .subscribe((event: NavigationStart) =gt; { // Take query params snaphot const map = this.route.snapshot.queryParams; });