获取和获取重试的拦截器?(JavaScript)

9lowa7mx  于 2023-08-02  发布在  Java
关注(0)|答案(4)|浏览(109)

我正在尝试创建一个拦截器,用于在JavaScript中获取(更具体地说是React)。它应该从每次调用的获取中获取结果,如果是401错误,它应该向另一个路由发起新的获取调用以获取cookie(刷新令牌)。然后,应该再次尝试原始的fetch调用(因为现在用户已经登录)。
我已经成功地触发了新的fetch调用,并为每个调用发回了cookie,但我遇到了以下两个问题:
1.我现在不知道如何在收到刷新令牌后重试fetch调用。这可能吗?我发现了fetch-retry npm(https://www.npmjs.com/package/fetch-retry),但不确定如何以及是否可以在拦截器上实现它,因为它应该为原始的fetch调用完成。
1.我似乎在async await上做错了什么(我认为),因为拦截在返回数据之前没有等待fetch调用(原始fetch的状态码似乎是401而不是200,在我们获得cookie之后应该是200。我还尝试返回拦截器内部的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,如果有,则:
1.获取API/令牌
1.如果API/token返回401,它应该只返回401。
1.如果API/token返回200,它应该再次运行original fetch

d5vmydt9

d5vmydt91#

你可以简单地使用originalFetch作为token并等待响应,如果响应是401,那么你只需返回空响应到第一个fetch调用,否则你更新了token,然后让它进入下一个条件,这将重新运行旧的请求。

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条件运行,然后调用200api,之后返回响应
  2. Example2向API发出的请求将返回200状态代码,这将导致200条件通过并运行,并返回响应
  3. Example3请求API获取401状态,这将导致401条件通过,然后调用200api并打印响应,之后它将脱离条件,您可以设置令牌,然后在另一个获取请求中使用
4smxwvx5

4smxwvx52#

尝试返回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));

字符串

ru9i0ody

ru9i0ody3#

Interceptor library用于本机获取命令。它修补了全局获取方法,并允许您在浏览器,节点和Webworker环境中使用。
fetch-retry它封装了任何Fetch API包(例如:同构获取、交叉获取、同构取消获取等),并重试由于网络问题而失败的请求。它还可以配置为重试特定HTTP状态代码的请求。

wh6knrhe

wh6knrhe4#

下面是一个使用return-fetch library在响应状态为401时重试的示例:https://stackblitz.com/edit/return-fetch-example-retrying?file=index.ts

import returnFetch from 'return-fetch';

const myFetch = returnFetch({
  interceptors: {
    response: async (response, requestArgs) => {
      console.log('requestArgs', requestArgs);
      if (response.status !== 401) {
        return response;
      }

      console.log('not authorized, trying to get refresh cookie..');
      const resopnseToRefreshCookie = await fetch('https://httpstat.us/200');
      if (resopnseToRefreshCookie.status !== 200) {
        throw Error('failed to refresh cookie');
      }

      console.log('succeeded to refresh cookie and retry request');
      return fetch(...requestArgs);
    },
  },
});

myFetch('https://httpstat.us/401')
  .then((it) => it.text())
  .then((body) => {
    console.log('retried but got 401 again. body:', body);
  });

/* results:

  requestArgs ["https://httpstat.us/401", {…}]
  not authorized, trying to get refresh cookie..
  succeeded to refresh cookie and retry request
  retried but got 401 again. body: 401 Unauthorized
*/

字符串
如果你想用自己的fetch替换全局fetch,只需保存全局fetch引用并替换它。

import returnFetch from 'return-fetch';

const globalFetch = window.fetch;
const myFetch = returnFetch({
  refch: globalFetch, // input global fetch here
  interceptors: {
    response: async (requestArgs, response) => {
      // use 'globalFetch' here instead of 'fetch'
    },
  },
});

window.fetch = myFetch;

相关问题