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):需同时满足以下两个条件
- 请求方法为HEAD、GET、POST三者之一
- 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:3000与https://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版本