Axios实战:如何优雅地为所有请求自动添加Token请求头

1次阅读
没有评论

共计 2689 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

背景痛点

在前端开发中,我们经常需要与后端 API 进行交互,而大多数 API 都需要在请求头中添加 Token 进行身份验证。手动管理 Token 会带来以下问题:

Axios 实战:如何优雅地为所有请求自动添加 Token 请求头

  • 重复代码 :需要在每个请求中手动添加 Authorization 头
  • 遗漏风险 :容易忘记添加 Token 导致请求失败
  • 刷新复杂 :Token 过期时需要手动刷新并重新发送请求

传统解决方案如直接从 localStorage 读取 Token 或手动添加 Header 都存在明显缺陷:

  • 直接在每次请求前从存储中读取 Token 不安全
  • 手动添加 Header 会导致代码冗余
  • 无法统一处理 Token 刷新逻辑

技术方案

Axios 提供了强大的拦截器机制,可以让我们在请求发送前和响应接收时进行统一处理。核心思路如下:

  1. 创建独立的 axios 实例
  2. 配置请求拦截器自动注入 Token
  3. 配置响应拦截器处理 401 未授权状态
  4. 实现 Token 刷新和请求重试机制

Axios 拦截器工作原理

Axios 拦截器分为请求拦截器和响应拦截器,它们按照添加顺序形成一个链式调用管道:

  • 请求拦截器:从最后一个添加到第一个依次执行
  • 响应拦截器:从第一个添加到最后一个依次执行

代码实现

下面是一个完整的 TypeScript 实现方案:

import axios, {AxiosInstance, AxiosRequestConfig, AxiosError} from 'axios';

interface TokenStorage {getToken(): string | null;
  setToken(token: string): void;
  clearToken(): void;}

class AuthAxios {
  private instance: AxiosInstance;
  private isRefreshing = false;
  private refreshSubscribers: ((token: string) => void)[] = [];

  constructor(private tokenStorage: TokenStorage, baseURL: string) {this.instance = axios.create({ baseURL});

    this.setupRequestInterceptor();
    this.setupResponseInterceptor();}

  private setupRequestInterceptor() {
    this.instance.interceptors.request.use((config) => {const token = this.tokenStorage.getToken();
        if (token) {config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );
  }

  private setupResponseInterceptor() {
    this.instance.interceptors.response.use((response) => response,
      async (error: AxiosError) => {
        const originalRequest = error.config;

        if (error.response?.status === 401 && !originalRequest._retry) {if (this.isRefreshing) {return new Promise((resolve) => {this.refreshSubscribers.push((token) => {originalRequest.headers.Authorization = `Bearer ${token}`;
                resolve(this.instance(originalRequest));
              });
            });
          }

          originalRequest._retry = true;
          this.isRefreshing = true;

          try {const newToken = await this.refreshToken();
            this.tokenStorage.setToken(newToken);

            originalRequest.headers.Authorization = `Bearer ${newToken}`;
            this.refreshSubscribers.forEach(cb => cb(newToken));
            this.refreshSubscribers = [];

            return this.instance(originalRequest);
          } catch (refreshError) {this.tokenStorage.clearToken();
            return Promise.reject(refreshError);
          } finally {this.isRefreshing = false;}
        }

        return Promise.reject(error);
      }
    );
  }

  private async refreshToken(): Promise<string> {
    // 实现你的 Token 刷新逻辑
    // 示例:调用刷新 Token 的 API
    const response = await axios.post('/auth/refresh');
    return response.data.token;
  }

  public getInstance(): AxiosInstance {return this.instance;}
}

生产环境考量

在实际生产环境中,我们还需要考虑以下方面:

安全建议

  • 使用 HttpOnly Cookie 存储敏感 Token
  • 实现 CSRF 防护机制
  • 避免在拦截器中存储过多状态导致内存泄漏

性能优化

  • 减少不必要的 Token 验证请求
  • 实现请求缓存机制
  • 对于公开 API 跳过 Token 注入

单元测试

测试时应重点覆盖以下场景:

  1. 正常请求自动添加 Token
  2. 401 状态码触发 Token 刷新
  3. 并发请求时的 Token 刷新队列
  4. 刷新 Token 失败后的处理

避坑指南

在实现过程中可能会遇到以下问题:

  1. 拦截器执行顺序 :后添加的请求拦截器先执行
  2. 循环重试 :设置最大重试次数避免死循环
  3. SSR 场景 :服务端渲染时注意 Cookie 处理

延伸思考

这种模式可以与其他技术栈结合:

  • Redux:将 Token 存储在 Redux 中并通过中间件注入
  • SWR:配合 SWR 的全局配置实现类似功能
  • GraphQL:Apollo Client 提供了类似的认证拦截机制

通过这种集中式管理 Token 的方式,我们的代码会更加简洁健壮,同时也更容易维护和扩展。希望这篇指南能帮助你更好地理解和使用 Axios 的拦截器机制。

正文完
 0
评论(没有评论)