最近几个项目都用到了 Axios,它是一个更现代的 API 请求库,基于 Promise,能运行在浏览器和 Node.js 里。在项目里,一般都是需要进行一次封装再来使用,比如处理鉴权、还有全局的请求 Loading 动画等。今天来总结一下。
Axios 简介
Axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端。它有以下功能:
- 从浏览器中创建 XMLHttpRequest
- 从 Node.js 发出 HTTP 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 自动转换 JSON 数据
- 客户端防止 CSRF/XSRF
基础 API
axios.request(config)
axios.get(url [,config])
axios.delete(url [,config])
axios.head(url [,config])
axios.options(url [,config])
axios.post(url [,data [,config]])
axios.put(url [,data [,config]])
axios.patch(url [,data [,config]])
用法示例
GET
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| axios .get('/user?ID=12345') .then(function(res) { console.log(res); }) .catch(function(error) { console.log(error); });
axios .get('/user', { params: { ID: 12345 } }) .then(function(response) { console.log(response); }) .catch(function(error) { console.log(error); });
|
POST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| axios .post( '/user', { userId: '123' }, { headers: { token: 'abc' } } ) .then(function(res) { console.log(res); }) .catch(function(error) { console.log(error); });
|
直接使用 config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| axios({ url: 'pakage.json', method: 'get', params: { userId: '123' }, headers: { token: 'http-test' } }).then(res => { console.log(res.data); });
axios({ url: 'pakage.json', method: 'post', data: { userId: '123' }, headers: { token: 'http-test' } }).then(res => { console.log(res.data); });
|
并发请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function getUserAcount() { return axios.get('/user/1234'); } function getUserPermissions() { return axios.get('/user/1234/getUserPermissions'); }
axios.all([getUserAccount(), getUserPerssions()]).then( axios.spread((acct, perms) => { }) );
|
全局封装、异常处理
在项目中,如果每次请求都写一堆 config
会存在大量重复代码,一般我们会封装成一个方法,把一些必要的参数配置好,同理,全局的异常还有鉴权等都统一配置。单次请求数据只做和数据相关的业务逻辑。下面来看一下 Axios 的全局封装例子。
封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import axios from 'axios'; import qs from 'qs';
export const postRequest = (url, params) => { return axios({ method: 'post', url: url, data: params, transformRequest: [ function(data) { return qs.stringify(data); } ], headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); };
export const uploadFileRequest = (url, params) => { return axios({ method: 'post', url: url, data: params, headers: { 'Content-Type': 'multipart/form-data' } }); };
export const getRequest = url => { return axios({ method: 'get', url: url }); };
export const putRequest = (url, params) => { return axios({ method: 'put', url: url, data: params, transformRequest: [ function(data) { return qs.stringify(data); } ], headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); };
export const deleteRequest = url => { return axios({ method: 'delete', url: url }); };
|
异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| import axios from 'axios'; import { Message } from 'element-ui';
axios.interceptors.request.use( config => { return config; }, err => { Message.error({ message: '请求超时!' }); return Promise.resolve(err); } );
axios.interceptors.response.use( data => {
switch (data.code) { case '0': return data; case '-1': break; default: }
if (data.status && data.status == 200 && data.data.status == 'error') { Message.error({ message: data.data.msg }); return; } return data; }, err => { if (err && err.response) { switch (err.response.status) { case 400: err.message = '请求错误 (400)'; break; case 401: err.message = '未授权,请重新登录 (401)'; break; case 403: err.message = '拒绝访问 (403)'; break; case 404: err.message = '请求出错 (404)'; break; case 408: err.message = '请求超时 (408)'; break; case 500: err.message = '服务器错误 (500)'; break; case 501: err.message = '服务未实现 (501)'; break; case 502: err.message = '网络错误 (502)'; break; case 503: err.message = '服务不可用 (503)'; break; case 504: err.message = '网络超时 (504)'; break; case 505: err.message = 'HTTP 版本不受支持 (505)'; break; default: err.message = `连接出错 (${err.response.status})!`; } } else { err.message = '连接服务器失败!'; } Message.err({ message: err.message }); return Promise.resolve(err); } );
|
请求出错的时候执行的是:Promise.resolve(err)
,而不是 Promise.reject(err)
,这样无论请求成功还是失败,在成功的回调中都能收到通知。
其它配置
baseURL
通过 axios.defaults.baseURL
来设置 API 的根域名。
1 2 3 4 5 6 7
| if (process.env.NODE_ENV == 'development') { axios.defaults.baseURL = 'https://dev.server.com/'; } else if (process.env.NODE_ENV == 'debug') { axios.defaults.baseURL = 'https://debug.server.com/'; } else if (process.env.NODE_ENV == 'production') { axios.defaults.baseURL = 'https://pro.server.com/'; }
|
process.env
是 Node.js 提供的全局变量,我们可以在 npm scripts 里通过 cross-env 这个工具统一配置不同的环境,cross-env 做了各种系统平台的兼容处理。一般用这个设置环境变量。
请求超时
通过 axios.defaults.timeout
设置默认的请求超时时间。例如超过了 10s,就会告知用户当前请求超时,请刷新等。
1
| axios.defaults.timeout = 10000;
|
单独设置 POST 的请求头
比如设置 POST 提交(原生 form)时单独配置。
1 2
| axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
|
附: 完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| import axios from 'axios'; import QS from 'qs'; import { Toast } from 'vant';
if (process.env.NODE_ENV == 'development') { axios.defaults.baseURL = 'https://dev.server.com/'; } else if (process.env.NODE_ENV == 'debug') { axios.defaults.baseURL = 'https://debug.server.com/'; } else if (process.env.NODE_ENV == 'production') { axios.defaults.baseURL = 'https://pro.server.com/'; }
axios.defaults.timeout = 10000;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
axios.interceptors.request.use( config = > { const token = store.state.token; token && (config.headers.Authorization = token); return config; }, error = > { return Promise.error(error); } )
axios.interceptors.response.use( response = > { if (response.status === 200) { return Promise.resolve(response); } else { return Promise.reject(response); } }, error = > { if (error.response.status) { switch (error.response.status) { case 401: router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); break; case 403: Toast({ message: '登录过期,请重新登录', duration: 1000, forbidClick: true }); break; case 404: Toast({ message: '网络请求不存在', duration: 1500, forbidClick: true }); break; default: Toast({ message: error.response.data.message, duration: 1500, forbidClick: true }); } return Promise.reject(error.response); } } );
export function get(url, params) { return new Promise((resolve, reject) => { axios .get(url, { params: params }) .then(res => { resolve(res.data); }) .catch (err => { reject(err.data) }) }); }
export function post(url, params) { return new Promise((resolve, reject) => { axios .post(url, QS.stringify(params)) .then(res = > { resolve(res.data); }) .catch (err => { reject(err.data) }) }); }
|
附: Content-Type
说到和服务端数据交互,总是绕不开这个,必须要扫盲一下了!😂😂
Content-Type 用于指定内容类型,一般是指网页中存在的 Content-Type,Content-Type 属性指定请求和响应的 HTTP 内容类型。如果未指定 ContentType,默认为 text/html
。
常见类型有:
- text/html
- application/x-www-form-urlencoded
- multipart/form-data
- application/json
- application/xml
application/x-www-form-urlencoded
、multipart/form-data
、application/json
、application/xml
这四个则是 ajax 请求需要指定的类型,表单提交或上传文件常用的资源类型。
这是表单默认提交方式,格式为 URL 编码 key=value&key1=value1
。
注:Chrome 浏览器会自动格式化成易读的格式
使用表单上传文件时,必须指定表单的 enctype 属性值为 multipart/form-data
。
请求体被分割成多部分,每部分使用 --boundary
分割,使用 --boundary--\r\n
结束。
表单上传文件 Demo:
1 2 3 4 5 6
| <form action="/upload" enctype="multipart/form-data" method="post"> 用户名: <input type="text" name="username"> 密码: <input type="password" name="password"> 上传文件: <input type="file" name="file"> <input type="submit" value="提交"> </form>
|
application/json
Axios 默认 POST
提交方式就是 application/json
,所以,在使用 axios 提交表单时需要注意后端能不能解析,不能解析需要设置 POST
常用格式 application/x-www-form-urlencoded
,且提交的数据需要使用 qs 模块序列化格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| axios.post( 'https://jsonplaceholder.typicode.com/posts', { userId: 1, name: 'ifyour', arr: [1, 2, 3, 4] }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, transformRequest: [ function(data) { return qs.stringify(data); } ] } );
|
application/xml
1 2
| POST http://www.example.com HTTP/1.1 Content-Type: text/xml
|
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0"?> <resource> <id>123</id> <params> <name> <value>example</value> </name> <age> <value>21</value> </age> </params> </resource>
|
参考