利用axios和vue-router钩子函数实现登陆权限控制,和实现全局请求配置

以前在一家面试时,面试官问及vue中使用ajax请求时我是如何作的,初学者在初学vue时或者说刚上手的开发者,会有人使用过vue-resource(事实上已经被官方抛弃的一个库,如今都推荐使用axios)或aioxs,可是大多不例外的都是在每一个组件中直接去调用这些库的api来进行请求,当时我第一次作vue项目的公司也是这么干的,面试官跟我说这样或许没有问题,可是不够规范也不能方便的管理全部请求的参数配置。
最近的一个vue项目是我本身建立的,因而便想到了此事,结果也正如我所想,确实涉及到许多关于请求参数上的统一配置好比token,用户id等须要传递给后台校验的参数,若是咱们是在每个请求中去传递,项目动辄几十个或上百个的请求,一个个的去添加那是很是麻烦且傻气的作法。
首先看看个人文件结构,我建立了一个http.js,这是我用来专门二次封装axios和管理全局请求的一个文件,router.js配置路由的文件,main.js是vue的入口文件,咱们主要靠这三个文件实现路由访问控制。
image.pngvue

http.js的内容其实也很是的简单ios

import Vue from 'vue';
import axios from 'axios';
import qs from 'qs';
import router from './router';
import md5 from 'js-md5';
let vm = new Vue();
axios.defaults.timeout = 60000;// 在超时前,全部请求都会等待60 秒

首先先使用请求拦截器,对发送的请求体作相应的配置,好比向请求头添加一些必须的参数,或许会用于后台的身份验证如token等,一些接口的加密方法,也能够在请求时统一的显示loading面试

function getHeaderKey(){// 一个加密的方法}
axios.interceptors.request.use(
    config => {
        // config.data = JSON.stringify(config.data);
        if (localStorage.token) {  // 判断是否存在token,若是存在的话,则每一个http header都加上token
            config.headers["X-Access-Token"] = `${localStorage.token}`;
            // 加载loading
            vm.$loading();
        }
      
        // 接口加密验证
        config.headers["channelType"] = 'wx_applet';
        config.headers["for_valid"] = getHeaderKey();
        return config;
    },
    err => {
        // 关闭loading
        vm.$loading.close();
        return Promise.reject(err);
    });

接下来使用响应拦截器对响应体返回的一些参数进行判断,好比接口是否返回了token失效等问题,以作统一的管理。token失效的状态码,须要和后台去约定,并不是必定是401,甚至能够在下面具体封装的post或get等方法中去判断。ajax

// http response 拦截器
axios.interceptors.response.use(
    response => {
        // 关闭loading
        vm.$loading.close();
        return response;
    },
    error => {
        // 关闭loading
        vm.$loading.close();
        if (error.response) {
            vm.$toast.center("返回code:" + error.response.status + ';'+'请求错误');
            switch (error.response.status) {
                case 401:
                    // 返回 401 清除token信息并跳转到登陆页面
                    localStorage.removeItem('token');
                    localStorage.clear();
                    router.replace({
                        path: 'login',
                        query: {redirect: router.currentRoute.fullPath}
                    });
                    break;
            }
        }
        return Promise.reject(error)   // 返回接口返回的错误信息
    });

接下来封装具体的请求方法,能够对不一样的请求方式进行全局管理,由于咱们整个项目都使用的post请求,我把全局的请求须要的reqfrom和appkey都写在了此处,这里不具体去写get方法如何封装,和post基本上差很少。可能会有一些开发者忽略的问题,就是当请求为form格式时,须要使用qs包的stringify方法进行参数的格式化,若格式为json时则不须要。咱们的接口恰好都有这两种格式,因而我也作了区分。由于接口都是post请求,将其判断写在post中。token的失效判断我也根据接口的实际,写在了此处,若失效便跳转登陆页json

export function post(url,data, type){
    // 设置公共请求参数 userid:用户id reqfrom:请求来源 appkey:机构代码(惟一)
    if(localStorage.jsonObject) {
        let userObj = JSON.parse(localStorage.jsonObject);
        data.userid = userObj.userid ? userObj.userid : '';
    }
    data.reqfrom = vm.$api.reqfrom;
    data.appkey = vm.$api.appKey;

    if(type==='json'){
        axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
        data.mobilePhone = localStorage.mobilePhone
    } else {
        axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
        data = qs.stringify(data);
    }
    return new Promise((resolve,reject) => {
        axios.post(url,data)
            .then(response => {
                let code = response.data.code;   
                if(type !=='json'){
                    if(response.data.msg == 'token已失效' || code == '99999'||code=='99997'||code=='40001'||code=='99996'||code=='"99998'||code=='9999'){
                        localStorage.removeItem('token');
                        localStorage.removeItem('jsonObject');
                        router.replace({
                            path: 'login',
                            query: {redirect: router.currentRoute.fullPath}
                        })
                    }
                }
                resolve(response.data);
            },err => {
                if(err.message.indexOf('timeout') !== -1) {
                    vm.$toast.bottom('请求超时')
                }
                reject(err);
            })
    })
}

接下来在main.js中,挂载封装的方法到vue的实例上,即可以在每一个组件进行调用。axios

import { post, get } from "./http";
Vue.prototype.$post = post;
Vue.prototype.$get = get;

页面是否须要登陆才能访问,这个利用了路由的元信息结合路由钩子函数beforeEach来判断,以下。
router.js
image.png
main.js
这里的判断逻辑也很是简单,若是须要登陆权限便去取locaStorage里面的token,没有便放行。若是须要登陆权限但本地不存在token的状况下跳转登陆页,若是token失效如何判断,已经写在上述的http.js中,如果接口返回失效,也会去跳转登陆页,至此,咱们的登陆配置就完成了。固然页面可能远远不止这么简单的逻辑判断,会有更复杂的判断须要去作,就须要根据我的项目的实际状况去增长了。api

router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) { // 判断该路由是否须要登陆权限
        let jsonObject = JSON.parse(localStorage.getItem('jsonObject'));
        // 容错,若localStorage不存在对象,初始化一个空对象
        if (jsonObject == null) {
            jsonObject = {};
        } 
        else {
            next({
                path: '/login',
                // query: { redirect: from.fullPath }
                query: { redirect: router.currentRoute.fullPath }
            })
        }
    } else {
        next();
    }
});
相关文章
相关标签/搜索