axios ⽹络请求
1.axios 简单介绍
⼀、本节⼤纲
- 前端 ajax 框架选型
- axios 的简介及优势
⼆、前端 ajax 框架选
在 ajax 最开始出现的时候,使⽤原⽣的 XMLHTTPRequest 进⾏ ajax 请求的操作,这种⽅式⽐较麻烦,浏览器兼容性各种问题都需要⾃⼰去处理,所以我们暂时不去讨论原⽣的 XMLHTTPRequest 的这种⽅ 法。
- jquery 的出现对前端开发产⽣了⽐较深远的影响。jquery 是⼀个相对完整的基于 DOM 的前端 js 框架,ajax ⽹络请求只是其中的⼀个模块,并且⽅便易⽤。但是随着新⼀代的前端框架 vue、 ReactJs、AngularJS 的兴起,Jquery 的路也越来越窄,jquery ajax 的替代品也越来越多,其中不乏⼀些设计精良、使⽤⽅便的 ajax ⽹络处理类库。如:本节为⼤家介绍的 axios。
- 在 vue1.x 的版本中,曾经有⼀个⽹络请求处理的模块 vue-resource,短⼩精悍,⽐ Jquery 要⼩很多。 但是从 vue2.0 开始,vue 作者尤⾬溪就宣布不再对 vue-resource 进⾏维护了,所以不建议⼤家继续使⽤。
- 在⼤神尤⾬溪宣布不再对 vue-resource 进⾏维护的同时,向⼴⼤的 Vue 开发者推荐了⼀个⾮常优秀的 ajax ⽹络请求处理库 axios。
- axios 本身并不属于 vue 范畴,但是属于前端⽣态圈特别重要的 HTTP ⼯具类库,由于 Vue 作者的推荐,在 Vue 项⽬中应⽤尤其⼴泛。
三、axios 的简介及优势
基于 Promise 的,⽤在浏览器和 nodeJS 环境下的 HTTP 客户端。( Promise based HTTP client for the browser and node.js )
- ⽀持从浏览器中创建XMLHttpRequests
- ⽀持从 node.js 创建 http 请求(这个是其他框架不具备的)
- ⽀持 PromiseAPI(最重要特点)
- ⽀持拦截器,拦截请求和响应(核⼼功能)
- ⽀持转换请求数据和响应数据(核⼼功能)
- ⾃动转换 JSON 数据(核⼼功能)
- 客户端⽀持防御 XSRF 攻击
⽀持多种请求⽅法
- request(config)
- get(url[, config])
- delete(url[, config])
- head(url[, config])
- post(url[, data[, config]])
- put(url[, data[, config]])
- patch(url[, data[, config]])
浏览器兼容性
2.axios 的基本⽤法
⼀、本节⼤纲
- 安装使⽤ axios
- axios 发送 GET 请求
- axios 发送 POST 请求
- axios 发送并⾏请求
⼆、安装使⽤ axios
代码⽂件引⽤⽅式
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
npm 安装⽅式
npm install axios
yarn 安装⽅式
yarn add axios
在 Vue 项⽬中引⼊ axios 模块
import axios from "axios";
三、axios 发送 GET 请求
在使⽤ axios 发送 HTTP 请求之前,介绍⼀个⽹站:http://httpbin.org
这个⽹站提供了免费的 HTTP 请求及相应服务,我们可以利⽤这个服务测试我们的 axios。
//使⽤axios发送get请求
axios({
method: "get", //http请求⽅法
url: "http://httpbin.org/get?param=hello", //http服务地址
})
.then(function (response) {
//请求成功响应结果response
console.log(response);
})
.catch(function (error) {
//请求异常响应结果
console.log(error);
});
//HTTP GET⽅法请求可以简写如下:
axios
.get("http://httpbin.org/get?param=hello")
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 使⽤ axios(config) 发送 http 请求,config 为该请求的配置信息对象
- axios.get 等同于 axios 使⽤ method:get
- axios 是基于 Promise 的 HTTP 客户端,所以可以使⽤ then、catch 对请求结果进⾏处理
- 如果不习惯在 URL 参数上直接传递参数,可以使⽤ axios 的 config :params 传递参数。如下所示:
// 上⾯的GET请求也可以通过下⾯的⽅式传参
axios
.get("http://httpbin.org/get", {
params: {
param: "hello",
},
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
四、axios 发送 POST 请求
axios 发送 HTTP post 请求的基本⽅法如下:
axios({
method: "post", //请求⽅法
url: "http://httpbin.org/post", //请求服务地址
data: {
firstName: "zhang",
lastName: "san",
},
});
//省略then、catch等请求结果处理代码,参考GET⽅法即可。
- post 请求使⽤ data 传递参数,特别注意和 GET ⽅法不⼀样。
- HTTP POST 请求的简写⽅法如下:
axios.post("http://httpbin.org/post", {
firstName: "zhang",
lastName: "san",
});
//省略then、catch
五、axios 发送并⾏请求
在有些情况下,我们希望某些异步⽹络请求结果处理有⼀定的顺序。⽐如,有 getHello() 和 getWorld() 两个异步操作,我们希望将两个 HTTP 的请求都完成之后,结果合并处理或者进⾏下⼀步操作。为此 axios 为我们提供了 axios.all ⽅法执⾏并发的异步⽹络请求操作,将响应结果统⼀处理。
function getHello() {
return axios.get("http://httpbin.org/get?param=hello");
}
function getWorld() {
return axios.get("http://httpbin.org/get?param=world");
}
axios.all([getHello(), getWorld()]).then(
axios.spread(function (res1, res2) {
// 两个请求都执⾏完成后,进⼊该函数
console.log(res1.data.args.param + " " + res2.data.args.param);
})
);
浏览器⽇志的打印结果如下:
hello world
上⾯的 axios.all 函数还可以写成如下的形式,返回值是⼀个数组。然后通过数组下标获取响应结果数据。results[0] 代表第⼀个 getHello() 函数的请求结果,results[1] 代表第⼆个 getWorld() 函数的请求结 果。
axios.all([getHello(), getWorld()]).then(function (results) {
console.log(results[0].data.args.param + " " + results[1].data.args.param);
});
所以 axios.spread 函数实际的作⽤就是将返回值数组,展开为多个返回值,解构赋值。
3.axios 的配置详解
⼀、本节⼤纲
- 常⽤数据格式
- 数据的转换
- ⽂件上传与下载进度
- axios 请求配置
- axios 响应数据结构
- axios 全局配置
⼆、常⽤数据格式
在实际的开发过程中,服务端接收数据的⽅式可能是多种数据格式的,⽐如:JSON、QueryString 表单数据等形式。
2.1.JSON 数据格式
当 data 参数是 JSON 对象的时候,默认的 Content-Type 是 application/json 。所以下⾯代码中的 headers 的 content-type 设置可以省略。
axios
.post(
"http://httpbin.org/post",
{
firstName: "zhang",
lastName: "san",
},
{
headers: {
"Content-Type": "application/json",
},
}
)
.then(function (response) {
console.log(response);
});
axios 的 post 请求⽅法三个参数分别是:url 请求地址,data 数据,config 配置。
2.2.表单数据格式
如果服务端不接收 JSON 对象数据,⽽是接收表单数据,那么我们该如何调整上⾯的代码?
⾸先我们需要将 JSON 对象转换为 QueryString 字符串,使⽤ qs.stringify() 函数
- 命令⾏输⼊:
npm install qs
- 安装完成后在需要⽤到的代码中:
import qs from 'qs'
- qs.parse() 是将 URL 解析成对象的形式, qs.stringify() 是将对象序列化成 URL 的形式,以 & 进⾏拼接
- 命令⾏输⼊:
当 data 参数是字符串的时候,默认的 Content-Type 是 application/x-www-form-urlencoded 。所以下⾯代码中的 headers 的 content-type 设置可以省略
axios
.post(
"http://httpbin.org/post",
qs.stringify({
firstName: "zhang",
lastName: "san",
}),
{
headers: {
"content-type": "application/x-www-form-urlencoded",
},
}
)
.then(function (response) {
console.log(response);
});
控制台可以看到,显示数据是以表单 form 的⽅式提交的。
三、数据的转换
除了常⽤的表单数据格式、JSON 数据格式,实际开发中还有很多的数据格式、内容需要进⾏处理。 ⽐如我们希望对发送出去的数据做特殊的⾃定义处理,axios 也为我们提供了⽅法。
- transfromRequest ⽤于在向服务端发送请求之前,将数据做处理修改请求数据。该⽅法只对 PUT、 POST、PATCH 请求⽣效。⽽且,此⽅法最后必须返回⼀个 string、ArrayBuffer 或者 Stream。
- transformResponse 对响应结果数据,进⾏转换处理。收到响应之后,then/catch 之前做数据转换处理⼯作。
{
transformRequest: [function (data) {
// 在这⾥对 data 进⾏任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 在这⾥对 data 进⾏任意转换处理
return data;
}]
}
四、⽂件上传与下载进度
使⽤如下⽅法可以将⽂件下载,此处并未对下载⽂件做额外处理,主要介绍⼀下配置函数。 progress.loaded 表示已经加载的进度,progress.total 表示需要加载的总量。
axios({
url: "https://niit-soft.oss-cn-hangzhou.aliyuncs.com/mp4/1.mp4",
method: "get",
onDownloadProgress(progress) {
console.log(Math.round((progress.loaded / progress.total) * 100) + "%");
},
});
axios 不仅可以获取下载的进度,还可以获取上传的进度。
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// 对原⽣进度事件的处理
},
五、axios 请求配置
我们已经学习了最基本的请求⽅法 GET、POST 的使⽤,PUT、DELETE 请求的⽅法和 POST 请求使⽤⽅法是基本⼀致的。如果没有指定 method,请求将默认使⽤ get ⽅法。此外,url 是必需的请求参数。 下⾯是其他的请求参数:
{
// `url` 是⽤于请求的服务器 URL(必填)
url: '/user',
// `method` 是HTTP请求时使⽤的⽅法, 默认是 get
method: 'get',
// `baseURL` 将⾃动加在 `url` 前⾯,除⾮ `url` 是⼀个绝对 URL。
// 它可以通过设置⼀个 `baseURL` 便于为 axios 实例的⽅法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `headers` 是即将被发送的⾃定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求⼀起发送的 URL 参数
// 必须是⼀个⽆格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是⼀个负责 `params` 序列化的函数。
//针对params的参数序列化通常axios⾃动完成,所以并不常⽤
paramsSerializer: function(params) {
return qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求body被发送的数据,只适⽤于这些请求⽅法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,data数据必须是以下类型之⼀:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示⽆超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使⽤凭证
withCredentials: false, // 默认的
// `adapter` 允许⾃定义处理请求,使⽤mock⽅式⽅便测试
// 返回⼀个 promise 并应⽤⼀个有效的响应
adapter: function (config) {
/* ... */
},
// `auth` 表示应该使⽤ HTTP 基础验证,并提供凭据
// 这将设置⼀个 `Authorization` 头,可以对应Http Basic基础认证模式。
//如果是java开发⼈员,可以简单的学⼀下Spring Security的Http Basic基础认证模式,就会理解。
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认的
//以下两个配置与跨站攻击伪造相关的防御,须与服务端名称保持⼀致。
// `xsrfCookieName` 是⽤作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
xsrfHeaderName: 'X-XSRF-TOKEN', // 默认的
// `maxContentLength` 定义允许的响应内容的最⼤尺⼨
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认的
},
// `maxRedirects` 定义在 node.js 中 follow 的最⼤重定向数⽬
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // 默认的
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中⽤于定义在执⾏ http 和 https 时使⽤的⾃定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启⽤
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' 定义代理服务器的主机名称和端⼝
// `auth` 表示 HTTP 基础验证应当⽤于连接代理,并提供凭据
// 这将会设置⼀个 `Proxy-Authorization` 头,覆写掉已有的通过使⽤ `header` 设置的⾃定义 `Proxy-Authorization` 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: : {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` 指定⽤于取消请求的 cancel token
cancelToken: new CancelToken(function (cancel) {
})
}
六、axios 响应数据结构
axios 请求的响应数据结构包含以下信息
{
// `data` 由服务器提供的响应
data: {},
// `status` 来⾃服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来⾃服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {}
}
使⽤ then 时,将接收下⾯这样的响应:
axios.get("/user/12345").then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
五、axios 全局配置
之前所有的配置都是针对单次请求或者某个 api 请求的配置,在实际的开发中请求配置都是⼤部分⼀致, ⼩部分个性化的。针对⼤部分⼀致的内容,我们可以采⽤全局配置。
⽐如:
- 请求服务端根地址 baseURL ,通常是很多 api 使⽤⼀个跟地址提供服务。
- Authorization 的⽤户名密码信息等,不需要每次请求都进⾏设置。
- timeout 请求超时时间,也不需要每次请求都单独设置等等。
下⾯的代码就是进⾏全局设置的⽅式。
axios.defaults.baseURL = "https://api.example.com";
axios.defaults.headers.common["Authorization"] = AUTH_TOKEN;
配置的优先级:
当 defaults 全局配置与局部 config 配置发⽣冲突的时候,局部 config 配置的优先级要⾼于全局配置。
4.axios 的实例与拦截器
⼀、本节⼤纲
- axios 多实例的必要性
- 创建 axios 实例
- axios 拦截器
⼆、axios 多实例的必要性
之前所有的配置都是针对默认实例的。但是在现实的开发中,我们通常不会去修改默认实例的配置,因为⾯向的后端服务可能是很多个,每⼀个后端服务 HTTP 请求的地址,请求超时的时间限制等都不⼀样,API 服务的鉴权的⽅式也有可能有所区别。
如上图中所示,我们通常根据请求不同的服务主体,创建多个 axios 实例。⽐如:
- 为“系统管理”类的 API 服务创建⼀个 axios 实例
- 为“业务数据”操作类 API 服务创建⼀个 axios 实例
这样做实际上是⼀种⼯程化的思想,将医⽣组织在⼀起就可以形成医院。但是我们不会把所有的医⽣放在⼀个医院⾥⾯,这样不利于应⽤,也不利于管理。我们会把医院分类,这样有利于对实例精细化管理和建设改造。
三、 创建 axios 实例
// 创建实例时设置配置的默认值
var instance = axios.create({
baseURL: "http://httpbin.org",
});
// 在实例已创建后修改默认值
instance.defaults.headers.common["Authorization"] = AUTH_TOKEN;
四、axios 拦截
axios 的拦截器的作⽤就是针对所有请求和响应做拦截,所谓的拦截就是统⼀修改请求或响应的内容、参数、配置等信息。⽐如:
- 在结合 JWT 令牌进⾏开发的时候,我们需要为每⼀个请求加上 JWT 令牌,这样服务端才能正确的授权访问。
- 在 http 请求没有成功连接到服务端的时候我们通常做⼀个统⼀的异常处理。等等
如下是⼀个典型的拦截器逻辑的实现。
// request拦截器
instance.interceptors.request.use(
(config) => {
// 根据条件加⼊token-安全携带
if (true) {
// 需⾃定义
// 让每个请求携带token
config.headers["JWT-Token"] = "";
}
return config;
},
(error) => {
// 请求错误处理
return Promise.reject(error);
}
);
// respone拦截器
instance.interceptors.response.use(
(response) => {
// 统⼀处理状态
const res = response.data;
if (res.statuscode != 1) {
// 需⾃定义
// 返回异常
return Promise.reject({
status: res.statuscode,
message: res.message,
});
} else {
return response.data;
}
},
// 处理处理
(error) => {
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = "错误请求";
break;
case 401:
error.message = "未授权,请重新登录";
break;
case 403:
error.message = "拒绝访问";
break;
case 404:
error.message = "请求错误,未找到该资源";
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}`;
}
} else {
error.message = "连接到服务器失败";
}
return Promise.reject(error);
}
);