×

先搞懂axios和Web API各自是啥?

提问者:Terry2025.12.15浏览:26

前端开发里,和后端接口打交道是绕不开的事儿,而axios作为当下热门的HTTP请求工具,和Web API的对接更是核心环节,你是不是也遇到过“请求发出去了但没数据”“跨域报错一头雾水”“token加了还是没权限”这些问题?这篇文章就用问答思路,把axios对接Web API的门道拆明白,从基础到实战,再到踩坑解决,帮你把数据交互这事儿理顺!

很多刚入门的同学会疑惑:axios是干啥的?Web API又是什么?简单说,axios是个能在浏览器和Node.js里发HTTP请求的工具,基于Promise设计,写代码时链式调用或者async/await都很顺手,而Web API是后端开发同学提供的“数据接口”,比如你做电商项目,获取商品列表、下单这些功能,后端会暴露对应的API地址(像/api/goods/api/order),前端通过请求这些地址来拿数据或者提交数据。

举个生活例子:你去奶茶店点单(前端发请求),菜单就是Web API里的“接口列表”(哪些能点),店员接收你的订单并和后厨沟通(后端处理请求),最后把奶茶给你(返回响应),axios就像是你和店员沟通的“语言工具”,让这个过程更顺畅。

为啥选axios对接Web API?它有几个硬优势:一是自动把响应数据转成JSON,不用自己手动parse;二是能设置请求拦截器、响应拦截器,统一处理token、loading这些逻辑;三是对请求取消、超时控制这些细节支持得很友好,比起原生fetch或者XMLHttpRequest,写代码效率高太多。

axios对接Web API的核心步骤是啥?

想让axios和Web API“打通”,得按步骤来,每一步都有讲究:

把axios引进项目里

如果是Vue、React这类前端框架项目,一般用npm安装:npm install axios,然后在需要的文件里引入:import axios from 'axios';,要是纯静态页面,用CDN更方便:<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>,这样全局就有axios对象了。

配置基础信息,减少重复代码

每个Web API都有固定的基础地址,比如后端接口统一是https://api.myblog.com/v1开头,那可以给axios设置baseURL,以后发请求就不用每次写完整地址,还能设置超时时间(比如5秒没响应就报错)、请求头(比如默认加Content-Type),代码长这样:

const request = axios.create({
  baseURL: 'https://api.myblog.com/v1', // 后端API的基础地址
  timeout: 5000, // 超时时间,单位毫秒
  headers: { 'Content-Type': 'application/json' } // 默认请求头
});

这样创建的request实例,后续发请求更简洁,还能给不同模块(比如用户模块、文章模块)创建不同实例,管理更灵活。

发起基本请求:GET、POST怎么用?

Web API最常用的就是GET(查数据)、POST(提交数据)这些请求方式,用axios写起来很直观:

  • GET请求(拿数据):比如获取文章列表,后端接口是/articles,还能传页码参数,用axios的话:

// 方式一:params传参
request.get('/articles', {
  params: { page: 1, size: 10 } // 会自动拼到url后面变成?page=1&size=10
}).then(res => {
  console.log('文章列表', res.data); // res.data是后端返回的实际数据
}).catch(err => {
  console.error('请求失败', err);
});
// 方式二:async/await(更简洁)
async function getArticles() {
  try {
    const res = await request.get('/articles', { params: { page: 1 } });
    return res.data;
  } catch (err) {
    // 处理错误
  }
}
  • POST请求(提交数据):比如用户注册,后端接口是/users/register,需要传用户名、密码,代码:

const userInfo = { username: '小明', password: '123456' };
request.post('/users/register', userInfo)
  .then(res => {
    console.log('注册成功', res.data);
  })
  .catch(err => {
    if (err.response) {
      // 后端返回了错误状态码,比如400(参数错)、500(服务器炸了)
      console.log('后端返回错误:', err.response.data.msg);
    } else {
      // 网络问题,比如断网、超时
      console.log('网络请求失败');
    }
  });

这里要注意,POST请求的第二个参数是请求体(body),axios会根据请求头自动处理格式,如果是表单提交(Content-Type是application/x-www-form-urlencoded),得用qs库把对象转成键值对格式,

