Axios 详解

Axios是一个基于Promise的HTTP客户端,可以在浏览器和Node.js环境中使用。它具有强大的功能和简洁的API,已成为前端开发中处理网络请求的首选工具。

1. Axios 基本介绍

Axios的主要特点:

  • 基于Promise:支持Promise API,可以使用async/await语法
  • 跨平台:同时支持浏览器和Node.js环境
  • 拦截请求和响应:可以在请求发送前或响应返回后进行处理
  • 转换请求和响应数据:自动转换JSON数据
  • 取消请求:支持取消请求功能
  • 自动处理CSRF保护:客户端支持防御XSRF攻击
  • 支持上传下载进度监控:可以监控上传和下载的进度

2. 安装与使用

安装

# 使用npm安装
npm install axios

# 使用yarn安装
yarn add axios

# 使用pnpm安装
pnpm add axios

基本使用

// 导入axios
import axios from 'axios';

// 发起GET请求
axios.get('/user?ID=12345')
  .then(function (response) {
    // 处理成功情况
    console.log(response.data);
  })
  .catch(function (error) {
    // 处理错误情况
    console.log(error);
  })
  .finally(function () {
    // 总是会执行
  });

// 使用async/await
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}

3. 请求方法

Axios支持所有常见的HTTP请求方法:

// GET请求
axios.get(url[, config])

// POST请求
axios.post(url[, data[, config]])

// PUT请求
axios.put(url[, data[, config]])

// DELETE请求
axios.delete(url[, config])

// HEAD请求
axios.head(url[, config])

// OPTIONS请求
axios.options(url[, config])

// PATCH请求
axios.patch(url[, data[, config]])

4. 请求配置

Axios请求可以通过配置对象进行自定义:

axios({
  method: 'post', // 请求方法
  url: '/user/12345', // 请求URL
  data: { // 请求体数据
    firstName: '张',
    lastName: '三'
  },
  headers: { // 自定义请求头
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  params: { // URL参数
    ID: 12345
  },
  timeout: 1000, // 请求超时时间(毫秒)
  responseType: 'json', // 响应类型
  withCredentials: false // 是否携带凭证
})

5. 创建实例

可以创建自定义的Axios实例,便于设置基本配置:

// 创建自定义实例
const instance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: {'X-Custom-Header': 'foobar'}
});

// 使用实例发送请求
instance.get('/user')
  .then(response => console.log(response.data));

6. 拦截器

Axios的拦截器是其最强大的功能之一,允许在请求发送前和响应接收后进行处理。

请求拦截器

// 添加请求拦截器
axios.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么
    // 例如:添加认证信息
    config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
    return config;
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

响应拦截器

// 添加响应拦截器
axios.interceptors.response.use(
  response => {
    // 对响应数据做点什么
    return response;
  },
  error => {
    // 对响应错误做点什么
    if (error.response) {
      // 服务器返回了错误状态码
      if (error.response.status === 401) {
        // 未授权,可能需要重新登录
        console.log('未授权,请重新登录');
      } else if (error.response.status === 404) {
        console.log('请求的资源不存在');
      } else if (error.response.status >= 500) {
        console.log('服务器错误,请稍后再试');
      }
    } else if (error.request) {
      // 请求已发出,但没有收到响应
      console.log('网络错误,请检查您的网络连接');
    } else {
      // 请求配置出错
      console.log('请求配置错误:', error.message);
    }
    return Promise.reject(error);
  }
);

7. 错误处理最佳实践

错误处理是使用Axios时的重要部分,以下是一些最佳实践:

// 创建一个axios实例
const api = axios.create({
  baseURL: '/api',
  timeout: 10000
});

// 统一错误处理
api.interceptors.response.use(
  response => {
    // 如果后端约定返回格式包含success字段
    const res = response.data;
    if (res.success === false) {
      // 业务逻辑错误
      console.error(res.message || '操作失败');
      // 可以使用UI库显示错误信息
      // Message.error(res.message || '操作失败');
      return Promise.reject(new Error(res.message || '操作失败'));
    }
    return res; // 直接返回数据部分,简化后续使用
  },
  error => {
    let message = '';
    if (error.response) {
      // HTTP状态码相关错误
      switch (error.response.status) {
        case 400:
          message = '请求错误';
          break;
        case 401:
          message = '未授权,请重新登录';
          // 可以在这里处理登出逻辑
          break;
        case 403:
          message = '拒绝访问';
          break;
        case 404:
          message = '请求地址出错';
          break;
        case 408:
          message = '请求超时';
          break;
        case 500:
          message = '服务器内部错误';
          break;
        case 501:
          message = '服务未实现';
          break;
        case 502:
          message = '网关错误';
          break;
        case 503:
          message = '服务不可用';
          break;
        case 504:
          message = '网关超时';
          break;
        default:
          message = `未知错误 ${error.response.status}`;
      }
    } else if (error.request) {
      message = '网络异常,请检查您的网络连接';
    } else {
      message = error.message;
    }
    
    console.error(message);
    // 可以使用UI库显示错误信息
    // Message.error(message);
    
    return Promise.reject(error);
  }
);

8. 取消请求

Axios支持取消请求,有两种方式:

使用AbortController(推荐,v0.22.0+)

// 创建一个AbortController实例
const controller = new AbortController();

// 发送请求
axios.get('/api/data', {
  signal: controller.signal
}).catch(function(error) {
  if (axios.isCancel(error)) {
    console.log('请求已被取消', error.message);
  } else {
    // 处理错误
  }
});

// 取消请求
controller.abort();

使用CancelToken(已弃用)

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/api/data', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('请求已被取消', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求
source.cancel('操作被用户取消');

9. 处理表单数据

x-www-form-urlencoded格式

// 使用URLSearchParams
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/api/form', params);

// 使用qs库
import qs from 'qs';
axios.post('/api/form', qs.stringify({ param1: 'value1', param2: 'value2' }));

// axios自动序列化(v0.27.0+)
axios.post('/api/form', { param1: 'value1', param2: 'value2' }, {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
});

multipart/form-data格式(文件上传)

// 使用FormData
const formData = new FormData();
formData.append('file', document.querySelector('#fileInput').files[0]);
formData.append('name', 'file名称');
axios.post('/api/upload', formData);

// axios自动序列化(v0.27.0+)
axios.post('/api/upload', {
  file: document.querySelector('#fileInput').files[0],
  name: 'file名称'
}, {
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

10. 进度监控

Axios支持上传和下载进度监控:

axios.post('/api/upload', formData, {
  onUploadProgress: function(progressEvent) {
    // 处理上传进度
    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log(`上传进度: ${percentCompleted}%`);
  },
  onDownloadProgress: function(progressEvent) {
    // 处理下载进度
    const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
    console.log(`下载进度: ${percentCompleted}%`);
  }
});

11. 实际项目中的封装示例

在实际项目中,通常会对Axios进行封装,以下是一个完整的封装示例:

import axios from 'axios';
import { Message } from 'element-ui'; // 假设使用Element UI
import router from '@/router'; // 假设使用Vue Router

// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 从环境变量中获取API基础URL
  timeout: 10000 // 请求超时时间
});

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 从localStorage获取token
    const token = localStorage.getItem('token');
    if (token) {
      // 设置token到请求头
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    console.log(error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data;
    
    // 如果后端约定返回格式包含code字段
    if (res.code !== 200) {
      Message({
        message: res.message || '操作失败',
        type: 'error',
        duration: 5 * 1000
      });
      
      // 处理特定的错误码
      if (res.code === 401) {
        // 未授权,可能是token过期
        // 弹出确认框,询问是否重新登录
        MessageBox.confirm(
          '您的登录已过期,请重新登录',
          '确认退出',
          {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning'
          }
        ).then(() => {
          // 清除用户信息并跳转到登录页
          localStorage.removeItem('token');
          router.push(`/login?redirect=${router.currentRoute.fullPath}`);
        });
      }
      return Promise.reject(new Error(res.message || '操作失败'));
    } else {
      // 直接返回数据部分,简化后续使用
      return res.data;
    }
  },
  error => {
    let message = '';
    if (error.response) {
      // HTTP状态码相关错误
      switch (error.response.status) {
        case 400:
          message = '请求错误';
          break;
        case 401:
          message = '未授权,请重新登录';
          // 清除用户信息并跳转到登录页
          localStorage.removeItem('token');
          router.push(`/login?redirect=${router.currentRoute.fullPath}`);
          break;
        case 403:
          message = '拒绝访问';
          break;
        case 404:
          message = '请求地址出错';
          break;
        case 408:
          message = '请求超时';
          break;
        case 500:
          message = '服务器内部错误';
          break;
        case 501:
          message = '服务未实现';
          break;
        case 502:
          message = '网关错误';
          break;
        case 503:
          message = '服务不可用';
          break;
        case 504:
          message = '网关超时';
          break;
        default:
          message = `未知错误 ${error.response.status}`;
      }
    } else if (error.request) {
      message = '网络异常,请检查您的网络连接';
    } else {
      message = error.message;
    }
    
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000
    });
    
    return Promise.reject(error);
  }
);

// 封装GET请求
export function get(url, params = {}) {
  return service({
    url,
    method: 'get',
    params
  });
}

// 封装POST请求
export function post(url, data = {}) {
  return service({
    url,
    method: 'post',
    data
  });
}

// 封装PUT请求
export function put(url, data = {}) {
  return service({
    url,
    method: 'put',
    data
  });
}

// 封装DELETE请求
export function del(url, params = {}) {
  return service({
    url,
    method: 'delete',
    params
  });
}

export default service;

12. 总结

Axios是一个功能强大且易于使用的HTTP客户端库,它提供了许多便捷的功能,如拦截器、请求取消、进度监控等。通过合理的封装和配置,可以大大简化API请求的处理,提高开发效率。在实际项目中,建议根据项目需求对Axios进行适当的封装,统一处理错误,简化数据获取,使代码更加简洁和可维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值