利用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();
    }
});
相關文章
相關標籤/搜索