与服务端通信——axios
在实际项目中,页面所需要的数据通常是从服务端获取的,这必然牵涉与服务端的通信,Vue官方推荐使用axios完成Ajax请求。axios是一个基于Promise的HTTP库,可以用在浏览器和Node.js中。
安装
可以使用CDN方式安装:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
如果采用模块化开发,则使用npm安装方式:
npm install axios --save
在vue的脚手架项目中使用,可以将axios结合vue-axios插件一起使用,该插件只是将axios集成到Vue.js的轻度封装,本身不能独立使用。可以使用以下命令安装:
npm install axios vue-axios
安装vue-axios插件后,使用形式如下:
import {createApp} from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
const app = createApp(App);
app.use(VueAxios,axios) //安装插件
app.mount('#app')
之后在组件内就可以通过this.axios调用axios的方法发送请求。
基本用法
HTTP最基本的请求就是get和post。使用axios发送get请求调用形式如下:
axios.get('/book?id=1')
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
get方法接收一个URL作为参数,如果有要发送的数据,则以查询字符串的形式附加在URL后面。当服务端发回成功响应(状态码是2XX)时调用then()方法中的回调,可以在该回调函数中对服务端的响应进行处理;如果出现错误则会调用catch中的回调,可以在该回调函数中对错误信息进行处理,并向用户提示错误。
如果不喜欢URL后附加查询参数的写法,可以为get方法传递一个配置对象作为参数,在配置对象中使用params字段指定要发送的数据:
axios.get('/book',{
params:{
id:1
}
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
可以使用ES2017的async/await执行异步请求:
async function getBook(){
try {
const response = await axios.get('/book?id=1');
console.log(response);
} catch(error){
console.error(error);
}
}
post请求是在请求体中发送数据,因此,axios的post方法比get方法多一个参数,该参数是一个对象,对象的属性就是要发送的数据:
axios.post('/login',{
username:'lisi',
password:'1234'
})
.then(function(response){
console.log(response);
})
.catch(function(error) {
console.log(error);
});
get()和post()方法的原型如下:
get(url[,config])
post(url[,data[,config]])
接收到服务端的响应信息后,需要对响应信息进行处理。例如,设置用于组件渲染或更新所需要的数据。回调函数中的response是一个对象,该对象常用的属性是data和status,前者用于获取服务端发回的响应数据,后者是服务端发送的HTTP状态码。
response对象的完整属性:
{
//data是服务器发回的响应数据
data:{},
//服务器响应的状态码
status:200,
//状态描述
statusText:'OK',
//headers是服务器响应的消息报头,所有报头的名字都是小写的
//可以通过response.headers['content-type']
headers:{},
//config是为请求提供的配置信息
config:{},
//request是生成此响应的请求
request:{}
}
成功响应后,获取数据的一般处理形式如下:
axios.get('/book?id=1')
.then(function(response){
if(response.status === 200) {
this.book = response.data;
}
})
.catch(function(error) {
console.log(error);
});
如果出现错误则会调用catch方法中的回调,并向该回调函数传递一个错误对象。错误对象的一般形式如下:
axios.get('/book?id=1')
.catch(function(error) {
if (error.response) {
//请求已发送并接收到服务端响应,但响应的状态码不是2XX
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
//请求已发送,但未接收到响应
console.log(error.request);
} else {
//在设置请求时出现问题而引发错误
console.log('Error',error.message);
}
console.log(error.config);
});
axios API
可以通过向axios传递相关配置来创建请求。axios原型如下:
axios(config)
axios(url[,config])
get请求和post请求的调用形式如下:
//发送get请求(默认的方法)
axios('/book?id=1');
//get请求,获取远端的图片
axios({
method:'get',
url:'/images/logo.png',
responseType:'stream'
})
.then(function(response) {
response.data.pipe(fs.createWriteStream('logo.png'))
});
//发送post请求
axios({
method:'post',
url:'/login',
data:{
username:'lisi',
password:'1234'
}
});
为了方便使用,axios库为所有支持的请求方法提供了别名:
- 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]])
在使用别名方法时,url、method、data这些属性都不必在配置对象中指定。
请求配置
axios库为请求提供了配置对象,在该对象中可以设置很多选项,常用的是url、method、headers和params:
{
url:'/book',
method:'get',
//baseURL将自动加在url前面,除非url是一个绝对URL
//为axios实例设置一个baseURL就可以将相对URL传递给该实例的方法
baseURL:'https://some-domain.com/api/',
//transformRequest允许在将请求数据发送到服务器前对其修改
//只能用于put、post、patch、delete这几个请求方法
//数组中的最后一个函数必须返回一个字符串、Buffer的实例、ArrayBuffer、FormData或Stream
//也可以修改headers对象
transformRequest:[function(data,headers) {
//对data进行任意转换处理
return data;
}],
//transformResponse允许在将响应数据传递给then/catch之前对其修改
transformResponse:[function(data) {
//对data进行任意转换处理
return data;
}],
//是要发送的自定义请求头
headers:{'X-Requested-With':'XMLHttpRequest'},
params:{
ID:1
},
//是一个负责params序列化的可选函数
paramsSerializer:function(params) {
return Qs.stringify(params,{arrayFormat:'brackets'})
}
data:{
firstName:'Fred'
},
//只发送值,不发送键
data:'Country=Brasil&City=Belo',
timeout:1000,//请求超过timeout则终止
withCredentials:false, //默认值,表示跨域请求时是否需要凭证
//adapter允许自定义处理请求,以使测试更加容易
//返回一个promise并提供一个有效的响应
adapter:function(config) {
...
},
//auth表示应该使用HTTP基础验证,并提供凭据
//这将设置一个Authorization报头,覆盖使用headers设置的现有的Authorization自定义报头
auth:{
username:'zzd',
password:'1234'
}
responseType:'json', //默认的,响应的数据类型
responseEncoding:'utf8' //解码响应数据的编码
...
}
并发请求
有时需要同时向服务端发起多个请求,这可以用Promise.all实现,例如:
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(),getUserPermissions()])
.than(function(results) {
//两个请求现在都执行完成
const acct = result[0]; //响应结果1
const perm = result[1]; //响应结果2
})
创建实例
可以使用自定义配置调用axios.create([config])方法创建一个axios实例,之后使用该实例向服务端发起请求,就不用每次请求时重复设置配置选项了:
const instance = axios.create({
baseURL:'https://some-domain.com/api/',
timeout:1000,
headers:{
'X-Custom-Header':'foobar'
}
})
配置默认值
对于每次请求相同的配置选项,可以通过为配置选项设置默认值来简化代码的编写。项目中用的全局axios默认值可以在项目的入口文件main.js中按照以下形式设置:
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.withCredentials = true
也可以在自定义实例中设置配置默认值,这些配置选项只有在使用该实例发起请求时才生效:
//创建实例时设置配置默认值
const instance = axios.create({
baseURL:'http://api.example.com'
});
//实例创建后更改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
配置将按优先顺序进行合并。顺序是在lib/defaults.js中找到的库的默认值,然后是实例的defaults属性,最后是请求的config参数。后者将优先于前者。
var instance = axios.create();
instance.default.timeout = 2500;
instance.get('/longRequest'.{
timeout:5000
})
拦截器
有时需要统一处理HTTP的请求和响应,如登录验证,这时就可以使用axios的拦截器,分为请求拦截器和响应拦截器,他们会在请求或响应被then()或catch()方法处理前拦截它们。axios的拦截器的使用形式如下:
//添加请求拦截器
axios.interceptors.request.use(function(config) {
//在请求发送前做些什么
return config;
}, function (error) {
//对请求错误做些什么
return Promise.reject(error);
});
//添加响应拦截器
axios.interceptors.response.use(function(response) {
//在响应数据做些什么
return response;
}, function (error) {
//对请求错误做些什么
return Promise.reject(error);
});
前面章节使用全局守卫实现了一个用户登录验证的例子,不过这种方式只是简单的前端路由控制,用户一旦登录成功,前端就保存了用户登录的状态,允许用户访问受保护的资源。如果在这期间,该用户在服务端失效了,比如用户长时间未操作、服务端强制下线、或者管理员将该用户拉入黑名单,那么前端就应该及时更新用户的状态,对用户的后续访问做出控制。在这种情况下,就应该使用axios的拦截器结合HTTP状态码进行用户是否已登录的判断:
//请求拦截器
axios.interceptors.request.use(
config => {
if (token) {
//判断是否存在token,如果存在,则每个HTTP header都加上token
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
});
//响应拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
//如果返回401,则清除token信息并跳转到登录页面
router.replace({
path:'login',
query:{redirect:router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data)
});
如果之后想移除拦截器,则可以:
const myInterceptor = axios.interceptors.request.use(function(){...});
axios.interceptors.request.reject(myInterceptor);
也可以为自定义的axios实例添加拦截器:
const instance = axios.create();
instance.interceptors.request.use(function(){});