You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目Fetch封装:异步函数使用Context的问题及替代方案

解决方案

一、解决Fetch拦截器中无法直接使用Context的问题

方案1:将封装的Fetch改为自定义Hook

useContext只能在React组件或自定义Hook内调用,把你的customFetcher包装成自定义Hook,就能直接在内部获取Context,不用每次请求传参数:

import React from 'react';
import { UserContext } from './path/to/UserContext';

const useCustomFetcher = () => {
  const { JWT, setJWT } = React.useContext(UserContext);

  const customFetcher = async (url, config = {}) => {
    // 合并用户传入的headers,避免覆盖原有配置
    const requestConfig = {
      ...config,
      headers: {
        ...config.headers,
        Authorization: `Bearer ${JWT}`,
      },
    };

    let { response, data } = await originalRequest(url, requestConfig);

    // 用状态码判断未授权,比statusText更可靠
    if (response.status === 401) {
      try {
        const refreshRes = await refreshToken();
        // 修正原代码语法错误:需要await解析JSON
        const { accessJWT } = await refreshRes.json();
        setJWT(accessJWT);

        const newConfig = {
          ...requestConfig,
          headers: {
            ...requestConfig.headers,
            Authorization: `Bearer ${accessJWT}`,
          },
        };

        const newResponse = await originalRequest(url, newConfig);
        response = newResponse.response;
        data = newResponse.data;
      } catch (err) {
        // 刷新失败处理:比如跳转登录页、清除用户状态
        console.error('令牌刷新失败:', err);
        throw err;
      }
    }

    return { response, data };
  };

  return customFetcher;
};

组件中使用方式:

const MyComponent = () => {
  const fetcher = useCustomFetcher();

  const loadData = async () => {
    try {
      const { data } = await fetcher('/api/resource');
      // 处理返回数据
    } catch (err) {
      // 处理请求错误
    }
  };

  return <button onClick={loadData}>加载数据</button>;
};

方案2:全局JWT管理器(保持Fetch为普通函数)

如果不想把Fetch改成Hook,可以单独写一个JWT管理模块,同步Context中的状态,这样普通的Fetch函数就能直接调用:

  1. 创建jwtManager.js:
// 存储当前JWT和更新回调
let currentJWT = null;
let updateJWTCallback = null;

// 初始化管理器,从Context传入初始值和更新方法
export const initJWTManager = (initialJWT, callback) => {
  currentJWT = initialJWT;
  updateJWTCallback = callback;
};

// 获取当前JWT
export const getCurrentJWT = () => currentJWT;

// 更新JWT并同步到Context
export const updateJWT = (newJWT) => {
  currentJWT = newJWT;
  if (updateJWTCallback) {
    updateJWTCallback(newJWT);
  }
};
  1. 在根组件初始化管理器:
import React, { useEffect } from 'react';
import { UserContext } from './path/to/UserContext';
import { initJWTManager } from './jwtManager';

const App = () => {
  const { JWT, setJWT } = React.useContext(UserContext);

  useEffect(() => {
    initJWTManager(JWT, setJWT);
  }, [JWT, setJWT]);

  return {/* 应用内容 */};
};
  1. 改写你的Fetch封装:
import { getCurrentJWT, updateJWT } from './jwtManager';

let customFetcher = async (url, config = {}) => {
  const jwt = getCurrentJWT();
  const requestConfig = {
    ...config,
    headers: {
      ...config.headers,
      Authorization: `Bearer ${jwt}`,
    },
  };

  let { response, data } = await originalRequest(url, requestConfig);

  if (response.status === 401) {
    try {
      const refreshRes = await refreshToken();
      const { accessJWT } = await refreshRes.json();
      updateJWT(accessJWT);

      const newConfig = {
        ...requestConfig,
        headers: {
          ...requestConfig.headers,
          Authorization: `Bearer ${accessJWT}`,
        },
      };

      const newResponse = await originalRequest(url, newConfig);
      response = newResponse.response;
      data = newResponse.data;
    } catch (err) {
      console.error('令牌刷新失败:', err);
      throw err;
    }
  }

  return { response, data };
};

这样组件里可以直接调用customFetcher,不用传任何Context相关参数。

二、关于JWT的存储建议

accessJWT(短令牌)

  • 优先存在React Context/全局状态(内存中):因为accessJWT有效期短,即使被窃取危害小,而且前端可以直接读取放到请求头。
  • 如果需要跨域请求或服务端渲染,可以同时存到Cookie,但要设置Secure(仅HTTPS传输)、SameSite=Strict/Lax(防CSRF),不要设置HttpOnly(否则前端拿不到,没法手动加到请求头)。

refreshJWT(长令牌)

  • 必须存在HttpOnly Cookie:HttpOnly Cookie前端无法读取,只能由浏览器自动携带到后端的刷新接口,能有效避免XSS攻击窃取。同时要设置Secure、SameSite=Strict,进一步提升安全性。
  • 绝对不能存在localStorage/内存/普通Cookie,否则一旦前端被XSS攻击,refreshJWT被窃取后,攻击者可以长期冒充用户。

内容的提问来源于stack exchange,提问作者Billy Rogers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:05:26