axios基础

47 阅读6分钟

axios

说到axios我们就不得不说下Ajax。在旧浏览器页面在向服务器请求数据时,因为返回的是整个页面的数据,页面都会强制刷新一下,这对于用户来讲并不是很友好。并且我们只是需要修改页面的部分数据,但是从服务器端发送的却是整个页面的数据,十分消耗网络资源。而我们只是需要修改页面的部分数据,也希望不刷新页面,因此异步网络请求就应运而生。

Ajax与Axios基础

Ajax(Asynchronous JavaScript and XML)

  • 定义:异步网络请求。
  • 核心作用:能够让页面无刷新的请求数据,解决传统请求中页面强制刷新、资源浪费的问题。
  • 实现方式
    • jQuery封装的ajax
    • 原生的XMLHttpRequest
    • axios

Axios(ajax i/o system)

  • 本质:并非新技术,是对原生XMLHttpRequest的封装,可用于浏览器和nodejs的HTTP客户端。
  • 核心特性
    • 基于Promise,符合最新的ES规范
    • 在浏览器中创建XMLHttpRequest请求
    • 在node.js中发送http请求
    • 支持Promise API
    • 拦截请求和响应
    • 自动转换JSON数据

服务器接口示例

浏览器访问地址http://192.168.5.136:3000/users/ajax可以拿到接口返回的数据(前提是在同一网络下)

1. JSONP接口

router.get("/jsonp", function (req, res, next) {
  // console.log(req.query.callback);
  let callback = req.query.callback;
  let data = "时鸣春涧中";
  // return res.end(`console.log(11111);`);
  res.send(`${callback}("${data}")`); // 前端获取数据时会自动JSON.parse()转义一下,所以得加双引号
});

2. GET请求接口

router.get("/get", function (req, res, next) {
  console.log(req.query);
  if (req.headers.token == 9527) {
    res.setHeader("Cache-Control", "Max-Age=6,public");
    return res.json({
      data: "时鸣春涧中",
      code: "0000",
      msg: "成功1111"
    });
  } else {
    res.statusCode = 403;
    return res.json({
      // data: "时鸣春涧中",
      code: "0001",
      msg: "失败"
    });
  }
});

3. POST请求(data传参)接口

// post data 传参
router.post("/post", function (req, res, next) {
  // console.log(req.body);
  const { name, sex, age } = req.body;
  console.log(name, sex, age);
  if (req.headers.token == 9527) {
    res.setHeader("Cache-Control", "Max-Age=6,public");
    return res.json({
      data: "时鸣春涧中",
      code: "0000",
      msg: "成功"
    });
  } else {
    return res.json({
      // data: "时鸣春涧中",
      code: "0001",
      msg: "失败"
    });
  }
});

4. POST请求(params传参)接口

// post params传参
router.post("/post1", function (req, res, next) {
  // console.log(req.body);
  console.log(req.query);
  // const { name, sex, age } = req.body;
  // console.log(name, sex, age);
  if (req.headers.token == 9527) {
    res.setHeader("Cache-Control", "Max-Age=6,public");
    return res.json({
      data: "时鸣春涧中",
      code: "0000",
      msg: "成功"
    });
  } else {
    return res.json({
      // data: "时鸣春涧中",
      code: "0001",
      msg: "失败"
    });
  }
});

原生Ajax实现

var xhr = new XMLHttpRequest();
xhr.open("GET", "a.txt", true); // open(请求方法,url,是否异步)
xhr.send(); // send(请求体)

// readyState属性:记录请求的过程
// 0: 未调用open
// 1: 调用了open但未调用send方法
// 2: 发送请求但未响应
// 3: 收到了部分响应
// 4: 响应全部接受完毕

// readyState发生变化时,会触发onreadystatechange,以此来监视请求到了哪个阶段
xhr.onreadystatechange = function() {
  // 可在此处根据readyState和status处理响应结果
};

请求头设置

不同数据类型对应的请求头配置:

1. 上传JSON格式数据

headers: {
  "Content-Type": "application/json;charset=UTF-8"
},

2. 上传图片(二进制数据)

headers: {
  "Content-Type": "multipart/form-data"
},

3. 表单提交(默认)

// HTTP会将请求参数用key1=val1&key2=val2的方式进行组织,并放到请求实体里面
// 注意如果是中文或特殊字符如"/"、","、":"等会自动进行URL转码
// 不支持文件,一般用于表单提交
headers: {
  "Content-Type": "application/x-www-form-urlencoded"
},

封装axios

import axios from "axios";
// import { Message } from "element-ui";

// 创建axios实例
const request = axios.create({
  baseURL: "http://localhost:3000", // url = base url + request url
  // baseURL: "/hexd",
  timeout: 5000 // request timeout
});

