如何刷新jwt并重新发送失败的http请求?

How to refresh jwt and resend failed http request?

我有一个带有 jwt 身份验证的后端,我想处理过期的令牌。

需要以下流程:

  1. 发出附加令牌的请求(并期望得到承诺)
  2. 如果一切顺利,那么 return promise(并且执行调用者的 then/fail 方法)
  3. 如果失败(401 未经授权),则请求刷新令牌并在本地更新令牌
  4. 如果第 3 步成功,return 对原始请求的承诺
  5. 如果第 3 步失败并显示 401(无法刷新令牌)错误重定向到登录页面

问题: 在第 4 步中,(再次)调用了原始函数,但未触发调用者的 then/fail 方法。

以下是我将 jwt 令牌附加到 url 并发送 http 请求的方法:

var AuthenticatedRequest = function(url, data, method) {
  return (function tryRequest(){
    console.log('calling tryRequest');
    return reqwest({
        url: ApiUtil.tokenUrlTo(url),
        method: method,
        crossOrigin: true,
        type: 'json',
        data: data
      })
      .fail(function(err) {
        if (err.status === 401) {
          return post('/auth/refresh-token')
            .then(function(response){
              console.log('assume token set');
              //code to update token locally
            })
            .then(tryRequest)
            .fail(function(err){
              // Can't refresh token. Send to login page
            })
          ;      
        }
      })
    ;
  })();
};

这是来电者:

fetchModules: function() {
    get('/admin/modules')
      .then(function(response) {
        Actions.modulesFetchSuccess(response.collection);
      })
      .fail(function(err) {
        Actions.modulesFetchError(ApiUtil.errorArrayForResponse(err));
      })
    ;
  },

现在,如果我因为令牌过期而收到 401,我会触发一个新周期来刷新令牌,如本问题 Restart a promise after fail 中所建议的那样。

注意:postget 函数只是 AuthenticatedRequest 函数的包装,方法设置为 POSTGET.

AuthenticatedRequest 函数 return 是一个承诺,如果令牌没有过期,它运行正常,但是,当令牌过期时,我的控制台和新令牌出现错误被获取并再次调用该函数,我的控制台的屏幕截图 - http://i.stack.imgur.com/hJdId.png

但是 fetchModulesthen 方法在令牌更新后不会被触发。我做错了什么?

可能重复:

2015 年 9 月 13 日更新

@Bergi's answer worked when I replaced reqwest.js and used q.js with vanilla ajax as show in this gist

问题是 .fail 总是捕捉到你的错误,不仅仅是第一次。您对 tryRequest 的递归调用将包括重试本身,并且永远不会 return 失败的承诺。
如果您只想重试一次,则需要将其放在外部:

function AuthenticatedRequest(url, data, method) {
  function tryRequest() {
    console.log('calling tryRequest');
    return reqwest({
      url: ApiUtil.tokenUrlTo(url),
      method: method,
      crossOrigin: true,
      type: 'json',
      data: data
    });
  }
  return tryRequest().fail(function(err) {
    if (err.status !== 401) throw err;
    return post('/auth/refresh-token')
    .then(function(response) {
      console.log('assume token set');
      // code to update token locally
    })
    .then(tryRequest)
    .fail(function(err) {
      // Can't refresh token. Send to login page
    });
  });
}

请注意,将用户从 AuthenticatedRequest 函数发送到另一个页面可能不是一个好的设计,也许可以考虑重新抛出错误(在使令牌无效之后?)并将重定向和所有内容放入错误中调用者的处理程序。