From 35be45b07f9e34592f31f1d0f90c430dc701e059 Mon Sep 17 00:00:00 2001 From: "954270063@qq.com" <954270063@qq.com> Date: Fri, 17 Apr 2020 12:06:41 +0800 Subject: [PATCH] =?UTF-8?q?:construction:=20=E7=BC=96=E5=86=99=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E4=B8=AD...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++- package.json | 11 ++-- src/axios.ts | 11 ++-- src/cancel/CancelToken.ts | 2 +- src/cancel/isCancel.ts | 4 +- src/core/InterceptorManager.ts | 6 +- src/core/createError.ts | 4 +- src/core/dispatchRequest.ts | 109 +++++++++++++------------------ src/core/request.ts | 34 ---------- src/core/requestAdapter.ts | 73 +++++++++++++++++++++ src/helper/defaults.ts | 3 +- src/helper/mergeConfig.ts | 21 ++++-- src/helper/utils.ts | 2 +- src/index.ts | 23 ++++++- src/types.ts | 115 +++++++++++++++++++++++---------- test/index.test.ts | 2 +- 16 files changed, 277 insertions(+), 161 deletions(-) delete mode 100644 src/core/request.ts create mode 100644 src/core/requestAdapter.ts diff --git a/README.md b/README.md index 2fc6224..2831242 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ # axios-miniprogram -微信小程序请求工具 + +[![build status](https://travis-ci.com/early-autumn/axios-miniprogram.svg?branch=master)](https://travis-ci.org/early-autumn/axios-miniprogram) +[![Coverage Status](https://coveralls.io/repos/github/early-autumn/axios-miniprogram/badge.svg?branch=master)](https://coveralls.io/github/early-autumn/axios-miniprogram?branch=master) +[![npm version](https://badge.fury.io/js/axios-miniprogram.svg)](https://badge.fury.io/js/axios-miniprogram) +[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) + +## 安装 + +` +yarn add axios-miniprogram +` + +或者 + +` +npm i axios-miniprogram +` diff --git a/package.json b/package.json index ed05bf3..902c6aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "axios-miniprogram", - "version": "1.0.0", - "description": "微信小程序专用请求库。", + "version": "1.0.1", + "description": "微信小程序平台专用请求库。", "main": "package/index.js", "miniprogram": "package", "types": "types/index.d.ts", @@ -35,8 +35,11 @@ "type": "git", "url": "git+https://github.com/early-autumn/axios-miniprogram.git" }, - "keywords": [], - "author": "", + "keywords": [ + "axios", + "miniprogram" + ], + "author": "early-autumn", "license": "MIT", "bugs": { "url": "https://github.com/early-autumn/axios-miniprogram/issues" diff --git a/src/axios.ts b/src/axios.ts index d38aeac..9d0cc07 100644 --- a/src/axios.ts +++ b/src/axios.ts @@ -2,9 +2,9 @@ * @Author: early-autumn * @Date: 2020-04-15 12:45:18 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 23:29:26 + * @LastEditTime: 2020-04-17 09:42:16 */ -import { AxiosRequestConfig, ResponseData, AxiosResponse, AxiosInstance } from './types'; +import { AxiosRequestConfig, ResponseData, AxiosResponse, AxiosBaseInstance, AxiosInstance } from './types'; import Axios from './core/Axios'; import Cancel from './cancel/Cancel'; import CancelToken from './cancel/CancelToken'; @@ -15,7 +15,7 @@ import defaults from './helper/defaults'; /** * 创建一个新的 Axios 实例 * - * 返回一个 axios 增强函数 + * 返回一个 Axios 实例增强 */ function createInstance(config: AxiosRequestConfig): AxiosInstance { const instance = new Axios(config); @@ -57,13 +57,16 @@ function createInstance(config: AxiosRequestConfig): AxiosInstance { return axios as AxiosInstance; } +/** + * Axios 实例增强 + */ const axios = createInstance(defaults); // 添加 Axios 类 axios.Axios = Axios; // 添加 create 工厂方法 -axios.create = function create(config: AxiosRequestConfig) { +axios.create = function create(config: AxiosRequestConfig): AxiosBaseInstance { return createInstance(mergeConfig(axios.defaults, config)); }; diff --git a/src/cancel/CancelToken.ts b/src/cancel/CancelToken.ts index a9cec5b..6adba80 100644 --- a/src/cancel/CancelToken.ts +++ b/src/cancel/CancelToken.ts @@ -46,7 +46,7 @@ export default class CancelTokenStatic implements CancelToken { * * 调用 CancelTokenSource.cancel('这里可以填写您的错误信息') * - * 取消 CancelTokenSource.token + * 取消请求 CancelTokenSource.token */ static source(): CancelTokenSource { let cancel!: CancelAction; diff --git a/src/cancel/isCancel.ts b/src/cancel/isCancel.ts index b1df2a9..f4d23a3 100644 --- a/src/cancel/isCancel.ts +++ b/src/cancel/isCancel.ts @@ -2,12 +2,12 @@ * @Author: early-autumn * @Date: 2020-04-14 09:23:25 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-14 19:18:58 + * @LastEditTime: 2020-04-17 09:27:59 */ import Cancel from './Cancel'; /** - * 是不是一个取消对象 + * 是否是取消请求实例 * * @param value 判断的值 */ diff --git a/src/core/InterceptorManager.ts b/src/core/InterceptorManager.ts index 6f21f07..1479c2e 100644 --- a/src/core/InterceptorManager.ts +++ b/src/core/InterceptorManager.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-15 17:50:50 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-15 23:41:22 + * @LastEditTime: 2020-04-17 09:32:24 */ import { InterceptorResolved, @@ -43,12 +43,12 @@ export default class InterceptorManagerStatic implements InterceptorManager { - return new Promise((resolve, reject) => { - const { headers = {}, cancelToken, ...options } = config; + throwIfCancellationRequested(config); - // 把方法转成全大写 - config.method = (config.method?.toUpperCase() ?? 'GET') as MethodType; + const { method = 'GET', headers = {} } = config; - // 合并 headers - config.headers = merge( - headers.common ?? {}, - (headers[(config.method as string).toLowerCase()] ?? {}) as AnyObject, - headers - ); + // 把方法转成全大写 + config.method = method.toUpperCase() as MethodType; - // 转换请求数据 - config.data = transformData(config.data, config.headers, config.transformRequest); + // 合并 headers + config.headers = merge( + headers.common ?? {}, + (headers[(config.method as string).toLowerCase()] ?? {}) as AnyObject, + headers + ); - /** - * 抛出异常 - * - * @param param0 错误信息 - * @param response 请求响应体 - */ - function catchError({ errMsg }: { errMsg: string }, response?: AxiosResponse): void { - reject(createError(errMsg, config, response)); - } + config.data = transformData(config.data, config.headers, config.transformResponse); - /** - * 检查请求结果的状态码 - * - * @param result 请求结果 - */ - function checkStatusCode(result: WechatMiniprogram.RequestSuccessCallbackResult): void { - const { header: headers, ...rest } = result; - const response = { ...rest, headers, config }; - const { statusCode, errMsg } = response; + function onResolved(response: AxiosResponse): AxiosResponse { + throwIfCancellationRequested(config); - // 成功 - if (config.validateStatus === undefined || config.validateStatus(statusCode)) { - // 转换响应数据 - response.data = transformData(response.data, response.headers, config.transformResponse) as ResponseData; + // Transform response data + response.data = transformData(response.data, response.headers, config.transformResponse) as ResponseData; - resolve(response); - } - // 失败 - else { - // `Request failed with status code ${statusCode}` - catchError({ errMsg }, response); + return response; + } + + function onRejected(reason: any): any { + if (!isCancel(reason)) { + throwIfCancellationRequested(config); + + // Transform response data + if (reason && reason.response !== undefined) { + reason.response.data = transformData( + reason.response.data, + reason.response.headers, + config.transformResponse + ) as ResponseData; } } - // 发送请求 - const requestTask = request({ - ...options, - url: transformURL(config), - method: config.method, - header: config.headers, - data: config.data, - success: checkStatusCode, - fail: catchError, - complete: undefined, - }); + return Promise.reject(reason); + } - // 如果存在取消令牌 - // 则调用取消令牌里的 listener 监听用户的取消操作 - if (cancelToken !== undefined) { - cancelToken.listener.then(function onCanceled(reason): void { - requestTask.abort(); - reject(reason); - }); - } - }); + return requestAdapter(config).then(onResolved, onRejected); } diff --git a/src/core/request.ts b/src/core/request.ts deleted file mode 100644 index 826efc2..0000000 --- a/src/core/request.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * @Author: early-autumn - * @Date: 2020-04-16 00:48:45 - * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 15:22:40 - */ - -let request = wx?.request; - -/** - * #### 非微信小程序中使用 axios 前需要先调用此函数重新设置请求方法 - * - * 假设在 uniapp 中使用: - * - * ```typescript - * import axios, { setRequest } from 'axios-miniprogram'; - * - * // 先设置 request - * setRequest(uni.request); - * - * // 现在可以正常发送请求了 - * axios('/test') - * ``` - * - * * 使用 `javascript` 开发忽略, 使用 `typescript` 开发注意: `axios 类型系统`是基于`微信小程序内置类型`定义的, 在其他平台使用类型可能存在不兼容的情况 - * - * @param r 需要设置的请求方法 - * - */ -export function setRequest(r: any): void { - request = r; -} - -export default request; diff --git a/src/core/requestAdapter.ts b/src/core/requestAdapter.ts new file mode 100644 index 0000000..d72d21a --- /dev/null +++ b/src/core/requestAdapter.ts @@ -0,0 +1,73 @@ +/* + * @Author: early-autumn + * @Date: 2020-04-16 00:48:45 + * @LastEditors: early-autumn + * @LastEditTime: 2020-04-17 11:43:25 + */ + +import { MethodType, AxiosRequestConfig, AxiosResponse } from '../types'; +import transformURL from '../helper/transformURL'; +import createError from './createError'; + +/** + * 请求适配器 + * + * @param config 请求配置 + */ +export default function requestAdapter(config: AxiosRequestConfig): Promise { + return new Promise(function dispatchRequestAdapter(resolve, reject): void { + const { adapter, cancelToken } = config; + + /** + * 抛出异常 + * + * @param param0 错误信息 + * @param response 请求响应体 + */ + function catchError({ errMsg }: { errMsg: string }, response?: AxiosResponse): void { + reject(createError(errMsg, config, response)); + } + + if (adapter === undefined) { + catchError({ errMsg: '请求失败,适配器未定义' }); + + return; + } + + /** + * 检查请求结果的状态码 + * + * @param result 请求结果 + */ + function checkStatusCode(result: WechatMiniprogram.RequestSuccessCallbackResult): void { + const { statusCode, header: headers, ...baseResponse } = result; + const response = { ...baseResponse, statusCode, headers, config }; + + if (config.validateStatus === undefined || config.validateStatus(statusCode)) { + resolve(response); + } else { + catchError({ errMsg: `请求失败,状态码为 ${statusCode}` }, response); + } + } + + // 发送请求 + const requestTask = adapter({ + ...config, + url: transformURL(config), + method: config.method as MethodType, + header: config.headers, + success: checkStatusCode, + fail: catchError, + complete: undefined, + }); + + // 如果存在取消令牌 + // 则调用取消令牌里的 listener 监听用户的取消操作 + if (cancelToken !== undefined) { + cancelToken.listener.then(function onCanceled(reason): void { + requestTask.abort(); + reject(reason); + }); + } + }); +} diff --git a/src/helper/defaults.ts b/src/helper/defaults.ts index 474ec41..89a7dda 100644 --- a/src/helper/defaults.ts +++ b/src/helper/defaults.ts @@ -2,11 +2,12 @@ * @Author: early-autumn * @Date: 2020-04-15 22:09:38 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 21:39:21 + * @LastEditTime: 2020-04-17 11:11:28 */ import { AxiosRequestConfig } from '../types'; const defaults: AxiosRequestConfig = { + adapter: wx?.request, method: 'get', timeout: 0, headers: { diff --git a/src/helper/mergeConfig.ts b/src/helper/mergeConfig.ts index 7d12890..613b5ca 100644 --- a/src/helper/mergeConfig.ts +++ b/src/helper/mergeConfig.ts @@ -2,10 +2,10 @@ * @Author: early-autumn * @Date: 2020-04-15 22:48:25 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 21:10:33 + * @LastEditTime: 2020-04-17 11:14:54 */ import { AxiosRequestConfig } from '../types'; -import { isPlainObject, deepMerge } from '../helper/utils'; +import { isPlainObject, deepMerge } from './utils'; /** * 合并配置 @@ -16,10 +16,12 @@ import { isPlainObject, deepMerge } from '../helper/utils'; export default function mergeConfig(config1: AxiosRequestConfig, config2: AxiosRequestConfig): AxiosRequestConfig { const config: AxiosRequestConfig = {}; - const keys1: ['url', 'method', 'data'] = ['url', 'method', 'data']; + const keys1: ['url', 'data'] = ['url', 'data']; const keys2: ['headers', 'params'] = ['headers', 'params']; const keys3: [ + 'adapter', 'baseURL', + 'method', 'dataType', 'responseType', 'timeout', @@ -27,7 +29,18 @@ export default function mergeConfig(config1: AxiosRequestConfig, config2: AxiosR 'enableQuic', 'enableCache', 'cancelToken' - ] = ['baseURL', 'dataType', 'responseType', 'timeout', 'enableHttp2', 'enableQuic', 'enableCache', 'cancelToken']; + ] = [ + 'adapter', + 'baseURL', + 'method', + 'dataType', + 'responseType', + 'timeout', + 'enableHttp2', + 'enableQuic', + 'enableCache', + 'cancelToken', + ]; // 只取 config2 中的值 keys1.forEach((key) => { diff --git a/src/helper/utils.ts b/src/helper/utils.ts index fa4e568..810d385 100644 --- a/src/helper/utils.ts +++ b/src/helper/utils.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-13 21:55:40 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 23:37:50 + * @LastEditTime: 2020-04-17 09:29:19 */ const _toString = Object.prototype.toString; diff --git a/src/index.ts b/src/index.ts index c4178f4..7f0c92c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,28 @@ * @Author: early-autumn * @Date: 2020-04-14 23:22:52 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-16 10:20:12 + * @LastEditTime: 2020-04-17 11:00:55 */ import axios from './axios'; -import { setRequest } from './core/request'; -export { setRequest }; +/** + * 导出 typescript 类型 + */ +export { + AxiosRequestConfig, + AxiosResponse, + Axios, + AxiosBaseInstance, + AxiosInstance, + AxiosError, + Cancel, + CancelAction, + CancelExecutor, + CancelTokenSource, + CancelToken, +} from './types'; +/** + * 导出 Axios 实例增强 + */ export default axios; diff --git a/src/types.ts b/src/types.ts index d51621e..763a3f2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-13 15:23:53 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 00:06:34 + * @LastEditTime: 2020-04-17 11:54:18 */ import 'miniprogram-api-typings'; @@ -95,6 +95,34 @@ export interface TransformData { */ export declare interface AxiosRequestConfig extends Pick { + /** + * 平台适配器, 默认支持微信小程序 + * + * 适配 uniapp 示例: + * + * ```typescript + * import axios from 'axios-miniprogram'; + * + * // 使用全局默认配置进行适配 + * axios.defaults.adapter = uni.request; + * + * // 现在可以正常发送请求了 + * axios('/test') + * + * // 或者使用工厂方法进行适配 + * const instance = axios.create({ + * adapter: uni.request + * }); + * + * // 现在可以正常发送请求了 + * instance('/test') + * ``` + * + * * 使用 `javascript` 开发忽略, 使用 `typescript` 开发注意: `axios 类型系统`是基于`微信小程序内置类型`定义的, 在其他平台使用类型可能存在不兼容的情况 + * + */ + adapter?: (option: WechatMiniprogram.RequestOption) => WechatMiniprogram.RequestTask; + /** * 基础地址 */ @@ -138,7 +166,7 @@ export declare interface AxiosRequestConfig data?: Data; /** - * 请求头配置 + * 请求头 */ headers?: Headers; @@ -184,12 +212,12 @@ export declare interface AxiosRequestConfig } /** - * 服务端响应数据 + * 响应数据 */ export declare type ResponseData = WechatMiniprogram.RequestSuccessCallbackResult['data']; /** - * 请求响应体 + * 响应体 */ export interface AxiosResponse extends Omit { @@ -286,8 +314,9 @@ export interface Interceptors { */ response: InterceptorManager; } + /** - * Axios + * Axios 原始实例 */ export interface Axios { /** @@ -409,13 +438,13 @@ export interface AxiosError extends Error { config: AxiosRequestConfig; /** - * 请求响应体 + * 响应体 */ response?: AxiosResponse; } /** - * 取消对象 + * 取消请求 */ export interface Cancel { /** @@ -429,6 +458,13 @@ export interface Cancel { toString(): string; } +/** + * 取消请求类接口 + */ +export interface CancelConstructor { + new (message?: string): Cancel; +} + /** * 取消操作 */ @@ -448,7 +484,7 @@ export interface CancelExecutor { */ export interface CancelToken { /** - * 取消对象 + * 取消请求 */ reason?: Cancel; @@ -463,26 +499,6 @@ export interface CancelToken { throwIfRequested(): void; } -/** - * 取消令牌类接口 - */ - -export interface CancelTokenConstructor { - new (executor: CancelExecutor): CancelToken; - /** - * 返回一个 CancelTokenSource - * - * CancelTokenSource.token 是一个 CancelToken 对象 - * - * CancelTokenSource.cancel 是一个 CancelAction 函数 - * - * 调用 CancelTokenSource.cancel('这里可以填写您的错误信息') - * - * 取消 CancelTokenSource.token - */ - source(): CancelTokenSource; -} - /** * 取消令牌 source */ @@ -499,11 +515,31 @@ export interface CancelTokenSource { } /** - * axios 增强函数 - * - * 支持两种调用方式, 并含有 Axios 实例的所有属性和方法 + * 取消令牌类接口 */ -export interface AxiosInstance extends Axios { +export interface CancelTokenConstructor { + new (executor: CancelExecutor): CancelToken; + + /** + * 返回一个 CancelTokenSource + * + * CancelTokenSource.token 是一个 CancelToken 对象 + * + * CancelTokenSource.cancel 是一个 CancelAction 函数 + * + * 调用 CancelTokenSource.cancel('这里可以填写您的错误信息') + * + * 取消请求 CancelTokenSource.token + */ + source(): CancelTokenSource; +} + +/** + * Axios 实例基础增强 + * + * * 支持两种函数调用方式 + */ +export interface AxiosBaseInstance extends Axios { /** * 调用方式一 * @@ -518,7 +554,16 @@ export interface AxiosInstance extends Axios { * @param config 额外配置 */ (url: string, config?: AxiosRequestConfig): Promise>; +} +/** + * Axios 实例增强 + * + * * 支持两种函数调用方式 + * + * * 同时拓展了一些静态属性和方法 + */ +export interface AxiosInstance extends AxiosBaseInstance { /** * Axios 类 */ @@ -529,12 +574,12 @@ export interface AxiosInstance extends Axios { * * @param config 全局配置 */ - create(config: AxiosRequestConfig): Axios; + create(config: AxiosRequestConfig): AxiosBaseInstance; /** * Cancel 类 */ - Cancel: Cancel; + Cancel: CancelConstructor; /** * CancelToken 类 @@ -542,7 +587,7 @@ export interface AxiosInstance extends Axios { CancelToken: CancelTokenConstructor; /** - * 是不是一个取消对象 + * 是否是取消请求实例 * * @param value 判断的值 */ diff --git a/test/index.test.ts b/test/index.test.ts index f65bed2..0cb33eb 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-14 23:43:45 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 00:14:34 + * @LastEditTime: 2020-04-17 11:44:55 */ import { isDate } from '../src/helper/utils';