Axios POST 请求的 Transform 处理

Gary Chen
Axios POST 请求的 Transform 处理

Axios POST 请求的 Transform 处理

Axios 本身已经内置了请求和响应拦截器(interceptors)机制,可以方便地实现 transform 处理。下面我将详细介绍如何使用 Axios 进行 POST 请求的 transform 处理。

基础封装

import axios from 'axios';

class ApiClient {
  constructor(baseURL = '') {
    this.instance = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // 添加请求拦截器
    this.instance.interceptors.request.use(
      this.transformRequest,
      this.transformRequestError
    );

    // 添加响应拦截器
    this.instance.interceptors.response.use(
      this.transformResponse,
      this.transformResponseError
    );
  }

  /**
   * 请求transform
   * @param {Object} config - axios 请求配置
   * @returns {Object} 转换后的请求配置
   */
  transformRequest(config) {
    // 可以在这里添加认证token等
    // config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
    
    // 对POST请求数据进行处理
    if (config.method?.toLowerCase() === 'post' && config.data) {
      // 示例:添加请求时间戳
      config.data = {
        ...config.data,
        _timestamp: Date.now(),
      };
      
      // 可以在这里对数据进行加密等操作
      // config.data = this.encryptData(config.data);
    }
    
    return config;
  }

  /**
   * 请求错误transform
   * @param {Error} error 
   * @returns {Promise} 转换后的错误
   */
  transformRequestError(error) {
    return Promise.reject(error);
  }

  /**
   * 响应transform
   * @param {Object} response - axios 响应对象
   * @returns {Object} 转换后的响应数据
   */
  transformResponse(response) {
    // 可以在这里对响应数据进行统一处理
    // 例如解密数据、标准化响应格式等
    
    // 示例:直接返回data字段
    return response.data;
  }

  /**
   * 响应错误transform
   * @param {Error} error 
   * @returns {Promise} 转换后的错误
   */
  transformResponseError(error) {
    if (error.response) {
      // 服务器返回了错误状态码 (4xx, 5xx)
      const { status, data } = error.response;
      error.message = data?.message || `请求失败,状态码: ${status}`;
      error.status = status;
      error.data = data;
    } else if (error.request) {
      // 请求已发出但没有收到响应
      error.message = '网络错误,请检查网络连接';
    } else {
      // 请求配置出错
      error.message = '请求配置错误';
    }
    
    return Promise.reject(error);
  }

  /**
   * POST 请求
   * @param {string} url - 请求路径
   * @param {Object} data - 请求数据
   * @param {Object} config - axios 配置
   * @returns {Promise} 请求结果
   */
  post(url, data = {}, config = {}) {
    return this.instance.post(url, data, config);
  }

  // 可以添加其他HTTP方法...
}

使用示例

const api = new ApiClient('https://api.example.com');

// 基本使用
api.post('/users', { name: 'John', age: 30 })
  .then(data => console.log('成功:', data))
  .catch(error => console.error('失败:', error.message));

// 带额外配置
api.post('/login', { username: 'admin', password: '123456' }, {
  headers: {
    'X-Custom-Header': 'value'
  },
  timeout: 5000
});

高级 Transform 功能

1. 请求数据加密/解密

transformRequest(config) {
  // 加密请求数据
  if (config.method?.toLowerCase() === 'post' && config.data) {
    config.data = this.encrypt(config.data);
  }
  return config;
}

transformResponse(response) {
  // 解密响应数据
  if (response.data && typeof response.data === 'object') {
    response.data = this.decrypt(response.data);
  }
  return response.data;
}

encrypt(data) {
  // 实际项目中应使用更安全的加密方式
  return {
    payload: btoa(JSON.stringify(data)),
    version: '1.0'
  };
}

decrypt(data) {
  try {
    if (data.payload) {
      return JSON.parse(atob(data.payload));
    }
    return data;
  } catch (e) {
    console.error('解密失败', e);
    return data;
  }
}

2. 自动重试机制

constructor(baseURL = '') {
  // ...其他初始化代码
  
  this.retryCount = 3;
  this.retryDelay = 1000;
}

transformResponseError(error) {
  const config = error.config;
  
  // 如果没有重试配置或者已经达到最大重试次数
  if (!config || !config.__retryCount || config.__retryCount >= this.retryCount) {
    return Promise.reject(this.normalizeError(error));
  }
  
  config.__retryCount = config.__retryCount || 0;
  config.__retryCount += 1;
  
  // 创建新的Promise来实现延迟重试
  const delay = new Promise(resolve => {
    setTimeout(() => resolve(), this.retryDelay * config.__retryCount);
  });
  
  return delay.then(() => this.instance(config));
}

normalizeError(error) {
  // ...之前的错误处理逻辑
}

3. 请求取消功能

constructor(baseURL = '') {
  // ...其他初始化代码
  this.cancelTokenSource = axios.CancelToken.source();
}

post(url, data = {}, config = {}) {
  // 将取消令牌加入配置
  const newConfig = {
    ...config,
    cancelToken: this.cancelTokenSource.token
  };
  
  return this.instance.post(url, data, newConfig);
}

cancelRequest(message = '请求已取消') {
  this.cancelTokenSource.cancel(message);
  // 创建新的取消令牌,以便后续请求
  this.cancelTokenSource = axios.CancelToken.source();
}

// 使用示例
const api = new ApiClient('https://api.example.com');
const request = api.post('/data', { query: 'test' });

// 需要时取消请求
api.cancelRequest();

TypeScript 版本

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

interface ApiResponse<T = any> {
  code: number;
  message: string;
  data: T;
}

interface ApiError extends Error {
  status?: number;
  data?: any;
  config?: AxiosRequestConfig;
}

class ApiClient {
  private instance: AxiosInstance;
  private cancelTokenSource = axios.CancelToken.source();

  constructor(baseURL: string = '') {
    this.instance = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    this.setupInterceptors();
  }

  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config: AxiosRequestConfig) => this.transformRequest(config),
      (error: AxiosError) => this.transformRequestError(error)
    );

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => this.transformResponse(response),
      (error: AxiosError) => this.transformResponseError(error)
    );
  }

  private transformRequest(config: AxiosRequestConfig): AxiosRequestConfig {
    // 添加认证token
    // const token = localStorage.getItem('token');
    // if (token) {
    //   config.headers = {
    //     ...config.headers,
    //     Authorization: `Bearer ${token}`,
    //   };
    // }
    
    // POST请求数据处理
    if (config.method?.toLowerCase() === 'post' && config.data) {
      config.data = {
        ...config.data,
        _timestamp: Date.now(),
      };
    }
    
    return config;
  }

  private transformRequestError(error: AxiosError): Promise<AxiosError> {
    return Promise.reject(error);
  }

  private transformResponse(response: AxiosResponse): any {
    // 直接返回data字段
    return response.data;
  }

  private transformResponseError(error: AxiosError): Promise<ApiError> {
    const normalizedError: ApiError = new Error();
    
    if (error.response) {
      // 服务器返回了错误状态码
      const { status, data } = error.response;
      normalizedError.message = (data as any)?.message || `请求失败,状态码: ${status}`;
      normalizedError.status = status;
      normalizedError.data = data;
    } else if (error.request) {
      // 请求已发出但没有收到响应
      normalizedError.message = '网络错误,请检查网络连接';
    } else {
      // 请求配置出错
      normalizedError.message = error.message || '请求配置错误';
    }
    
    return Promise.reject(normalizedError);
  }

  public post<T = any>(url: string, data: any = {}, config: AxiosRequestConfig = {}): Promise<T> {
    const newConfig = {
      ...config,
      cancelToken: this.cancelTokenSource.token,
    };
    
    return this.instance.post(url, data, newConfig);
  }

  public cancelRequest(message: string = '请求已取消'): void {
    this.cancelTokenSource.cancel(message);
    this.cancelTokenSource = axios.CancelToken.source();
  }
}

// 使用示例
interface User {
  id: number;
  name: string;
  age: number;
}

const api = new ApiClient('https://api.example.com');

api.post<User>('/users', { name: 'John', age: 30 })
  .then((user: User) => console.log('创建的用户:', user))
  .catch((error: ApiError) => console.error('错误:', error.message));

与原生 Fetch 封装的对比

  1. 内置拦截器机制:Axios 原生支持请求/响应拦截器,transform 处理更简单
  2. 自动JSON转换:Axios 自动处理请求/响应的JSON转换
  3. 更完善的错误处理:Axios 区分了网络错误、超时错误和HTTP状态码错误
  4. 取消请求:Axios 内置了取消请求的机制
  5. 浏览器/Node.js兼容:Axios 在两种环境下都能工作
  6. 进度跟踪:Axios 支持上传/下载进度跟踪

这个 Axios 封装提供了完整的 transform 处理点,你可以根据项目需求在请求前、响应后和错误时进行各种数据转换和处理。