欢迎光临
我们一直在努力

vue 单页面应用 Axios的二次封装

文章目录

Axios的封装

在vue项目中,获取数据时,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。

安装

npm install axios

引入

通常我会在项目的src目录中,新建一个request文件夹,然后在里面新建一个http.js和一个api.js文件。http.js文件用来封装我们的axios,api.js用来统一管理我们的接口。

import axios from 'axios';
import QS from 'qs'; 
import { Toast } from 'vant';

环境

我们的项目会有开发环境、测试环境、生产环境,不同环境对应的后端的主机IP/域名可能是不一样的,因此可以在这里做一个环境判断。

if (process.env.NODE_ENV == 'development') { 
 axios.defaults.baseURL = 'https://www.baidu.com';} 
else if (process.env.NODE_ENV == 'debug') { 
 axios.defaults.baseURL = 'https://www.ceshi.com';
} 
else if (process.env.NODE_ENV == 'production') { 
 axios.defaults.baseURL = 'https://www.production.com';
}

请求超时

如果长时间请求不到数据,应当向用户返回错误,提示用户刷新等行为。

axios.defaults.timeout = 10000;

POST 请求头

Post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置。

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

请求拦截

我们在发送请求前可以进行一个请求的拦截,为什么要拦截呢,我们拦截请求是用来做什么的呢?比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。

// 先导入vuex,因为我们要使用到里面的状态对象
// vuex的路径根据自己的路径去写
import store from '@/store/index';

// 请求拦截器
axios.interceptors.request.use( 
 config => { 
 // 每次发送请求之前判断vuex中是否存在token 
 // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 
 const token = store.state.token; 
 token && (config.headers.Authorization = token); 
 return config; 
 }, 
 error => { 
 return Promise.error(error); 
})

补充:一般用户登陆完成后,都会将用户的token通过localStorage或者cookie存在本地。当用户进入页面时,会从本地读取token,如果token存在,则证明用户已经登陆了,可以去VUEX中更新用户的状态。当请求接口的时候,都会在请求的header中携带token,后端会通过token来判断登陆是否过期。

响应拦截

响应拦截器很好理解,就是服务器返回给我们的数据,我们在拿到之前可以对他进行一些处理。例如上面的思想:如果后台返回的状态码是200,则正常返回数据,否则的根据错误的状态码类型进行一些我们需要的错误,其实这里主要就是进行了错误的统一处理和没登录或登录过期后调整登录页的一个操作。

// 响应拦截器
axios.interceptors.response.use( 
 response => { 
 // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据 
 // 否则的话抛出错误
 if (response.status === 200) {  
  return Promise.resolve(response); 
 } else {  
  return Promise.reject(response); 
 } 
 }, 
 // 服务器状态码不是2开头的的情况
 // 这里可以跟你们的后台开发人员协商好统一的错误状态码 
 // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
 // 下面列举几个常见的操作,其他需求可自行扩展
 error => {  
 if (error.response.status) {  
  switch (error.response.status) {  
  // 401: 未登录
  // 未登录则跳转登录页面,并携带当前页面的路径
  // 在登录成功后返回当前页面,这一步需要在登录页操作。  
  case 401:   
   router.replace({   
   path: '/login',   
   query: { 
    redirect: router.currentRoute.fullPath 
   }
   });
   break;

  // 403 token过期
  // 登录过期对用户进行提示
  // 清除本地token和清空vuex中token对象
  // 跳转登录页面  
  case 403:
   Toast({
   message: '登录过期,请重新登录',
   duration: 1000,
   forbidClick: true
   });
   // 清除token
   localStorage.removeItem('token');
   store.commit('loginSuccess', null);
   // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 
   setTimeout(() => {   
   router.replace({    
    path: '/login',    
    query: { 
    redirect: router.currentRoute.fullPath 
    }   
   });   
   }, 1000);   
   break;

  // 404请求不存在
  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);
 }
 } 
});

Get与Post封装

Get 封装

