Angular HTTP 拦截器执行内部 http 请求两次

问题描述 投票:0回答:4

我的应用程序使用

JWT Token
与后端进行身份验证。我有一个有角度的
Interceptor
可以捕获 http 调用中的任何错误。此拦截器检查调用是否返回
401 Unauthorized
响应,如果是,它会调用端点以刷新令牌,然后再次重试原始调用。

我的问题是,由于某种原因,对refreshToken端点的调用被执行了两次。

这是我的拦截器(为简洁起见进行了编辑):

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((error: any) => {
        switch (error.status) {
          // unauthorized
          case 401:
            // the token is stored in an NGRX store, so retrieve it first
            return this.userStore.pipe(select(fromUser.getUserAccessToken)).pipe(
              take(1),
              mergeMap(userToken => {
                if (userToken) {
                  // token exists but may be invalid, try to refresh
                  return this.userService.refreshToken(userToken).pipe(
                    switchMap(refreshedToken => {
                      if (refreshedToken) {
                        ...
                      } else {
                        // token was not refreshed, delete session
                        ...
                      }
                    })
                  );
                }
              })
            );
        }
      })
    );
  }

return this.userService.refreshToken(userToken)
是执行两次的语句(使用相同的原始令牌),因此第一次调用刷新将执行,第二次调用显然会失败,因为它试图再次刷新相同的令牌,从而导致用户会话被删除。

有什么想法吗?

更新: 我仍在挖掘这一点。我在代码中放置了一堆断点和

console.log

      catchError((error: any) => {
        >>>>> console.log('1');
        switch (error.status) {
          case 401:
            return this.userStore.pipe(select(fromUser.getUserAccessToken)).pipe(
              take(1),
              mergeMap(userToken => {
                >>>>> console.log('2');
                if (userToken) {
                  return this.userService.refreshToken(userToken).pipe(
                    switchMap(refreshedToken => {
                      >>>>> console.log('3');

Console.log
#1 和 #2 仅打印到控制台一次,这意味着
http error
只被捕获一次。但是 #3 被打印了两次,我认为这意味着要么调用刷新端点两次,要么可观察以某种方式返回两个值?

angular interceptor angular-httpclient angular-http-interceptors
4个回答
3
投票

不知道为什么,但似乎

refreshToken
输出多个值。这可以通过
take
运算符轻松修复。像这样的东西:

 return this.userService.refreshToken(userToken).pipe(
                take(1),
                switchMap(refreshedToken => {
                  >>>>> console.log('3');

0
投票

您的后端调用可能是一个 OPTIONS 请求

预检请求

与简单请求(上面讨论的)不同,“预检”请求首先将 HTTP OPTIONS 请求标头发送到其他域上的资源,以确定实际请求是否可以安全发送。跨站点请求会像这样进行预检,因为它们可能会对用户数据产生影响。特别是,在以下情况下,请求会被预检:

它使用 GET 或 POST 以外的方法。另外,如果 POST 用于发送 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 的请求数据,例如如果 POST 请求使用 application/xml 或 text/xml 将 XML 有效负载发送到服务器,则该请求将被预检。 它在请求中设置自定义标头(例如,请求使用 X-PINGOTHER 等标头)


0
投票

首先,您确实需要提供一个 StackBlitz 或与该错误类似的东西,以便我们可以帮助诊断此问题。话虽如此,我对此很好奇,因为我以前遇到过这样的问题,所以我做了一些研究。我发现的大多数示例都表明,

refreshToken
方法返回的Observable是共享的,以防止多个订阅。在这个答案中https://stackoverflow.com/a/52720791/5799931你可以看到
this.authService.refreshToken()
Observable是共享的。我会尝试这个

return this.userService.refreshToken(userToken).pipe(
       share(), <----- ADD THE SHARE HERE
       switchMap(refreshedToken => {
          if (refreshedToken) {
             ...
          } else {
            // token was not refreshed, delete session
            ...
          }
       })
   );

您还可以在

refreshToken
方法中共享 HttpClient 返回的 Observable。


0
投票

也许我回答晚了,但是为什么拦截器会获取两次是因为拦截器会检查 HTTPEvent 的事件..

export function testInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
    return next(req).pipe(
        tap((event) => {
            console.log(event.type, 'Test Interceptor', req.url);
        })
    );
}

查看 event.type。请参阅此处文档

© www.soinside.com 2019 - 2024. All rights reserved.