【问题标题】:Can't call api again inside function无法在函数内部再次调用 api
【发布时间】:2021-10-18 07:20:39
【问题描述】:

我的请求有问题。
这个想法是,如果请求失败,因为令牌过期函数将发送登录请求(带有来自会话数据的数据),并将再次发送原始请求并返回值。
我认为这可能有明显的解决方案,但我几乎没有用 js 编写代码。 知道如何解决吗?

代码在这里:

  apiCall(url, body, first = true) {

    const httpOptions = {
      headers: new HttpHeaders()
        .set('Authorization', `Bearer ${sessionStorage.getItem('jwt')} `),
    };
    this.deleteNullParams(body);
    return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
      map(response => {
        if (response['result'] != 'success' && first) {
          console.log("Failed");
          this.loginWithSessionData();
          return this.apiCall(url, body, false);
        }
        else {
          return response;
        }
      })
    );
  }

getLocationDetails(body) {
    return this.apiCall('/api/locationList', body);
  }

  loginWithSessionData() {
    let password = sessionStorage.getItem('password');
    let username = sessionStorage.getItem('username');
    if (password && password != '' && username && username != '') {
      const json = {};
      json['username'] = username;
      json['password'] = password;
      this.userLogin(json).subscribe((res: any) => {
        if (res['body']['result'] !== 'failed') {
          sessionStorage.setItem('jwt', res.body['data'].token);
          localStorage.setItem('login_data', JSON.stringify(res.body['data']));
          return true;
        }
      }, (error) => { });
    }
    else {
      sessionStorage.clear();
    }
    return false;
  }

所以我想在获得有效令牌后再次返回 apiCall,但由于某种原因,除了登录之外的任何其他请求都不会在 apiCall 函数中发送。

编辑
更具体地说,调用函数时会发生什么:
尝试获取数据,
由于没有(或过期)令牌而失败,
发送登录请求,获取它,
设置令牌,就是这样,
在下一个刷新/更改页面之前,不会再发送任何请求。

回答

好的,所以接受的答案是正确的,只需要添加返回:

  apiCall(url, body, first = true) {
    const httpOptions = {
      headers: new HttpHeaders()
        .set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
    };
    this.deleteNullParams(body);
  
    return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
      switchMap((response: any) => {
        return iif(
          () => response['result'] != 'success' && first,
          this.loginWithSessionDataObs().pipe(switchMap(() => this.apiCall(url, body, false))),
          of(response)
        )
        
      })
    );
  }

【问题讨论】:

  • 问题是,您尝试在this.loginWithSessionData(); 甚至有机会登录之前运行this.apiCall(url, body, false); - 您需要等待登录完成,然后再尝试调用 api再次
  • 我是这么想的,但即使我设置了 80 apiCall 仍然只有一个请求可见,为什么?
  • 我不知道,这是个谜

标签: javascript angular typescript request


【解决方案1】:

从可观察对象中获取值是异步的,这意味着您不能期望在订阅回调之后编写一些语句并在回调之后执行。它们会更早运行。

在您的情况下,您需要使用多个 RxJS 函数(ofiif)和运算符(switchMap)使其相互依赖。

apiCall(url, body, first = true) {
  const httpOptions = {
    headers: new HttpHeaders()
      .set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
  };
  this.deleteNullParams(body);

  this.httpClient.post(this.API_URL + url, body, httpOptions);.pipe(
    switchMap((response: any) => {
      iif(
        () => response['result'] != 'success' && first,
        this.loginWithSessionData().pipe(switchMap(() => this.apiCall(url, body, false))),
        of(response)
      )
    })
  );
}

loginWithSessionData(): Observable<any> {     // <-- return observable here
  let username = sessionStorage.getItem('username');
  let password = sessionStorage.getItem('password');

  const storeData = (data: any) => {
    sessionStorage.setItem('jwt', data.body['data'].token);
    localStorage.setItem('login_data', JSON.stringify(data.body['data']));
  }

  return iif(
    () => (password && password != '' && username && username != ''),
    this.userLogin({ username: username, password: password }).pipe(
      map((data: any) => {
        storeData(data);
        return true;
      })
    ),
    of(false).pipe(tap(() => sessionStorage.clear()))
  );
}

但是,尽管它可能有效,但我想说可能有更好的方法来重新触发 HTTP 请求,而不是递归调用。例如,您可以在最初不触发 HTTP 请求的情况下检查token expiration

【讨论】:

  • 我认为它可能会更好,但它不是真正的 jwt 它只是这样命名的,我正在编辑其他人的代码。无论如何,您编写的代码看起来不错,但是登录无法按我的预期工作
  • @zywy:真可惜。尽管如此,您仍然可以尝试解码令牌以找到某种到期时间戳的方式。尝试使用jwt.io之类的工具
  • 当然,但实际上是一样的。我没有触发请求,但每次都检查它是否因为令牌而失败(稍后会添加响应代码,所以我会确定它是因为令牌),然后我才会发送登录请求。在那种情况下,我什至不需要解码它,我可以节省上次登录的时间,而且我知道令牌的生命周期,所以很容易调用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-21
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
相关资源
最近更新 更多