• 问题: 在 axios 封装时, 我们经常在响应拦截器中做统一的错误异常处理以减少重复代码, 但是有些情况又想要自行处理失败和异常, 怎么办?
  • 解决: 可以通过给 axios 扩展 skipErrorHandler 请求入参的方式实现, 以下是具体实现

封装

以下代码重点突出错误处理逻辑, 已简化 axios 的其他封装逻辑

假设接口响应数据结构为:

{
    errcode:0, // 错误码, 0-成功, 其他-失败
    errmsg: "失败原因" // 失败信息
    data: {...} // 响应数据
}
// src/utils/request.ts
import { message, notification } from 'antd';
import axios from 'axios';
import type { AxiosError, AxiosResponse } from 'axios';
 
// 扩展 axios 的 TS 类型
declare module 'axios' {
  export interface AxiosRequestConfig {
    /** 是否跳过统一错误处理 */
    skipErrorHandler?: boolean;
  }
}
 
const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队(异步任务)。',
  204: '删除数据成功。',
  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限(令牌、用户名、密码错误)。',
  403: '用户得到授权,但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
  405: '请求方法不被允许。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除,且不会再得到的。',
  422: '当创建一个对象时,发生一个验证错误。',
  500: '服务器发生错误,请检查服务器。',
  502: '网关错误。',
  503: '服务不可用,服务器暂时过载或维护。',
  504: '网关超时。',
};
 
const request = axios.create();
 
// 响应拦截器
request.interceptors.response.use(
  (response: AxiosResponse) => {
    const res = response.data;
    const { skipErrorHandler } = response.config;
    const { errcode, errmsg = '未知的业务处理错误' } = res;
 
    // 选择性忽略错误码统一处理
    if (errcode && !skipErrorHandler) {
      message.error(errmsg);
    }
 
    return res;
  },
  (error: AxiosError) => {
    const { skipErrorHandler } = error.config;
 
    // 忽略统一异常处理
    if (skipErrorHandler) {
      return Promise.reject(error);
    }
 
    // 异常统一处理
    const status = error.response?.status ?? '';
    const message = codeMessage[status] || error.message || '未知异常';
    const url = error.config.url ?? '';
    notification.error({
      message: `请求错误 ${status}: ${url}`,
      description: message,
    });
 
    return Promise.reject(error);
  },
);
 
export default request;

使用

// src/services/user.ts 定义请求方法
export async function updateName(data: {
  name: string;
  id: number;
}): Promise<API.BaseType<boolean>> {
  return request.put('/user/update/password', data, {
    skipErrorHandler: true, // 忽略拦截器中的错误处理
  });
}
// src/pages/xxx.tsx 发起请求, 自行处理错误
const editName = async () => {
  try {
    const res = await updateName({ name: 'lisa', id: 1 });
    if (res.errcode) {
      // 修改成功
    } else {
      // 业务失败处理
    }
  } catch (error) {
    // 接口异常处理
  }
};

总结

通过给 axios 的入参扩展一个自定义参数(skipErrorHandler), 在响应拦截器中拿到这个参数, true表示忽略统一的错误处理逻辑, false反之