// 请求拦截器
request.interceptors.request.use(
  (config) => {
    // 可在此处添加token等公共请求头
    config.headers.token = 9527;
    console.log(config);
    return config;
  },
  (error) => {
    // console.log(error);
    return Promise.reject(error);
  }
);

// 响应拦截器
request.interceptors.response.use(
  (response) => {
    console.log(response);
    // 直接返回响应体中的data,简化前端处理
    return response.data;
  },
  (error) => {
    // 根据状态码统一处理错误信息
    switch (error.response.status) {
      case 400:
        error.message = "错误请求";
        break;
      case 401:
        error.message = "未授权,请重新登录";
        break;
      case 403:
        error.message = "拒绝访问";
        break;
      case 404:
        error.message = "请求错误,未找到该资源";
        // window.location.href = "/NotFound"
        break;
      case 405:
        error.message = "请求方法未允许";
        break;
      case 408:
        error.message = "请求超时";
        break;
      case 500:
        error.message = "服务器端出错";
        break;
      case 501:
        error.message = "网络未实现";
        break;
      case 502:
        error.message = "网络错误";
        break;
      case 503:
        error.message = "服务不可用";
        break;
      case 504:
        error.message = "网络超时";
        break;
      case 505:
        error.message = "http版本不支持该请求";
        break;
      default:
        error.message = `连接错误${error.response.status}`;
    }
    return Promise.reject(error);
  }
);

export default request;

调用接口示例

1. GET方法(params传参)

get() {
  // let name = "王维";
  request
    .get("/get", { params: { name: "王维", sex: "男", age: "未知" } })
    .then((res) => {
      // console.log(res);
      if (res.code == "0000") {
        this.msg = res.data;
        this.$message({
          message: res.msg,
          type: "success" // 原文缺失type字段,此处补充常见用法
        });
      }
    })
    .catch((err) => {
      console.log(err);
    });
},

2. POST方法(data传参)

post() {
  request
    .post("/post", { name: "王维", sex: "男", age: "未知" })
    .then((res) => {
      // console.log(res);
      if (res.code == "0000") {
        this.msg = res.data;
        this.$message({
          message: res.msg,
          type: "success" // 原文缺失type字段,此处补充常见用法
        });
      }
    })
    .catch((err) => {
      console.log(err);
    });
}

3. POST方法(params传参)

export function post1(params) {
  return request({
    method: "post",
    url: "/post1",
    params: params,
  });
}

常见axios请求方式说明

请求方式核心用途支持传参方式
GET获取数据只支持params(对应后台query)
POST提交数据(新建)、表单提交、文件上传支持data(对应后台body)和params
PUT更新数据(修改,推送全部数据)支持data和params
PATCH更新数据(修改,只推送修改数据)支持data和params
DELETE删除数据只支持params
HEAD本质与GET一致(仅返回响应头)只支持params

补充:RESTful只是一种规范,并非强制。如果后端未按规范处理PUT和PATCH,二者表现可能一致,规范的核心价值是降低沟通成本、提高开发效率。

GET与POST的区别

对比维度GET请求POST请求
URL可见性参数在URL中可见参数在body中,URL不可见
数据传输方式通过拼接URL传递通过请求体(body)传输
缓存性可缓存(适用于“查询”类请求,不修改数据)不可缓存(适用于“提交”类请求,会修改数据)
传输数据大小一般不超过2k-4k(浏览器限制)无明确限制(可通过服务器配置调整,如php.ini)
安全性较低(参数暴露在URL)相对较高(参数隐藏在body),但均需加密防抓包,本质“防君子不防小人”
数据包数量产生1个TCP数据包(header+data一并发送,服务器响应200)多数浏览器产生2个TCP数据包(先发送header,服务器响应100 continue,再发送data,服务器响应200),Firefox仅发送1个

发送OPTIONS请求的原因

1. 前提概念:CORS请求分类

浏览器将CORS(跨域资源共享)请求分为两类:

  • 简单请求(Simple Request):需同时满足以下两个条件
    1. 请求方法为HEAD、GET、POST三者之一
    2. HTTP头信息仅包含:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(且值仅为application/x-www-form-urlencoded、multipart/form-data、text/plain)
  • 非简单请求(Not-So-Simple Request):不同时满足简单请求条件的请求

2. OPTIONS请求触发场景

当请求满足“跨域(CORS)”且“非简单请求”时,浏览器会先发送预检请求(Preflight Request),预检请求的方法即为OPTIONS,用于确认服务器是否允许该跨域请求。

3. 常见触发案例

  • 前端请求需在Header中添加自定义字段(如token)
  • 请求头中Content-Type为application/json(非简单请求允许的三个值之一)

跨域相关问题

