【问题标题】:Interceptor for fetch and fetch retry? (Javascript)获取和获取重试的拦截器? (Javascript)
【发布时间】:2021-04-14 17:14:02
【问题描述】:

我正在尝试为 javascript 中的 fetch 创建一个拦截器(反应更具体)。它应该从每个被调用的 fetch 中获取结果,如果是 401 错误,它应该启动对另一个路由的新 fetch 调用以获取 cookie(刷新令牌)。然后,应该再次尝试最初的 fetch 调用(因为现在用户已登录)。

我已经成功触发了新的 fetch 调用并为每个人发回了 cookie,但我遇到了以下两个问题:

  1. 我现在不知道如何在收到刷新令牌后重试 fetch 调用。那可能吗?我找到了 fetch-retry npm (https://www.npmjs.com/package/fetch-retry),但不确定如何以及是否可以在拦截器上实现它,什么时候应该为原始 fetch 调用完成。

  2. 我似乎对异步等待做错了(我认为),因为拦截在返回数据之前没有等待 fetch 调用(原始 fetch 上的状态码似乎是 401 而不是应该是 200在我们得到 cookie 之后。我还尝试在拦截器中返回 fetch 的响应,但返回的是 undefined)。

关于如何解决这个问题的任何想法?有人做过类似的吗?

下面是我的代码:

(function () {
  const originalFetch = fetch;
  fetch = function() {
      return originalFetch.apply(this, arguments).then(function(data) {

          if(data.status === 401) {
            console.log('not authorized, trying to get refresh cookie..')

            const fetchIt = async () => {
              let response = await fetch(`/api/token`, {
                  method: 'POST',
                  credentials: 'include', 
                  headers: {
                      'Content-Type': 'application/json'
                  },
              });
          }
        fetchIt();
          } 
         return data

      }); 
  };
})();

编辑:让我更清楚我的追求。我需要一个如上所述的拦截器才能工作,所以我不必在每次 fetch 调用后做这样的事情:

getData() {
        const getDataAsync = async () => {
            let response = await fetch(`/api/loadData`, { method: 'POST' });

           if(response.status === 401) {
            let responseT = await fetch(`/api/token`, {
                method: 'POST',
                credentials: 'include', 
                headers: {
                    'Content-Type': 'application/json'
                },
            });

            if(responseT.status === 401) {
                return responseT.status
            }

            if(responseT.status === 200) {
            response = await fetch(`/api/loadData`, { method: 'POST' });
            }
           }

          let data = await response.json();
            //Do things with data
        };
        getDataAsync();
    };

所以基本上拦截器应该:

  1. 检查是否有401,如果有则:
  2. 获取 api/令牌
  3. 如果 api/token 返回 401,它应该只返回那个。
  4. 如果 api/token 返回 200,它应该再次运行原始提取

【问题讨论】:

    标签: javascript interceptor


    【解决方案1】:

    您可以简单地使用originalFetch 获取令牌,如果响应为 401,则等待响应,然后您只需将空响应返回给第一次 fetch 调用,否则您更新令牌,然后让它进入下一个条件,这将重新运行旧请求。

    let TEMP_API = {
      '401': {
        url: 'https://run.mocky.io/v3/7a98985c-1e59-4bfb-87dd-117307b6196c',
        args: {}
      },
      '200': {
        url: 'https://jsonplaceholder.typicode.com/todos/2',
        args: {}
      },
      '404': {
        url: 'https://jsonplaceholder.typicode.com/todos/1',
        args: {
          method: "POST",
          credentials: "include"
        }
      }
    }
    
    const originalFetch = fetch;
    fetch = function() {
      let self = this;
      let args = arguments;
      return originalFetch.apply(self, args).then(async function(data) {
        if (data.status === 200) console.log("---------Status 200----------");
        if (data.status === 401) {
          // request for token with original fetch if status is 401
          console.log('failed');
          let response = await originalFetch(TEMP_API['200'].url, TEMP_API['200'].args);
          // if status is 401 from token api return empty response to close recursion
          console.log("==========401 UnAuthorize.=============");
          console.log(response);
          if (response.status === 401) {
            return {};
          }
          // else set token
          // recall old fetch
          // here i used 200 because 401 or 404 old response will cause it to rerun
          // return fetch(...args); <- change to this for real scenarios
          // return fetch(args[0], args[1]); <- or to this for real sceaerios
          return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
        }
        // condition will be tested again after 401 condition and will be ran with old args
        if (data.status === 404) {
          console.log("==========404 Not Found.=============");
          // here i used 200 because 401 or 404 old response will cause it to rerun
          // return fetch(...args); <- change to this for real scenarios
          // return fetch(args[0], args[1]); <- or to this for real scenarios
          return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
    sceaerios
        } else {
          return data;
        }
      });
    };
    
    (async function() {
      console.log("==========Example1=============");
      let example1 = await fetch(TEMP_API['404'].url, TEMP_API['404'].args);
      console.log(example1);
      console.log("==========Example2=============");
      let example2 = await fetch(TEMP_API['200'].url, TEMP_API['200'].args);
      console.log(example2);
      console.log("==========Example3=============");
      let example3 = await fetch(TEMP_API['401'].url, TEMP_API['401'].args);
      console.log(example3);
    })();
    1. Example1 向 api 发出 404 状态请求,这将导致 404 条件运行,然后调用 200 api,之后将返回响应
    2. Example2 请求 200 api 将返回 200 状态代码,这将导致 200 条件通过并运行并返回响应
    3. Example3 向 api 发出 401 状态请求,这将导致 401 条件通过,然后调用 200 api 并打印响应,之后它将失去可以设置令牌的条件,然后将在另一个获取请求中使用该令牌

    【讨论】:

    • 您好,感谢您抽出宝贵时间尝试解决此问题!我猜你的意思是我先检查刷新令牌?刷新令牌命中数据库以检查验证,所以我不希望它为每个请求都命中数据库。这就是为什么我需要它仅在访问令牌为假时才检查刷新令牌(返回 401)
    • @Hejhejhej123 请求令牌仅在响应状态为 401 时运行,如果响应状态不是 401,它将永远不会运行,并且如果令牌 api 返回 401,那么您将返回,然后取消获取重试
    • 对不起,我刚刚在代码之前阅读了您的评论,我误解了。但是是否可以在不指定 url 的情况下重新运行原始提取?我只希望它在每个 fetch 调用上运行而不对原始 fetchcode 进行任何修改
    • @Hejhejhej123 我已经用临时未经授权的 api 和解释更新了我的答案。
    • 非常感谢!就像我想要的那样工作。但我没有得到 404 .. 什么时候使用?我没有从服务器发送任何 404,所以我只使用 return 并删除了 if (data.status === 404)。
    【解决方案2】:

    尝试重新调整 fetch promise 而不是等待。

    (function () {
      const originalFetch = fetch;
      fetch = function () {
        return originalFetch.apply(this, arguments).then(function (data) {
          if (data.status === 200) console.log("---------Status 200----------");
          if (data.status === 404) {
              console.log("==========404 Not Found.=============");
              return fetch(`https://jsonplaceholder.typicode.com/todos/2`);
    
          } else {
            return data;
          }
        });
      };
    })();
    
    function test(id) {
      //will trigger 404 status
      return fetch(`https://jsonplaceholder.typicode.com/todos/` + id, {
        method: "POST",
        credentials: "include",
      });
    }
    
    test(1).then((i) => console.log(i));

    【讨论】:

    • 谢谢!这返回了正确的状态并解决了我的第二个问题。我仍然无法解决问题 1,这里有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 2014-09-12
    • 2014-04-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2015-04-16
    相关资源
    最近更新 更多