与服务端通信——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.eject(myInterceptor);
也可以为自定义的axios实例添加拦截器:
const instance = axios.create(); instance.interceptors.request.use(function(){});