import qs from 'qs';
const userInfo = { username: '小明', password: '123456' };
request.post('/users/login', qs.stringify(userInfo), {
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});

响应和错误怎么处理更稳?

后端返回的数据里,一般会有状态码(比如200成功,401没权限,500服务器错)和数据体,axios的响应对象res里,res.data才是后端真正返回的业务数据,res.status是HTTP状态码,res.headers是响应头。

错误处理分两种情况:一是请求发出去了但后端返回错误状态码(比如400、404),这时err.response存在;二是请求没发出去(比如网络断了、超时),这时err.responseundefined,所以catch里要分情况处理:

async function fetchData() {
  try {
    const res = await request.get('/some-api');
    // 成功,处理res.data
  } catch (err) {
    if (err.response) {
      // 后端返回错误
      const { status, data } = err.response;
      if (status === 401) {
        // 没权限,跳登录页
        window.location.href = '/login';
      } else if (status === 400) {
        alert('参数错误:' + data.msg);
      }
    } else {
      // 网络或超时错误
      alert('请检查网络连接');
    }
  }
}

对接时最容易踩的坑,咋解决?

很多同学跟着步骤做还是会报错,这部分把高频问题拎出来,教你怎么破:

跨域报错:No 'Access-Control-Allow-Origin' header...

这是浏览器的同源策略搞的鬼(协议、域名、端口有一个不一样就算跨域),解决得前后端配合:

  • 后端配置CORS:后端在响应头里加Access-Control-Allow-Origin: *(开发时可以,生产要限制域名),还要允许对应的请求方法(GET、POST等)和请求头,比如Node.js的Express框架,装cors中间件:npm install cors,然后用:

const cors = require('cors');
app.use(cors());
  • 前端开发时用代理:比如Vue项目,在vue.config.js里配置devServer.proxy,把前端请求代理到后端域名,假装是同源请求:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.myblog.com', // 后端真实地址
        changeOrigin: true, // 开启代理
        pathRewrite: { '^/api': '' } // 把请求里的/api替换成空
      }
    }
  }
};

这样前端发请求到/api/articles,会被代理到https://api.myblog.com/articles,绕开跨域限制。

多次请求重复,数据覆盖(竞态问题)

比如用户快速点“刷新列表”按钮,多次发GET请求,后发的请求先返回,导致界面显示旧数据,解决用axios的取消请求功能:

let cancelToken = null;
async function refreshList() {
  // 每次请求前,取消上一次的请求
  if (cancelToken) {
    cancelToken.cancel('请求被取消');
  }
  cancelToken = axios.CancelToken.source(); // 创建新的取消令牌
  try {
    const res = await request.get('/articles', {
      cancelToken: cancelToken.token
    });
    // 处理数据
  } catch (err) {
    if (axios.isCancel(err)) {
      console.log('请求被取消', err.message);
    } else {
      // 其他错误处理
    }
  }
}

token失效或权限不足(401错误)

后端一般用token做身份验证,请求时要把token放到请求头里,可以用**请求拦截器**统一加token:

request.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, err => {
  return Promise.reject(err);
});

然后用**响应拦截器**处理401错误(比如token过期,跳登录页):

request.interceptors.response.use(res => {
  return res;
}, err => {
  if (err.response && err.response.status === 401) {
    // 清除过期token
    localStorage.removeItem('token');
    // 跳转到登录页
    window.location.href = '/login';
  }
  return Promise.reject(err);
});

数据格式不对,后端收不到或前端解析错

比如前端发POST请求,后端期望form-data格式,但前端用了json格式,就会解析失败,这时候要检查请求头和数据格式:

  • 发form-data:用FormData对象,或者qs库转成键值对,同时设置请求头:

const formData = new FormData();
formData.append('username', '小明');
formData.append('avatar', file); // 上传文件时用FormData更方便
request.post('/users/upload', formData, {
  headers: { 'Content-Type': 'multipart/form-data' }
});
  • 后端返回非JSON格式:比如返回纯文本,axios默认会尝试转JSON导致报错,这时候在请求配置里加responseType: 'text'

request.get('/api/text', {
  responseType: 'text'
}).then(res => {
  console.log(res.data); // 现在是纯文本
});

