axios使用和源碼閱讀

Axios是近年來備受推崇的一個網絡請求庫,它以基於Promise的方式封裝了瀏覽器的XMLHttpRequest和服務器端node http請求,使得咱們能夠用es6推薦的異步方式處理網絡請求。
功能特性:前端

  • 從瀏覽器建立XMLHttpRequest
  • 從node.js建立http請求
  • 支持Promise API
  • 攔截請求與響應
  • 轉換請求與響應數據
  • 取消請求
  • 自動轉換JSON數據
  • 支持客戶端XSRF攻擊防禦

axios.interceptors

axios.interceptors攔截器主要用來做什麼?和路由攔截有什麼區別
  • 一個是接口的request, response數據處理
  • 一個是對業務處理,好比頁面權限控制

axios攔截

axios攔截器分爲請求攔截器和響應攔截器。用戶能夠經過then方法爲請求添加回調,而攔截器中的回調將在then中的回調以前執行vue

// 添加請求攔截器
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// 添加響應攔截器
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

移除已經設置的攔截器node

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

給自定義的axios實例添加攔截器ios

var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
import axios from 'axios'
import env from '@/env'
import Utils from '@/components/Utils'
import {
  Message
} from 'element-ui'

// 獲取系統ID
const systemId = Utils.getQueryString('systemId')

if (systemId) {
  window.systemId = systemId
} else {
  // 沒有系統ID
  location.href = env.dataCloud
}

const auth = (response) => {
  // 未登陸
  if (response && ((response.status === 401) || (response.status === 412))) {
    location.href = env.dataCloud
  }
}

const instance = axios.create({
  baseURL: `${env.api}${env.prefix.develop}`,
  params: {
    systemId
  },
  withCredentials: true
})
instance.nterceptors.response.use((response) => {
  auth(response)
  if (response && response.data && response.data.statusCode !== '0') {
    Message.error((response.data && response.data.msg) || response.data.errmsg || '抱歉')
    return Promise.reject(response).catch(() => { })
  }
  return Promise.resolve(response.data)
}, (error) => {
  let msg = '網絡錯誤,請稍後再試'
  if (error && error.response && error.response.status === 401) {
    msg = '請登陸'
  }
  const notice = document.getElementsByClassName('ivu-message-error')
  if (notice.length === 0) {
    Message.error(msg)
  }
  auth(error && error.response)
  return Promise.reject(error).catch(() => { })
})

export default instance

路由攔截

login.jsgit

clipboard.png

main.js
clipboard.pnges6

axios/lib/core/Axios.js
clipboard.png
axios/lib/core/InterceptorManager.js
clipboard.pnggithub

攔截器上有request,response。分別用於攔截髮送,接收。vuex

經典場景

好比這樣一個場景:element-ui

在進行敏感操做(常見敏感操做如 購買獲取列表等)以前,每一個請求須要攜帶token,可是token 有有效期,token 失效後須要換取新的token並繼續請求。

需求分析:axios

每一個請求都須要攜帶 token ,因此咱們可使用 axios request 攔截器,在這裏,咱們給每一個請求都加 token,這樣就能夠節省每一個請求再一次次的複製粘貼代碼。
token 失效問題,當咱們token 失效,咱們服務端會返回一個特定的錯誤表示,好比 token invalid,可是咱們不能在每一個請求以後去作刷新 token 的操做呀,因此這裏咱們就用 axios response 攔截器,咱們統一處理全部請求成功以後響應過來的數據,而後對特殊數據進行處理,其餘的正常分發。

功能實現

在 main.js 註冊 axios

jsVue.use(Vuex)
Vue.use(VueAxios, axios)
Vue.use(qs)
注:qs,使用axios,必須得安裝 qs,全部的Post 請求,咱們都須要 qs,對參數進行序列化。

在 request 攔截器實現

axios.interceptors.request.use(
 config => {
  config.baseURL = '/api/'
  config.withCredentials = true // 容許攜帶token ,這個是解決跨域產生的相關問題
  config.timeout = 6000
  let token = sessionStorage.getItem('access_token')
  let csrf = store.getters.csrf
  if (token) {
   config.headers = {
    'access-token': token,
    'Content-Type': 'application/x-www-form-urlencoded'
   }
  }
  if (config.url === 'refresh') {
   config.headers = {
    'refresh-token': sessionStorage.getItem('refresh_token'),
    'Content-Type': 'application/x-www-form-urlencoded'
   }
  }
  return config
 },
 error => {
  return Promise.reject(error)
 }
)
//在 response 攔截器實現

axios.interceptors.response.use(
 response => {
  // 定時刷新access-token
  if (!response.data.value && response.data.data.message === 'token invalid') {
   // 刷新token
   store.dispatch('refresh').then(response => {
    sessionStorage.setItem('access_token', response.data)
   }).catch(error => {
    throw new Error('token刷新' + error)
   })
  }
  return response
 },
 error => {
  return Promise.reject(error)
 }
)

源碼分析

[libcoreInterceptorManager.js L5]()

'use strict';

var utils = require('./../utils');

function InterceptorManager() {
  this.handlers = [];
}

/**
 * Add a new interceptor to the stack
 *
 * @param {Function} fulfilled The function to handle `then` for a `Promise`
 * @param {Function} rejected The function to handle `reject` for a `Promise`
 *
 * @return {Number} An ID used to remove interceptor later
 */

//  InterceptorManager的對象中是經過handlers數組變量存儲攔截器,
//  數組每項同時包含了分別做爲Promise中resolve和reject的回調。
// InterceptorManager類中還包含了對該數組變量的添加、 移除、 遍歷方法。
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

/**
 * Remove an interceptor from the stack
 *
 * @param {Number} id The ID that was returned by `use`
 */

// 移除方法是經過直接將該項設爲null實現的, 
// 而不是用splice剪切該數組, 遍歷方法中也增長了相應的null值處理。 
// 這樣作一方面使得每一項ID保持爲項的數組索引不變, 
// 另外一方面也避免了從新剪切拼接數組的性能損失。
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

/**
 * Iterate over all the registered interceptors
 *
 * This method is particularly useful for skipping over any
 * interceptors that may have become `null` calling `eject`.
 *
 * @param {Function} fn The function to call for each interceptor
 */
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

module.exports = InterceptorManager;

參考

vuex使用axios攔截器及持久化

vue-axios interceptors(攔截器)實際應用
一個項目學會前端實現登陸攔截
axios-explore

Vue二次封裝axios

相關文章
相關標籤/搜索