Retry a Restangular call in an interceptor

2019-03-28 18:49发布

问题:

The official restangular documentation provides this code sample to retry a request in the ErrorInterceptor:

var refreshAccesstoken = function() {
    var deferred = $q.defer();

    // Refresh access-token logic

    return deferred.promise;
};

Restangular.setErrorInterceptor(function(response, deferred, responseHandler) {
    if(response.status === 403) {
        refreshAccesstoken().then(function() {
            // Repeat the request and then call the handlers the usual way.
            $http(response.config).then(responseHandler, deferred.reject);
            // Be aware that no request interceptors are called this way.
        });

        return false; // error handled
    }

    return true; // error not handled
});

However, as it says in the comments, the second attempt will not trigger any interceptors because it uses $http directly.

Is there a way to make the second request also go through the Restangular pipeline and execute the ErrorInterceptor?

回答1:

Actually, the requests made with Restangular use promises that have been designed to be resolved only once. You can't resend it in case of rejection.

I don't really know if you want a re-usable errorInterceptor or not, but the way the service works don't seems to allow to re-build a Restangular object simply from the response object config.

One thing you could do is save the reference to your object and then use it in the interceptor.

You should have your reasons, but I must warn you with this because in case of failure and if it persists, you could end up with an infinite call, since the Restangular will always call the errorInterceptor.

I've mocked up a little Plunkr for the demonstration, just open your network tab and click the button, you should see all the request goes trough there.



回答2:

Using async.retry may fit to your problem.

Let's assume your asynchronous function is :

function apiCall(callback) {
  Restangular.all('items').getList()
    .then(function (res) {
      // all is OK
      callback(null, res);
    })
    .catch(function (err) {
      if (err.status === 403) {
        // this one will bring async to retry apiMethod
        return callback(err);
      }
      // others errors codes will not call apiMethod again
      callback(null);
    });
}

Example using retry :

// async will try it 3 times
async.retry(3, apiCall, function(err, result) {
  // do something with the result
});

async.doUntil could interest you too.



回答3:

It is too late for an answer, however I found a solution for this, So I am going to mention it here as possible solution.

There is a hint in the following link which says you need to use restangularizeElement.

https://github.com/mgonto/restangular/issues/1022

I am fixing restangular official example as follow:

Restangular.setErrorInterceptor(function(response, deferred, responseHandler) {
  if(response.status === 403) {
    refreshAccesstoken().then(function() {
      // instead of using $http, I will use restangularizeElement as follow
      // I still have no idea how is the best method to repeat the request after restangularized it. I am repeating it with a simple get
      var request = Restangular.restangularizeElement('', response.data, url);
      request.get().then(function() {});
    });
    return false; // error handled
  }

  return true; // error not handled
});

In this way, if repeated request cause any error, it will be caught in restangular error interceptor again.