实战:用axios对接RESTful Web API做个博客系统

光说不练假把式,现在模拟一个博客系统的核心功能,看axios怎么和Web API配合:

场景:用户登录 + 获取文章列表 + 发布文章

配置axios实例(统一管理基础地址、token)

import axios from 'axios';
// 创建带配置的axios实例
const api = axios.create({
  baseURL: 'https://api.blogdemo.com/v1',
  timeout: 6000,
  headers: { 'Content-Type': 'application/json' }
});
// 请求拦截器:加token
api.interceptors.request.use(config => {
  const token = localStorage.getItem('blog_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
// 响应拦截器:处理401
api.interceptors.response.use(res => res, err => {
  if (err.response?.status === 401) {
    localStorage.removeItem('blog_token');
    window.location.href = '/login.html';
  }
  return Promise.reject(err);
});
export default api;

用户登录功能(POST请求)

import api from './api';
// 登录函数
export async function login(user) {
  try {
    const res = await api.post('/auth/login', user);
    // 登录成功,存token
    localStorage.setItem('blog_token', res.data.token);
    return res.data; // 返回用户信息
  } catch (err) {
    if (err.response) {
      // 后端返回错误,比如账号密码错
      return { code: err.response.status, msg: err.response.data.msg };
    } else {
      return { code: 500, msg: '网络连接失败' };
    }
  }
}
// 调用登录
const user = { username: 'test', password: '123' };
login(user).then(res => {
  if (res.code === 200) {
    alert('登录成功');
  } else {
    alert(res.msg);
  }
});

获取文章列表(GET请求,带参数)

export async function getArticleList(page = 1) {
  try {
    const res = await api.get('/articles', {
      params: { page, size: 10 }
    });
    return res.data.list; // 假设后端返回{ list: [...], total: 100 }
  } catch (err) {
    console.error('获取文章列表失败', err);
    return [];
  }
}
// 调用
getArticleList(1).then(list => {
  if (list.length) {
    // 渲染到页面
    renderArticles(list);
  } else {
    alert('暂无文章');
  }
});

发布文章(POST请求,带token)

export async function publishArticle(data) {
  try {
    const res = await api.post('/articles', data);
    return res.data; // 后端返回文章ID等信息
  } catch (err) {
    if (err.response) {
      return { code: err.response.status, msg: err.response.data.msg };
    } else {
      return { code: 500, msg: '发布失败,请检查网络' };
    }
  }
}
// 调用:假设data是{ title: '我的第一篇博客', content: '正文...' }
const articleData = { title: '测试', content: '内容' };
publishArticle(articleData).then(res => {
  if (res.code === 200) {
    alert('发布成功,文章ID:' + res.data.id);
  } else {
    alert(res.msg);
  }
});

这个实战案例里,能看到axios实例的配置、拦截器的作用、不同请求的写法,还有错误处理的细节,实际项目中,还可以把这些函数封装到Vuex的action里,或者React的hooks中,让组件更简洁。

进阶:让axios对接更高效的技巧

把基础流程跑通后,还要优化体验和代码质量,这些技巧能帮你更上一层楼:

拦截器玩出花:统一处理loading、错误提示

很多项目里,发请求时要显示loading,请求结束后隐藏,用请求拦截器和响应拦截器统一处理:

// 全局loading(假设用element-ui的Loading组件)
import { Loading } from 'element-ui';
let loadingInstance = null;
// 请求拦截器:显示loading
api.interceptors.request.use(config => {
  loadingInstance = Loading.service({ fullscreen: true });
  return config;
}, err => {
  loadingInstance.close();
  return Promise.reject(err);
});
// 响应拦截器:隐藏loading
api.interceptors.response.use(res => {
  loadingInstance.close();
  return res;
}, err => {
  loadingInstance.close();
  if (err.response?.status === 401) {
    // 之前的处理
  }
  return Promise.reject(err);
});

这样所有请求都会自动显示loading,不用每个请求都写重复代码。

封装请求工具:减少重复,提高可维护性

把常用的GET、POST、PUT、DELETE封装

您的支持是我们创作的动力!

网友回答文明上网理性发言 已有0人参与

发表评论: