rxjs重新验证解决了

发布于 2025-01-25 06:49:53 字数 1618 浏览 5 评论 0原文

我正在实施一个呼叫服务器的角服务。在每个通话中,我都需要传递一个持续约一分钟的令牌,然后我需要绘制响应以获取特定字段。因此,如果我的主电话失败了,我需要再次致电以获得令牌,等待响应然后重试我的第一个电话,是否有一种简单的方法来执行此操作?这是我的两种方法(它们都不属于属性):

return this.http.post(url,
  firstCallText(this.token), { 
  responseType: 'text',
  headers
 })
  .pipe(
    map((xmlString: string) => {
      let asJson = this.xmlStringToJson(xmlString);
      return asJson["soap:Envelope"]["soap:Body"]["Response"]["#text"];
    }),
    catchError(async err=>{
      await this.http.post(url,
      getToken(),
      { 
        responseType: 'text',
        headers
      }).pipe(map((xmlString: string) => {
        let asJson = this.xmlStringToJson(xmlString);
        this.token = asJson["soap:Envelope"]["soap:Body"]["Response"]["Token"]["#text"];
      })).toPromise()
      return EMPTY
    }),
    retry()
  )

第一种方法失败了,因为在收到新令牌之前,请重试()。 我的第二种方法是:

return this.http.post(url,
  firstCallText(this.token), { 
  responseType: 'text',
  headers
 })
  .pipe(
    map((xmlString: string) => {
      let asJson = this.xmlStringToJson(xmlString);
      return asJson["soap:Envelope"]["soap:Body"]["Response"]["#text"];
    }),
    retryWhen((errors) =>{
       this.http.post(url,
        getToken(),
        { 
          responseType: 'text',
          headers
        }).pipe(map((xmlString: string) => {
          let asJson = this.xmlStringToJson(xmlString);
          this.token = asJson["soap:Envelope"]["soap:Body"]["Response"]["Token"]["#text"];
        })).toPromise()
        return EMPTY          
    })
  )

第二种方法无法正确重试,我不想设置延迟,因为令牌呼叫可能更短或更长。

I'm implementing an Angular service which calls a server for data. In every call I need to pass a token which lasts about a minute and then I need to map the response to get an specific field. So if my main call fails I need to call again for a token, wait for the response and then retry my first call, is there an easy way of doing this? Here are my two approaches (neither of them work propertly):

return this.http.post(url,
  firstCallText(this.token), { 
  responseType: 'text',
  headers
 })
  .pipe(
    map((xmlString: string) => {
      let asJson = this.xmlStringToJson(xmlString);
      return asJson["soap:Envelope"]["soap:Body"]["Response"]["#text"];
    }),
    catchError(async err=>{
      await this.http.post(url,
      getToken(),
      { 
        responseType: 'text',
        headers
      }).pipe(map((xmlString: string) => {
        let asJson = this.xmlStringToJson(xmlString);
        this.token = asJson["soap:Envelope"]["soap:Body"]["Response"]["Token"]["#text"];
      })).toPromise()
      return EMPTY
    }),
    retry()
  )

This first method fails because the retry() gets called before the new token is received.
My second approach:

return this.http.post(url,
  firstCallText(this.token), { 
  responseType: 'text',
  headers
 })
  .pipe(
    map((xmlString: string) => {
      let asJson = this.xmlStringToJson(xmlString);
      return asJson["soap:Envelope"]["soap:Body"]["Response"]["#text"];
    }),
    retryWhen((errors) =>{
       this.http.post(url,
        getToken(),
        { 
          responseType: 'text',
          headers
        }).pipe(map((xmlString: string) => {
          let asJson = this.xmlStringToJson(xmlString);
          this.token = asJson["soap:Envelope"]["soap:Body"]["Response"]["Token"]["#text"];
        })).toPromise()
        return EMPTY          
    })
  )

The second one doesn´t retry correctly, I don't want to set a delay because the token call might be shorter or longer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

您在这里面临的问题是重试模仿源而没有错误,您实际上想处理错误本身。

因此,我的建议是将您的catherror opertaor扩展,以使其本身处理“失败”情况并恢复操作,以获取服务器的数据。

