rxJs&angular 4&restangular:stack errorInterceptors

byC*_*der 5 observable rxjs restangular angular

在我的角度4应用程序中,我正在使用ngx-restangular来处理所有服务器调用.它返回可观察的结果,并且该模块具有处理错误的钩子(如401等).

但是从文档中,我可以处理403(401)所以:

  RestangularProvider.addErrorInterceptor((response, subject, responseHandler) => {
    if (response.status === 403) {

      refreshAccesstoken()
      .switchMap(refreshAccesstokenResponse => {
        //If you want to change request or make with it some actions and give the request to the repeatRequest func.
        //Or you can live it empty and request will be the same.

        // update Authorization header
        response.request.headers.set('Authorization', 'Bearer ' + refreshAccesstokenResponse)

        return response.repeatRequest(response.request);
      })
      .subscribe(
        res => responseHandler(res),
        err => subject.error(err)
      );

      return false; // error handled
    }
    return true; // error not handled
  });
Run Code Online (Sandbox Code Playgroud)

这对于一个已经破坏了403错误的请求很有用.如何使用rxJs堆叠此调用?例如,现在,我有3个请求,有403个,每个这个破坏的请求我刷新令牌 - 这不太好,我必须更新我的令牌,然后重复我所有破碎的请求.我怎样才能使用Observables来实现这个目标?

角度1很容易:

Restangular.setErrorInterceptor(function (response, deferred, responseHandler) {
  if (response.status == 403) {
    // send only one request if multiple errors exist
    if (!refreshingIsInProgress) {
      refreshingIsInProgress = AppApi.refreshAccessToken(); // Returns promise
    }


    $q.when(refreshingIsInProgress, function () {
      refreshingIsInProgress = null;

      setHeaders(response.config.headers);

      // repeat request with error
      $http(response.config).then(responseHandler, deferred);
    }, function () {
      refreshingIsInProgress = null;

      $state.go('auth');
    });

    return false; // stop the promise chain
  }

  return true;
});
Run Code Online (Sandbox Code Playgroud)

一切都像魅力一样.但我是rxJs和angular 4的新手,我不知道如何用可观察和角度4来实现这个.也许有人有想法?

UPD!这是我的refreshAccesstoken方法

const refreshAccesstoken = function () {
  const refreshToken = http.post(environment.apiURL + `/token/refresh`,
    {refreshToken: 'someToken'});
  return refreshToken;
};
Run Code Online (Sandbox Code Playgroud)

Rad*_*ari 3

我看到使用 ngx-restangular 执行此操作的一种方法是使用共享运算符。这样您就不必实现复杂的排队逻辑。这个想法是,如果你有 3 个请求,所有请求都带有 403 响应,它们都会命中你的拦截器并调用你的可观察对象。如果您共享该可观察量,则对于 3 个带有损坏令牌的请求,您将只有一个令牌请求。

您只需在代码中使用共享运算符,如下所示:

refreshAccesstoken()
  .share()
  .switchMap(refreshAccesstokenResponse => {
    //If you want to change request or make with it some actions and give the request to the repeatRequest func.
    //Or you can live it empty and request will be the same.

    // update Authorization header
    response.request.headers.set('Authorization', 'Bearer ' + refreshAccesstokenResponse)

    return response.repeatRequest(response.request);
  })
  .subscribe(
    res => responseHandler(res),
    err => subject.error(err)
  );
Run Code Online (Sandbox Code Playgroud)

我还没有检查代码是否确实有效,但我之前曾在相同的用例中使用过这种方法,但我使用的是 Angular HTTP 服务,而不是拦截器。

编辑更改刷新访问令牌:

您需要将您的refreshAccessToken方法包装在延迟的Observable中并共享它。这样你每次都会重用相同的可观察值。

在构造函数中:

this.source = Observable.defer(() => {
        return this.refreshAccesstoken();
    }).share();
Run Code Online (Sandbox Code Playgroud)

创建另一个将调用该可观察量的方法:

refreshToken(): Observable<any> {
    return this.source
        .do((data) => {
            this.resolved(data);
        }, error => {
            this.resolved(error);
        });
}
Run Code Online (Sandbox Code Playgroud)

编辑2

我创建了一个git 存储库,该存储库使用 angular2 和 restangular。场景如下:

  1. 在我的 app.component 中,我发出 3 个并发请求来获取订单列表。请求完成后,我将记录“已收到订单”。
  2. 订单端点需要身份验证令牌。如果未提供,则会返回 401。
  3. 在我的 app.module 中,我仅将基本 URL 设置为我的 API。因为我也没有设置授权令牌,所以我的所有请求都将失败并返回 401。
  4. 当执行拦截器代码时,它将设置刷新令牌,在我的例子中,刷新令牌是在请求上硬编码的并重复请求。
  5. 返回令牌的可观察对象每次执行时都会记录“获取令牌”。

这是我在控制台中可以看到的内容: 控制台日志和请求

如果我删除共享运算符,我将得到以下日志: 在此输入图像描述 这意味着每次都会创建可观察对象。

为了使其工作,在 RestangularConfigFactory 中声明和创建源非常重要。它本质上将成为一个单例对象,这就是共享操作符能够工作的原因。

笔记:

我为这个项目创建了一个本地托管的简单 Web API,只是因为它对我来说更快。

EDIT3:更新以包含刷新令牌的代码:

refreshAccesstoken()
  .share()
  .switchMap(refreshAccesstokenResponse => {
    //If you want to change request or make with it some actions and give the request to the repeatRequest func.
    //Or you can live it empty and request will be the same.

    // update Authorization header
    response.request.headers.set('Authorization', 'Bearer ' + refreshAccesstokenResponse)

    return response.repeatRequest(response.request);
  })
  .subscribe(
    res => responseHandler(res),
    err => subject.error(err)
  );
Run Code Online (Sandbox Code Playgroud)

  • 我进行了编辑,显示了您需要如何调整 HTTP 调用。最初的答案不起作用,因为当您刷新令牌时,每次调用该方法时都会创建一个新的可观察值。希望这可以帮助。 (2认同)