每次用户注销时都会发送一个新的重复注销 HTTP 请求:Angular, RxJs
A new duplicate sign-out HTTP request is sent every time the user logs out: Angular, RxJs
问题描述
首先,让我先说一下,我使用 Angular 10 和 Nebular UI Library 作为前端,Node.js 用于后端 API,JWT 与 email/password 策略 用于身份验证。我注意到 每次用户登录并退出而不刷新应用程序时,都会向服务器发出新的重复退出请求 (正在发送多个 http 请求出去)。
如果您在注销后刷新应用程序,问题就会消失。我不确定我是否在跳过某些东西,或者我只是不知道使用 JWT 注销和重新登录的正确方法,但几天来我一直在努力寻找解决这个问题的方法,但没有成功所以我渴望得到一些帮助。
当前行为:
如果用户多次登录并再次注销,向服务器发出的注销请求会重复。无论您是否使用 http 拦截器(NbAuthJWTInterceptor 或其他),此问题仍然存在。
预期行为:
如果用户要登录并再次注销,不应向服务器发出多余的注销请求,无论用户重复这些请求多少次无需刷新应用程序的步骤。
重现步骤:
- 用户第一次登录一切正常,注销时没有向服务器发出重复请求。
- 在您第二次重新登录并且第二次退出后没有刷新应用程序,第二次退出您向服务器发出的请求将发出重复的注销请求(2 个相同的注销请求被发送到服务器)。
- 如果用户第 3 次再次登录并且第 3 次退出,则 3 个注销请求会发送到服务器(一共发送了3个相同的请求)。
- 如果用户登录并再次退出,退出请求将被重复发送一次并且总共发送 4 个相同的退出请求会被送出去。这将无限期地持续下去。
这是我的开发工具网络选项卡中这 4 个步骤的屏幕截图(在登录和退出 4 次之后):
相关代码:
在客户端,我有 header.component.ts 文件,从中启动注销过程:
...
ngOnInit() {
// Context Menu Event Handler.
this.menuService.onItemClick().pipe(
filter(({ tag }) => tag === 'my-context-menu'),
map(({ item: { title } }) => title),
).subscribe((title) => {
// Check if the Logout menu item was clicked.
if (title == 'Log out') {
// Logout the user.
this.authService.logout('email').subscribe(() => {
// Clear the token.
this.tokenService.clear()
// Navigate to the login page.
return this.router.navigate([`/auth/login`]);
});
}
if (title == 'Profile') {
return this.router.navigate([`/pages/profile/${this.user["_id"]}`]);
}
});
}
...
在服务器端,有退出 API 路由 returns 成功的 200 响应:
// Asynchronous POST request to logout the user.
router.post('/sign-out', async (req, res) => {
return res.status(200).send();
});
您正在另一个订阅内订阅。这会导致每次调用 this.menuService.onItemClick()
时进行另一个订阅。
您需要使用适当的 Rxjs 运算符(exhaustMap、concatMap、switchMap、mergeMap)来使用展平策略。
在你的情况下,我会这样重构(不要忘记 取消订阅 ngOnDestroy 中的每个订阅)
const titleChange$ = this.menuService.onItemClick()
.pipe(
filter(({ tag }) => tag === 'my-context-menu'),
map(({ item: { title } }) => title)
);
this.logOutSubscription = titleChange$.pipe(
filter((title) => title == 'Log out'),
exhaustMap((title) => this.authService.logout('email')),
tap(() => {
this.tokenService.clear()
this.router.navigate([`/auth/login`]);
})
.subscribe();
this.profileNavSubscription = titleChange$
.pipe(
filter((title) => title == 'Profile'),
tap(title => {
this.router.navigate([`/pages/profile/${this.user["_id"]}`])
})
.subscribe();
`
问题描述
首先,让我先说一下,我使用 Angular 10 和 Nebular UI Library 作为前端,Node.js 用于后端 API,JWT 与 email/password 策略 用于身份验证。我注意到 每次用户登录并退出而不刷新应用程序时,都会向服务器发出新的重复退出请求 (正在发送多个 http 请求出去)。 如果您在注销后刷新应用程序,问题就会消失。我不确定我是否在跳过某些东西,或者我只是不知道使用 JWT 注销和重新登录的正确方法,但几天来我一直在努力寻找解决这个问题的方法,但没有成功所以我渴望得到一些帮助。
当前行为:
如果用户多次登录并再次注销,向服务器发出的注销请求会重复。无论您是否使用 http 拦截器(NbAuthJWTInterceptor 或其他),此问题仍然存在。
预期行为:
如果用户要登录并再次注销,不应向服务器发出多余的注销请求,无论用户重复这些请求多少次无需刷新应用程序的步骤。
重现步骤:
- 用户第一次登录一切正常,注销时没有向服务器发出重复请求。
- 在您第二次重新登录并且第二次退出后没有刷新应用程序,第二次退出您向服务器发出的请求将发出重复的注销请求(2 个相同的注销请求被发送到服务器)。
- 如果用户第 3 次再次登录并且第 3 次退出,则 3 个注销请求会发送到服务器(一共发送了3个相同的请求)。
- 如果用户登录并再次退出,退出请求将被重复发送一次并且总共发送 4 个相同的退出请求会被送出去。这将无限期地持续下去。
这是我的开发工具网络选项卡中这 4 个步骤的屏幕截图(在登录和退出 4 次之后):
相关代码: 在客户端,我有 header.component.ts 文件,从中启动注销过程:
...
ngOnInit() {
// Context Menu Event Handler.
this.menuService.onItemClick().pipe(
filter(({ tag }) => tag === 'my-context-menu'),
map(({ item: { title } }) => title),
).subscribe((title) => {
// Check if the Logout menu item was clicked.
if (title == 'Log out') {
// Logout the user.
this.authService.logout('email').subscribe(() => {
// Clear the token.
this.tokenService.clear()
// Navigate to the login page.
return this.router.navigate([`/auth/login`]);
});
}
if (title == 'Profile') {
return this.router.navigate([`/pages/profile/${this.user["_id"]}`]);
}
});
}
...
在服务器端,有退出 API 路由 returns 成功的 200 响应:
// Asynchronous POST request to logout the user.
router.post('/sign-out', async (req, res) => {
return res.status(200).send();
});
您正在另一个订阅内订阅。这会导致每次调用 this.menuService.onItemClick()
时进行另一个订阅。
您需要使用适当的 Rxjs 运算符(exhaustMap、concatMap、switchMap、mergeMap)来使用展平策略。
在你的情况下,我会这样重构(不要忘记 取消订阅 ngOnDestroy 中的每个订阅)
const titleChange$ = this.menuService.onItemClick()
.pipe(
filter(({ tag }) => tag === 'my-context-menu'),
map(({ item: { title } }) => title)
);
this.logOutSubscription = titleChange$.pipe(
filter((title) => title == 'Log out'),
exhaustMap((title) => this.authService.logout('email')),
tap(() => {
this.tokenService.clear()
this.router.navigate([`/auth/login`]);
})
.subscribe();
this.profileNavSubscription = titleChange$
.pipe(
filter((title) => title == 'Profile'),
tap(title => {
this.router.navigate([`/pages/profile/${this.user["_id"]}`])
})
.subscribe();
`