这是一个伪代码解决方案

// Answer for rxjs retryWhen promise is resolved  :https://stackoverflow.com/questions/72061841/rxjs-retrywhen-promise-is-resolved
const {interval, of, catchError} = rxjs;
const { switchMap, tap } = rxjs.operators;

// Start of Mock for your backednd
// requestToBeValidatedWithToken will either return success meassage whenver the token is valid or throw an error when the token has expired
let token = 0;
const requestToBeValidatedWithToken = () => {
  if (token < 0) {
    throw 'Expired token';
  }
  return of('Validated request suceeds');
};
// this mocks the refresh token logic
const fetchNewToken = () => {
  token = 3;
  return of('This refreshes the token');
};
// Timer that will invalidate your token
interval(1000).subscribe(() => {
  token--;
  if (token < 0) {
    console.log('BE message: Token has expired');
  }
});
// End of Mock for your backednd

// Start of rxjs pseudo code
// This will mock your base request stream, imaginge as the request is made each seconds
const methodInYourService = () => {
  const httpCall = interval(1000).pipe(
    switchMap(() => {
      return requestToBeValidatedWithToken();
    }),
    catchError((e) => {
      // This checks makes sure that only the "Expired token" case is handled so that you dont end in infinite loop
      if (e === 'Expired token') {
        console.log('Fire refresh token request', e);
        return fetchNewToken().pipe(
          tap(() => console.log('save your token as you recieve it')),
          switchMap(() => httpCall))
      }
      return e;
    })
  );
  return httpCall;
};

// This is the code inside your component, e.g. the place where you subscribe for the data
methodInYourService().subscribe(
  (x) => {
    console.log(x, 'fin');
  },
  (e) => {
    console.log('Will never come here, as we are handling the error');
  }
);
<script src="https://unpkg.com/rxjs@^7/dist/bundles/rxjs.umd.min.js"></script>

您可以在此处找到工作的伪代码解决方案
live xpemend xpemppect

The issue that you are facing here is that the retry mimics the source without it's errors, where you actually want to handle the error itself.

So my suggestion is to extend your catchError opertaor in such a manner that it by itself handled the "failing" case and resumes the operation, for fetching data from the server.

Here is a pseudo code solution

// Answer for rxjs retryWhen promise is resolved  :https://stackoverflow.com/questions/72061841/rxjs-retrywhen-promise-is-resolved
const {interval, of, catchError} = rxjs;
const { switchMap, tap } = rxjs.operators;

// Start of Mock for your backednd
// requestToBeValidatedWithToken will either return success meassage whenver the token is valid or throw an error when the token has expired
let token = 0;
const requestToBeValidatedWithToken = () => {
  if (token < 0) {
    throw 'Expired token';
  }
  return of('Validated request suceeds');
};
// this mocks the refresh token logic
const fetchNewToken = () => {
  token = 3;
  return of('This refreshes the token');
};
// Timer that will invalidate your token
interval(1000).subscribe(() => {
  token--;
  if (token < 0) {
    console.log('BE message: Token has expired');
  }
});
// End of Mock for your backednd

// Start of rxjs pseudo code
// This will mock your base request stream, imaginge as the request is made each seconds
const methodInYourService = () => {
  const httpCall = interval(1000).pipe(
    switchMap(() => {
      return requestToBeValidatedWithToken();
    }),
    catchError((e) => {
      // This checks makes sure that only the "Expired token" case is handled so that you dont end in infinite loop
      if (e === 'Expired token') {
        console.log('Fire refresh token request', e);
        return fetchNewToken().pipe(
          tap(() => console.log('save your token as you recieve it')),
          switchMap(() => httpCall))
      }
      return e;
    })
  );
  return httpCall;
};

// This is the code inside your component, e.g. the place where you subscribe for the data
methodInYourService().subscribe(
  (x) => {
    console.log(x, 'fin');
  },
  (e) => {
    console.log('Will never come here, as we are handling the error');
  }
);
<script src="https://unpkg.com/rxjs@^7/dist/bundles/rxjs.umd.min.js"></script>

You can find working pseudo code solution here
Live example

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文