在注销 Web 应用程序的过程中,如何防止 API 调用分钟计时器循环?
How to prevent API calls on a minute timer loop when in the process of logging out of a web app?
在我的 Ionic/Angular 应用程序中,我有一个 60 秒的可观察计时器,它只发出与服务器时间同步的当前时间。每分钟我都会获取权限、设置等。我会为每个请求传递一个令牌。注销时我撤销了令牌。这是我的逻辑示例。
旁注:还有一个功能,用户可以“更改登录类型”,例如,他们可以“成为”管理员,这个过程也可能触发类似的情况。
this.clientTimeSub = this.timeService.clientTime
.pipe(takeUntil(this.logoutService.isLoggingOut$))
.subscribe(async (latestClientTime) => {
this.clientTime = { ...latestClientTime };
// if client time just rolled over to a new minute, update settings
if (
this.clientTime?.time?.length === 7 &&
this.clientTime?.time?.slice(-1) === '0'
) {
await updateSettings();
await updatePermissions();
// etc
// These functions will:
// (1) make an api call (using the login token!)
// (2) update app state
// (3) save to app storage
}
});
当我退出应用程序时,有一小段时间 window 我可能正在发送多个 api 请求并且令牌不再有效,因为就在我注销或接近注销时,计时器滚动到新的一分钟。然后我在注销过程中收到 401:未经授权。
我天真的解决方案是在 Subject 或 BehaviorSubject 触发一个值告诉这个 observable 它正在注销时告诉这个 observable 停止传播,你可以在这里看到这个 .pipe(takeUntil(this.logoutService.isLoggingOut$))
.
然后,在我的任何注销方法中,我会使用:
logout() {
this.isLoggingOut.next(true);
...
// Logout logic here, token becomes invalidated somewhere here
// then token is deleted from state, etc, navigate back to login...
...
this.isLoggingOut.next(false);
}
在注销的那一小段时间 window 中,客户端计时器应该停止触发并检查它是否滚动到新的一分钟,以防止任何进一步的 api 调用可能未经身份验证。
有什么方法可以轻松防止此问题发生,或者我的逻辑是否存在缺陷可能导致此问题?
感谢任何帮助,谢谢!
首先,将 async-await 与 RXJS 一起使用并不是最好的方法。这是因为 RXJS 作为函数式编程的一种反应方式,具有其“可管道化”的运算符,因此您可以有点“链接”一切。
因此,与其在订阅回调函数中使用计算时间的逻辑,不如使用 filter() RXJ 运算符,而不是使用 await-async 你可以使用 switchMap 运算符并在其中使用 forkJoin 或 concat 运算符。
this.timeService.clientTime
.pipe(
// Filter stream (according to your calculation)
filter((time) => {
// here is your logic to calculate if time has passed or whatever else you are doing
// const isValid = ...
return isValid;
}),
// Switch to another stream so you can call api calls
// Here with "from" we are converting promises to observables in order to be able to use magic of RXJS
switchMap(_ => forkJoin([from(updateSettings), from(updatePermissions)])),
// Take until your logout
takeUntil(this.logoutService.isLoggingOut$)
).subcribe(([updateSettings, updatePermissions]) => {
// Basically your promises should just call API services, and other logic should be here
// Here you can use
// (2) update app state
// (3) save to app storage
})
如果你像我的例子那样拆分动作,在你的承诺中你只需调用 api 调用来更新你正在做的任何事情,然后当它完成时,在订阅回调中你可以更新应用程序状态,保存到应用程序存储等。所以你可以在这里有 2 个场景:
- Api 来自承诺的调用仍在进行中。如果您同时触发注销 takeUntil 将执行此操作,您将不会更新应用程序状态等。
- 如果来自 promises 的两个 Api 调用都已完成,则您处于订阅回调块中,如果它只是一个同步代码(希望如此),它将完成。然后可以执行异步代码(您的计时器现在可以发出下一个值,这都是关于 javascript 中的事件循环)
在我的 Ionic/Angular 应用程序中,我有一个 60 秒的可观察计时器,它只发出与服务器时间同步的当前时间。每分钟我都会获取权限、设置等。我会为每个请求传递一个令牌。注销时我撤销了令牌。这是我的逻辑示例。
旁注:还有一个功能,用户可以“更改登录类型”,例如,他们可以“成为”管理员,这个过程也可能触发类似的情况。
this.clientTimeSub = this.timeService.clientTime
.pipe(takeUntil(this.logoutService.isLoggingOut$))
.subscribe(async (latestClientTime) => {
this.clientTime = { ...latestClientTime };
// if client time just rolled over to a new minute, update settings
if (
this.clientTime?.time?.length === 7 &&
this.clientTime?.time?.slice(-1) === '0'
) {
await updateSettings();
await updatePermissions();
// etc
// These functions will:
// (1) make an api call (using the login token!)
// (2) update app state
// (3) save to app storage
}
});
当我退出应用程序时,有一小段时间 window 我可能正在发送多个 api 请求并且令牌不再有效,因为就在我注销或接近注销时,计时器滚动到新的一分钟。然后我在注销过程中收到 401:未经授权。
我天真的解决方案是在 Subject 或 BehaviorSubject 触发一个值告诉这个 observable 它正在注销时告诉这个 observable 停止传播,你可以在这里看到这个 .pipe(takeUntil(this.logoutService.isLoggingOut$))
.
然后,在我的任何注销方法中,我会使用:
logout() {
this.isLoggingOut.next(true);
...
// Logout logic here, token becomes invalidated somewhere here
// then token is deleted from state, etc, navigate back to login...
...
this.isLoggingOut.next(false);
}
在注销的那一小段时间 window 中,客户端计时器应该停止触发并检查它是否滚动到新的一分钟,以防止任何进一步的 api 调用可能未经身份验证。
有什么方法可以轻松防止此问题发生,或者我的逻辑是否存在缺陷可能导致此问题?
感谢任何帮助,谢谢!
首先,将 async-await 与 RXJS 一起使用并不是最好的方法。这是因为 RXJS 作为函数式编程的一种反应方式,具有其“可管道化”的运算符,因此您可以有点“链接”一切。
因此,与其在订阅回调函数中使用计算时间的逻辑,不如使用 filter() RXJ 运算符,而不是使用 await-async 你可以使用 switchMap 运算符并在其中使用 forkJoin 或 concat 运算符。
this.timeService.clientTime
.pipe(
// Filter stream (according to your calculation)
filter((time) => {
// here is your logic to calculate if time has passed or whatever else you are doing
// const isValid = ...
return isValid;
}),
// Switch to another stream so you can call api calls
// Here with "from" we are converting promises to observables in order to be able to use magic of RXJS
switchMap(_ => forkJoin([from(updateSettings), from(updatePermissions)])),
// Take until your logout
takeUntil(this.logoutService.isLoggingOut$)
).subcribe(([updateSettings, updatePermissions]) => {
// Basically your promises should just call API services, and other logic should be here
// Here you can use
// (2) update app state
// (3) save to app storage
})
如果你像我的例子那样拆分动作,在你的承诺中你只需调用 api 调用来更新你正在做的任何事情,然后当它完成时,在订阅回调中你可以更新应用程序状态,保存到应用程序存储等。所以你可以在这里有 2 个场景:
- Api 来自承诺的调用仍在进行中。如果您同时触发注销 takeUntil 将执行此操作,您将不会更新应用程序状态等。
- 如果来自 promises 的两个 Api 调用都已完成,则您处于订阅回调块中,如果它只是一个同步代码(希望如此),它将完成。然后可以执行异步代码(您的计时器现在可以发出下一个值,这都是关于 javascript 中的事件循环)