我们通过定义一个get函数,get函数有两个参数,第一个参数表示我们要请求的url地址,第二个参数是我们要携带的请求参数。get函数返回一个promise对象,当axios其请求成功时resolve服务器返回 值,请求失败时reject错误值。最后通过export抛出get函数。

/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
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) 
 }) 
});}

Post 封装

原理同get基本一样,但是要注意的是,post方法必须要使用对提交从参数对象进行序列化的操作,所以这里我们通过node的qs模块来序列化我们的参数。这个很重要,如果没有序列化操作,后台是拿不到我们提交的数据的。

/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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)
 })
 });
}

补充:axios.get()方法和axios.post()在提交数据时参数的书写方式还是有区别的。区别就是,get的第二个参数是一个{},然后这个对象的params属性值是一个参数对象的。而post的第二个参数就是一个参数对象。

API 统一管理

创建文件

通常我们会创建一个 api.js 用于存放我们所有的API接口。
首先我们在 api.js 中引入我们封装的 get 和 post 方法。

/** 
 * api接口统一管理
 */
import { get, post } from './http'

封装案例

我们定义了一个apiAddress方法,这个方法有一个参数p,p是我们请求接口时携带的参数对象。而后调用了我们封装的post方法,post方法的第一个参数是我们的接口地址,第二个参数是apiAddress的p参数,即请求接口时携带的参数对象。最后通过export导出apiAddress。

export const apiAddress = p => post('api/v1/users/my_address/address_edit_before', p);

调用封装

当我们想请求对应的API地址时,可以将上面创建的文件引入,并请求以及处理数据。

import { apiAddress } from '@/request/api';// 导入我们的api接口
export default {  
 name: 'Address', 
 created () {
  this.onLoad();
 },
 methods: {   
  // 获取数据   
  onLoad() {
   // 调用api接口,并且提供了两个参数    
   apiAddress({     
    type: 0,     
    sort: 1    
   }).then(res => {
    // 获取数据成功后的其他操作
    ………………    
   })   
  }  
 }
}

更多接口

想要拓展接口的时候只需要在 api.js 文件中拓展就行了,记得为每个接口写好注释,不然后面调用的时候会很痛苦。

完整代码

Axios 封装

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */
import axios from 'axios';import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'

if (process.env.NODE_ENV == 'development') { 
 axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') { 
 axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') { 
 axios.defaults.baseURL = 'http://api.123dailu.com/';
}

axios.defaults.timeout = 10000;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 请求拦截器
axios.interceptors.request.use( 
 config => {
  // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
  // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
  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);  
  } 
 },
 // 服务器状态码不是200的情况 
 error => {  
  if (error.response.status) {   
   switch (error.response.status) {    
    // 401: 未登录    
    // 未登录则跳转登录页面,并携带当前页面的路径    
    // 在登录成功后返回当前页面,这一步需要在登录页操作。    
    case 401:     
     router.replace({      
      path: '/login',      
      query: { redirect: router.currentRoute.fullPath } 
     });
     break;
    // 403 token过期    
    // 登录过期对用户进行提示    
    // 清除本地token和清空vuex中token对象    
    // 跳转登录页面    
    case 403:      
     Toast({      
      message: '登录过期,请重新登录',      
      duration: 1000,      
      forbidClick: true     
     });     
     // 清除token     
     localStorage.removeItem('token');     
     store.commit('loginSuccess', null);     
     // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
     setTimeout(() => {      
      router.replace({       
       path: '/login',       
       query: { 
        redirect: router.currentRoute.fullPath 
       }      
      });     
     }, 1000);     
     break; 
    // 404请求不存在    
    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);  
  }  
 }
);

API 统一管理

/** 
 * get方法,对应get请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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)  
  }) 
 });
}
/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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)  
  }) 
 });
}
赞(0) 打赏
未经允许不得转载:散人研 » vue 单页面应用 Axios的二次封装
分享到: 更多 (0)

评论 抢沙发

1 + 2 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