import axios from 'axios' import { Message, MessageBox } from 'element-ui' import router from '@/router' /** * 配置的优先顺序 * 查找顺序:lib/defaults.js 中库的默认值 -----> 实例的 defaults 属性 -----> 请求的 config 参数 * 优先级为查找的反序,如上顺序中,后面覆盖前面 * 优先级:请求的 config 参数 -----> 实例的 defaults 属性 -----> lib/defaults.js 中库的默认值 * * @type {AxiosInstance} */ /** * 响应消息体 * { * // `data` 由服务器提供的响应 * data: {}, * // `status` 来自服务器响应的 HTTP 状态码 * status: 200, * // `statusText` 来自服务器响应的 HTTP 状态信息 * statusText: 'OK', * // `headers` 服务器响应的头 * headers: {}, * // `config` 是为请求提供的配置信息 * config: {}, * // `request` 生成响应的请求 * // It is the last ClientRequest instance in node.js (in redirects) * // and an XMLHttpRequest instance the browser * request: {} * } * */ /** * Axios封装 */ class AxiosService { /** * 定义公共属性 * @returns */ buildOptions() { return { // ---------- 自定义配置 ---------- // HTTP response消息体 {data: {}, status: 200, statusText: 'OK', headers: {}, config: {}, request: {}} // 服务端消息体 service body {code: '0', message: 'success', data: {page: 0, totalPage: 1, count: 0, totalCount: 0, rows: [{},{}]} // HTTP response消息体中response.data即是服务端消息体service body // service body中的data是服务端消息体中的业务数据 data // 解构分为,解构HTTP response消息体, 和解构服务端消息体service body(即解构response.data) // never, 不解构HTTP response消息体 // serviceBody 和 serviceData, 一定会解构出HTTP response消息体中的data // 默认serviceBody, 解构HTTP response, 但不解构服务端消息体service body, 返回服务端消息体service body全部 // serviceData, 解构HTTP response, 并解构出服务端消息体service body中的业务数据data // 配置值为 never | serviceBody(默认) | serviceData deconstruction: 'serviceBody', // ---------- 以下为原生配置 ---------- // `url` 是用于请求的服务器 URL url: '', // `method` 是创建请求时使用的方法 method: 'get', // 默认是 get // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL // url = base url + request url, 区分开发环境和生产环境 baseURL: '/api', // `headers` 是即将被发送的自定义请求头 headers: { 'X-Requested-With': 'Songmao' }, // `params` 是即将与请求一起发送的 URL 参数 // 必须是一个无格式对象(plain object)或 URLSearchParams 对象 params: { // xxx: yyy }, // `data` 是作为请求主体被发送的数据 // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH' // 在没有设置 `transformRequest` 时,必须是以下类型之一: // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams // - 浏览器专属:FormData, File, Blob // - Node 专属: Stream data: { // xxx: yyy }, // `timeout` 指定请求超时的毫秒数(0 表示无超时时间) // 如果请求话费了超过 `timeout` 的时间,请求将被中断 timeout: 10000, // `withCredentials` 表示跨域请求时是否需要使用凭证,// 默认的false withCredentials: true, // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'json', // 默认的 // `onUploadProgress` 允许为上传处理进度事件 onUploadProgress: function(progressEvent) { // 对原生进度事件的处理 }, // `onDownloadProgress` 允许为下载处理进度事件 onDownloadProgress: function(progressEvent) { // 对原生进度事件的处理 }, // `maxContentLength` 定义允许的响应内容的最大尺寸 maxContentLength: 2000, // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte validateStatus: function(status) { return status >= 200 && status < 300 // 默认的 }, // 'proxy' 定义代理服务器的主机名称和端口 // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据 // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。 proxy: { // // e.g. // host: '127.0.0.1', // port: 8080 // auth: : { // username: 'xxx', // password: 'yyy' // } } } } /** * 实例化 Axios */ constructor() { // 实例化axios对象 this.inst = axios.create(this.buildOptions()) /** * request interceptor */ this.inst.interceptors.request.use( config => { // do something before request is sent if (!config.deconstruction) { const xConfig = { // 默认从response中解构出服务端消息体 deconstruction: 'serviceBody' } config = Object.assign({}, config, xConfig) } return config }, error => { // do something with request error return Promise.reject(error) } ) /** * response interceptor */ this.inst.interceptors.response.use( response => { const { data, status, statusText, config } = response const deconstruction = config.deconstruction || 'serviceBody' // ---------- 返回response 消息体 ---------- if (deconstruction === 'never') { return response => response } // ---------- 返回服务端消息体 ---------- if (deconstruction === 'serviceBody') { if (status === 200) { return data } else { Message({ type: 'error', title: '消息', message: statusText || 'Http Error', duration: 5 * 1000 }) return Promise.reject(new Error(statusText || 'Http Error')) } } // ---------- 返回服务端消息体中的data ---------- if (!data) { Message({ type: 'error', title: '消息', message: 'Error: HTTP消息体data为空', duration: 5 * 1000 }) return Promise.reject(new Error('Error: HTTP消息体data为空')) } const res = data if (!res.code) { Message({ type: 'error', title: '消息', message: 'Error: 服务端未返回code', duration: 5 * 1000 }) return Promise.reject(new Error('Error: 服务端未返回code')) } if (res.code !== '0') { Message({ type: 'error', title: '消息', message: res.message ? '【' + res.code + ':' + res.message + '】' : '业务异常', duration: 5 * 1000 }) // ---------- 服务端需要处理的消息码 ---------- // 服务端自定义消息码处理 // 100012: Illegal token, 100020: SESSION_EXPIRE if (res.code === '100012' || res.code === '100020') { MessageBox.confirm(res.message ? '【' + res.code + ':' + res.message + '】, 非法请求, 或会话过期' : '非法请求, 或会话过期', '警告', { type: 'warning', confirmButtonText: '确认', cancelButtonText: '取消' }).then(() => { router.push({ path: '/login' }) }) } // others server message code...... return Promise.reject(new Error(res.message || '业务异常')) } else { // 返回服务端消息体Service Body 中的 data return res.data } }, error => { Message({ type: 'error', message: error.message, duration: 5 * 1000 }) return Promise.reject(error) } ) } // ---------- 对外提供访问方法 ---------- instance() { return this.inst } setOptions(options) { this.inst.defaults = Object.assign({}, this.inst.defaults, options) } request(options) { return this.inst.request(options) } get(url, options) { return this.inst.get(url, options) } post(url, options) { return this.inst.post(url, options) } } const Service = (() => new AxiosService())() export default Service