1. 为什么会产生跨域?

  • 核心原因:浏览器的同源策略限制。同源策略是浏览器对JavaScript实施的安全机制,禁止执行其他网站的脚本。
  • 同源定义:协议、域名、端口三者完全一致,只要有一个不同,即视为不同域,触发跨域限制。
    • 示例:http://127.0.0.1:3000https://127.0.0.1:3000(协议不同)、http://localhost:3000(域名不同)、http://127.0.0.1:8080(端口不同)均为不同域。
  • 跨域原理:通过特定技术避开浏览器的同源策略限制,实现跨域数据交互。

2. 解决跨域的方法

方法1:后端CORS配置
// 1. 安装cors依赖
// npm i cors

// 2. 引入并使用cors中间件
var cors = require("cors");
// ...
app.use(cors()); // 允许所有域跨域访问,也可指定特定域:app.use(cors({origin: "http://localhost:8080"})))
方法2:JSONP跨域
  • 前端实现
function getJsonpData(data) {
  // console.log(data);
  document.getElementById("content").innerHTML = data; // 假设页面有id为content的元素
}

// 点击按钮触发JSONP请求
b1.addEventListener('click', function () {
  var script = document.createElement("script");
  // 拼接callback参数,指定回调函数名
  script.src = 'http://127.0.0.1:3000/jsonp?callback=getJsonpData';
  document.body.appendChild(script);
})
  • 后端JSONP接口(同前文“服务器接口示例-1”):
router.get("/jsonp", function (req, res, next) {
  // console.log(req.query.callback);
  let callback = req.query.callback;
  let data = "时鸣春涧中";
  // return res.end(`console.log(11111);`);
  res.send(`${callback}("${data}")`);
});
  • Vue中使用JSONP
// 1. 安装依赖
// npm i vue-jsonp

// 2. 全局引入
import { VueJsonp } from "vue-jsonp";
Vue.use(VueJsonp);

// 3. 使用(示例)
this.$jsonp("http://127.0.0.1:3000/jsonp", { callback: "getJsonpData" })
  .then(data => {
    console.log(data);
  })
  .catch(err => {
    console.log(err);
  });
方法3:代理(前端本地代理)

通过在本地创建虚拟服务端,转发请求和响应,避免浏览器跨域限制:

// vue.config.js(Vue项目配置示例)
module.exports = {
  devServer: {
    port: 9527, // 当前本地项目端口号
    host: "localhost", // 当前域名
    https: false,
    open: true, // 项目启动后自动打开浏览器
    // 配置代理
    proxy: {
      // 匹配规则:请求路径中包含"/hexd"时,触发代理
      "/hexd": {
        target: "http://localhost:3000", // 代理转发的目标服务器地址
        // target: process.env.VUE_APP_SERVER_URL, // 也可通过环境变量配置
        changeOrigin: true, // 开启代理(影响后台收到的请求源地址)
        pathRewrite: {
          "^/hexd": "" // 重写路径:去掉请求路径中的"/hexd"前缀
        }
      }
    }
  },
  lintOnSave: false, // eslint-loader 是否在保存时检查
};

常见HTTP状态码说明

1. 2XX(成功状态码)

  • 200 OK:客户端请求在服务器端被正确处理
  • 204 No Content:请求成功,但响应报文无实体主体(仅返回响应头)
  • 206 Partial Content:服务器成功处理部分范围请求(如断点续传)

2. 3XX(重定向状态码)

  • 301 Moved Permanently:永久性重定向,资源已分配新URL
  • 302 Found:临时性重定向,资源临时分配新URL
  • 303 See Other:资源存在另一个URL,需用GET方法获取
  • 304 Not Modified:客户端缓存的文档未修改,直接使用缓存(无需重新下载)
  • 307 Temporary Redirect:临时重定向,含义与302一致

3. 4XX(客户端错误状态码)

  • 400 Bad Request:错误请求(如参数格式错误)
  • 401 Unauthorized:未授权(需重新登录)
  • 403 Forbidden:拒绝访问(服务器理解请求,但禁止执行)
  • 404 Not Found:请求资源不存在
  • 405 Method Not Allowed:请求方法不被允许(如服务器仅支持GET,客户端用POST)
  • 408 Request Timeout:请求超时

4. 5XX(服务器错误状态码)

  • 500 Internal Server Error:服务器端出错(如代码异常)
  • 501 Not Implemented:服务器不支持该请求方法
  • 502 Bad Gateway:网关错误(服务器作为网关,从上游服务器收到无效响应)
  • 503 Service Unavailable:服务不可用(如服务器维护、过载)
  • 504 Gateway Timeout:网关超时(服务器作为网关,上游服务器未及时响应)
  • 505 HTTP Version Not Supported:服务器不支持请求使用的HTTP版本