UNPKG

bkui-cli-template-webpack4-saas

Version:

bkui-cli-template-webpack4-saas

255 lines (229 loc) 7.72 kB
/** * @file axios 封装 * @author <%- author %> */ import Vue from 'vue' import axios from 'axios' import cookie from 'cookie' import CachedPromise from './cached-promise' import RequestQueue from './request-queue' import { bus } from '../common/bus' import { messageError } from '@/common/bkmagic' import UrlParse from 'url-parse' import queryString from 'query-string' // axios 实例 const axiosInstance = axios.create({ withCredentials: true, headers: { 'X-REQUESTED-WITH': 'XMLHttpRequest' }, baseURL: AJAX_URL_PREFIX }) /** * request interceptor */ axiosInstance.interceptors.request.use(config => { const urlObj = new UrlParse(config.url) const query = queryString.parse(urlObj.query) if (query[AJAX_MOCK_PARAM]) { // 直接根路径没有 pathname,例如 http://localhost:LOCAL_DEV_PORT/?mock-file=index&invoke=btn1&btn=btn1 // axios get 请求不会请求到 devserver,因此在 pathname 不存在或者为 / 时,加上一个 /mock 的 pathname if (!urlObj.pathname) { config.url = `${LOCAL_DEV_URL}:${LOCAL_DEV_PORT}/mock/${urlObj.query}` } else if (urlObj.pathname === '/') { config.url = `${LOCAL_DEV_URL}:${LOCAL_DEV_PORT}/mock/${urlObj.query}` } else { config.url = `${LOCAL_DEV_URL}:${LOCAL_DEV_PORT}${urlObj.pathname}${urlObj.query}` } } return config }, error => Promise.reject(error)) /** * response interceptor */ axiosInstance.interceptors.response.use( response => response.data, error => Promise.reject(error) ) const http = { queue: new RequestQueue(), cache: new CachedPromise(), cancelRequest: requestId => { return http.queue.cancel(requestId) }, cancelCache: requestId => http.cache.delete(requestId), cancel: requestId => Promise.all([http.cancelRequest(requestId), http.cancelCache(requestId)]) } const methodsWithoutData = ['delete', 'get', 'head', 'options'] const methodsWithData = ['post', 'put', 'patch'] const allMethods = [...methodsWithoutData, ...methodsWithData] // 在自定义对象 http 上添加各请求方法 allMethods.forEach(method => { Object.defineProperty(http, method, { get () { return getRequest(method) } }) }) /** * 获取 http 不同请求方式对应的函数 * * @param {string} http method 与 axios 实例中的 method 保持一致 * * @return {Function} 实际调用的请求函数 */ function getRequest (method) { if (methodsWithData.includes(method)) { return (url, data, config) => getPromise(method, url, data, config) } return (url, config) => getPromise(method, url, null, config) } /** * 实际发起 http 请求的函数,根据配置调用缓存的 promise 或者发起新的请求 * * @param {method} http method 与 axios 实例中的 method 保持一致 * @param {string} 请求地址 * @param {Object} 需要传递的数据, 仅 post/put/patch 三种请求方式可用 * @param {Object} 用户配置,包含 axios 的配置与本系统自定义配置 * * @return {Promise} 本次http请求的Promise */ async function getPromise (method, url, data, userConfig = {}) { const config = initConfig(method, url, userConfig) let promise if (config.cancelPrevious) { await http.cancel(config.requestId) } if (config.clearCache) { http.cache.delete(config.requestId) } else { promise = http.cache.get(config.requestId) } if (config.fromCache && promise) { return promise } promise = new Promise(async (resolve, reject) => { const axiosRequest = methodsWithData.includes(method) ? axiosInstance[method](url, data, config) : axiosInstance[method](url, config) try { const response = await axiosRequest Object.assign(config, response.config || {}) handleResponse({ config, response, resolve, reject }) } catch (error) { Object.assign(config, error.config) reject(error) } }).catch(error => { return handleReject(error, config) }).finally(() => { // console.log('finally', config) }) // 添加请求队列 http.queue.set(config) // 添加请求缓存 http.cache.set(config.requestId, promise) return promise } /** * 处理 http 请求成功结果 * * @param {Object} 请求配置 * @param {Object} cgi 原始返回数据 * @param {Function} promise 完成函数 * @param {Function} promise 拒绝函数 */ function handleResponse ({ config, response, resolve, reject }) { if (!response.data && config.globalError) { reject({ message: response.message }) } else { resolve(config.originalResponse ? response : response.data, config) } http.queue.delete(config.requestId) } /** * 处理 http 请求失败结果 * * @param {Object} Error 对象 * @param {config} 请求配置 * * @return {Promise} promise 对象 */ function handleReject (error, config) { if (axios.isCancel(error)) { return Promise.reject(error) } http.queue.delete(config.requestId) if (config.globalError && error.response) { const { status, data } = error.response const nextError = { message: error.message, response: error.response } if (status === 401) { bus.$emit('show-login-modal', nextError.response) } else if (status === 500) { nextError.message = '系统出现异常' } else if (data && data.message) { nextError.message = data.message } messageError(nextError.message) console.error(nextError.message) return Promise.reject(nextError) } messageError(error.message) console.error(error.message) return Promise.reject(error) } /** * 初始化本系统 http 请求的各项配置 * * @param {string} http method 与 axios 实例中的 method 保持一致 * @param {string} 请求地址, 结合 method 生成 requestId * @param {Object} 用户配置,包含 axios 的配置与本系统自定义配置 * * @return {Promise} 本次 http 请求的 Promise */ function initConfig (method, url, userConfig) { const defaultConfig = { ...getCancelToken(), // http 请求默认 id requestId: method + '_' + url, // 是否全局捕获异常 globalError: true, // 是否直接复用缓存的请求 fromCache: false, // 是否在请求发起前清楚缓存 clearCache: false, // 响应结果是否返回原始数据 originalResponse: true, // 当路由变更时取消请求 cancelWhenRouteChange: true, // 取消上次请求 cancelPrevious: true } return Object.assign(defaultConfig, userConfig) } /** * 生成 http 请求的 cancelToken,用于取消尚未完成的请求 * * @return {Object} {cancelToken: axios 实例使用的 cancelToken, cancelExcutor: 取消http请求的可执行函数} */ function getCancelToken () { let cancelExcutor const cancelToken = new axios.CancelToken(excutor => { cancelExcutor = excutor }) return { cancelToken, cancelExcutor } } Vue.prototype.$http = http export default http /** * 向 http header 注入 CSRFToken,CSRFToken key 值与后端一起协商制定 */ export function injectCSRFTokenToHeaders () { const CSRFToken = cookie.parse(document.cookie).csrftoken if (CSRFToken !== undefined) { axiosInstance.defaults.headers.common['X-CSRFToken'] = CSRFToken } else { console.warn('Can not find csrftoken in document.cookie') } }