Skip to content

Instantly share code, notes, and snippets.

@superlbr
Last active October 10, 2019 03:03
Show Gist options
  • Save superlbr/e3b24c74c8df2e5ca17a05f13ca7ee7f to your computer and use it in GitHub Desktop.
Save superlbr/e3b24c74c8df2e5ca17a05f13ca7ee7f to your computer and use it in GitHub Desktop.
request using axios in miniprogram
'use strict'
/** copy axios.min.js */
import axios from 'axios'
import Tips from './Tips'
const host = process.env.NODE_ENV === 'production' ? 'https://xxx.com' : 'http://localhost:3003'
axios.defaults.baseURL = `${host}/api`
axios.defaults.timeout = 10000
/** follow by wepy-axios start */
const supportedMethods = ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']
const supportedResponseTypes = ['json', 'text', 'file']
const unSupportOptions = ['timeout', 'xsrfCookieName', 'xsrfHeaderName', 'maxContentLength']
function createError(message, config, code, request, response) {
var error = new Error(message)
error.config = config
if (code) error.code = code
error.request = request
error.response = response
return error
}
function settle(resolve, reject, response) {
var validateStatus = response.config.validateStatus
// Note: status is not exposed by XDomainRequest
if (!response.status || !validateStatus || validateStatus(response.status)) {
resolve(response)
} else {
reject(createError(
'网络访问失败 ' + response.status,
response.config,
null,
response.request,
response
))
}
}
/**
* 将 headers 对象中的 key 都转为标准格式
*
* @param {object} headers 要转换的 headers 对象
*/
function normalizeHeaders(headers) {
if (headers == null) return {}
const result = {}
Object.keys(headers).forEach(key => {
if (headers[key] == null) return
const keyParts = key.split('-').map(part => {
return part[0].toUpperCase() + part.slice(1).toLowerCase()
})
result[keyParts.join('-')] = headers[key]
})
return result
}
/**
* 将 headers 对象中的 key 都转为小写
*
* @param {object} headers 要转换的 headers 对象
*/
function lowerCaseHeaders(headers) {
if (headers == null) return {}
const result = {}
Object.keys(headers).forEach(key => {
result[key.toLowerCase()] = headers[key]
})
return result
}
/**
* URL 编码
*
* @param {string} val 要编码的字符串
*/
function encode(val) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
.replace(/%24/g, '$')
.replace(/%2C/gi, ',')
.replace(/%20/g, '+')
.replace(/%5B/gi, '[')
.replace(/%5D/gi, ']')
}
/**
* 拼接 URL 与参数
*
* @param {string} url 原 URL
* @param {object} params 要拼接的参数
* @param {Function} paramsSerializer 参数序列化方法
*/
function buildURL(url, params, paramsSerializer) {
if (!params) return url
var serializedParams
if (paramsSerializer) {
serializedParams = paramsSerializer(params)
} else {
var parts = []
Object.keys(params).forEach(key => {
var val = params[key]
if (val == null) return
if (Array.isArray(val)) {
key = key + '[]'
} else {
val = [val]
}
Object.keys(val).forEach(valKey => {
var v = val[valKey]
if (Object.prototype.toString.call(v) === '[object Date]') {
v = v.toISOString()
} else if (v != null && typeof v === 'object') {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}
// 不使用默认的 transformResponse
axios.defaults['transformResponse'] = []
// 使用自定义的 transformRequest
axios.defaults['transformRequest'] = [(data, headers) => {
const contentTypeKey = Object.keys(headers).find(key => /content-type/i.test(key))
if (contentTypeKey == null && data != null && typeof data === 'object') {
headers['Content-Type'] = 'application/json; charset=utf-8'
}
return data
}]
// 删除无法使用的 axios 选项
unSupportOptions.forEach(item => {
delete axios.defaults[item]
})
// 请求队列
const queue = []
let pendingRequests = 0 // 未完成的请求数
const MAX_PENDING_REQUESTS = 5 // 最多同时发送的请求数
// 检查是否可以发送下一个请求. 如果可以,则发送
function checkQueue() {
while (pendingRequests < MAX_PENDING_REQUESTS) {
const item = queue.shift()
if (!item) break
pendingRequests++
new Promise(item.promiseFunc).then(result => {
pendingRequests--
item.resolve(result)
checkQueue()
}).catch(err => {
pendingRequests--
item.reject(err)
checkQueue()
})
}
}
/**
* 将一个请求加入队列中. 如果当前没有正在排队的请求则立即发送
*
* @param {Function} promiseFunc 要入队的请求
*/
function enqueue(promiseFunc) {
return new Promise((resolve, reject) => {
queue.push({
resolve,
reject,
promiseFunc
})
checkQueue()
})
}
// 请求封装
axios.defaults.adapter = function (config) {
return enqueue((resolve, reject) => {
/** 发送普通请求或是上传、下载请求 */
let requestType = 'request'
/** 请求所使用的参数 */
let request = {
url: buildURL(config.url, config.params, config.paramsSerializer),
header: normalizeHeaders(config.headers)
}
// 必须在 URL 中指定是 http 还是 https 协议
if (!/https?:\/\//.test(request.url)) {
return reject(createError('Request protocol must be http or https', config))
}
// 处理调用上传文件接口的情况
if (config.data && config.data.$upload) {
const {
filePath,
name
} = config.data.$upload
if (typeof filePath === 'string' && typeof name === 'string') {
if (config.method !== 'post') {
return reject(createError('HTTP methods must be POST when uploading file', config))
}
requestType = 'uploadFile'
config.responseType = 'file'
request.filePath = filePath
request.name = name
request.formData = config.data
delete config.data.$upload
} else {
return reject(createError('Invalid upload paramaters', config))
}
}
// 处理响应类型和下载接口类型
if (config.responseType && supportedResponseTypes.indexOf(config.responseType) === -1) {
// 只接受 json 或 text 的返回类型
return reject(createError('Unsupported responseType', config))
} else if (requestType === 'request') {
// 确认是否调用下载文件接口
if (config.responseType === 'file') {
if (config.method !== 'get') {
reject(createError('HTTP method must be GET when downloading file', config))
} else {
requestType = 'downloadFile'
}
} else {
// 普通请求必须使用 https
if (process.env.NODE_ENV === 'production' && config.url.slice(0, 5).toLowerCase() !== 'https') {
return reject(createError('Requesting an http URL is not allowed', config))
}
// 检查小程序是否支持所使用的 method
config.method = config.method.toUpperCase()
if (supportedMethods.indexOf(config.method) === -1) {
return reject(createError('Unsupported request method', config))
}
request.data = config.data
request.method = config.method
// 增加 Content-Type
if (!request.header['Content-Type'] && config.data && typeof config.data === 'object') {
request.header['Content-Type'] = 'application/json; charset=utf-8'
}
}
}
// 加入回调函数
request.success = response => {
// 按照 axios 的返回格式构造返回值
settle(resolve, reject, {
data: response.data || response.tempFilePath,
status: response.statusCode,
headers: lowerCaseHeaders(response.header),
config,
request
})
}
request.fail = response => reject(createError(response.errMsg, config))
// 发送请求
wx[requestType](request)
})
}
/** follow by wepy-axios end */
// axios 内置的中断ajax的方法
const cancelQueue = []
const CancelToken = axios.CancelToken
// 拼接请求的url和方法,同样的url+方法可以视为相同的请求
const token = config => `${config.url}_${config.method}`
// 中断重复的请求,并从队列中移除
const removeQueue = config => {
let len = cancelQueue.length
while (len--) {
const task = cancelQueue[len]
if (task.token === token(config)) {
task.cancel()
cancelQueue.splice(len, 1)
}
}
}
const instance = axios.create()
// 添加请求拦截器
instance.interceptors.request.use(config => {
removeQueue(config) // 中断之前的同名请求
// 添加cancelToken
config.cancelToken = new CancelToken(c => {
cancelQueue.push({
token: token(config),
cancel: c
})
})
return config
}, error => {
return Promise.reject(error)
})
// 添加响应拦截器
instance.interceptors.response.use(response => {
// 在请求完成后,自动移出队列
const {
config,
status,
data
} = response
removeQueue(config)
if (!status) return Tips.error('服务器未知错误')
if ([404, 504].includes(status)) return Tips.error('服务器失去响应')
if (status === 403) return Tips.error('账户权限不足')
if (data.success === false) {
let message = data.message
if (message === 'BusinessError: ORDERPAID') {
message = '订单已支付'
}
return Tips.error(message)
}
return data.data
}, err => {
return Promise.resolve(err)
})
/**
* 封装后的ajax get方法
*
* @param {string} url 请求路径
* @param {object} params 请求参数
* @param {object} config 用户自定义设置
* @returns
*/
function get(url, params = {}, config = {}) {
const { key } = wx.getStorageSync('authority')
if (key) params.key = key
return instance.get(url, { params }, config)
}
/**
* 封装后的ajax post方法
*
* @param {string} url 请求路径
* @param {object} data 请求参数
* @param {object} config 用户自定义设置
* @returns
*/
function post(url, data = {}, config = {}) {
const { key } = wx.getStorageSync('authority')
if (key) data.key = key
return instance.post(url, data, config)
}
export default {
get,
post
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment