diff --git a/README.md b/README.md index 727c4bf..50e38fd 100644 --- a/README.md +++ b/README.md @@ -26,28 +26,29 @@ npm i axios-miniprogram * 支持 `Promise`。 * 支持 拦截器。 * 支持 取消请求。 +* 支持 自定义合法状态码。 * 支持 自定义参数序列化。 * 支持 自定义转换数据。 -* 支持 自定义成功状态码。 * 支持 自定义平台适配器 -## API +## 使用 可以通过将相关配置传递给`axios`来发送请求。 ### `axios(config)` ```typescript -// 默认发送 GET 请求 +// 发送 GET 请求 axios({ + method: 'get', url: '/test', params: { test: 1 } }); // 发送 POST 请求 axios({ - url: '/test', method: 'post', + url: '/test', data: { test: 1 } }); ``` @@ -64,7 +65,7 @@ axios('/test/xxx'); axios('/test/xxx', { method: 'post' }); ``` -还可以使用请求方法别名来简化请求 +还可以使用请求方法的别名来简化请求。 * ##### axios.request(config) * ##### axios.options(url, config?) @@ -106,6 +107,30 @@ axios.post('/test', { test: 1 }, { }); ``` +## 配置 + +|参数|类型|默认值|说明|平台差异 +|:-|:-|:-|:-|:-|:-|:-| +|adapter|Function|| +|baseURL|String|| +|url|String|| +|method|String|get| +|params|Object|| +|data|String/Object/ArrayBuffer|| +|headers|Object|[查看]()| +|validateStatus|Function|| +|paramsSerializer|Function|| +|transformRequest|Function/Array| +|transformResponse|Function/Array| +|cancelToken|Object| +|timeout|Number|0| +|dataType|String|json| +|responseType|String|text| +|enableHttp2|Boolean|false| +|enableQuic|Boolean|false| +|enableCache|Boolean|false| +|sslVerify|Boolean|false| + ### 默认配置 ##### 全局默认配置`axios.defaults` @@ -140,36 +165,27 @@ instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlenco ##### 配置优先顺序 -发送请求时,会使用默认配置`defaults`和自定义配置`config`合并出请求配置`requestConfig`,然后用合并出的请求配置`requestConfig`去发送请求,多数情况下,后者优先于前者,具体合并策略可以参考 [mergeConfig.ts](https://github.com/early-autumn/axios-miniprogram/blob/master/src/helper/mergeConfig.ts) 的实现。 +发送请求时,会使用默认配置`defaults`和自定义配置`config`合并出请求配置`requestConfig`,然后用合并出的请求配置`requestConfig`去发送请求,多数情况下,后者优先级要高于前者,具体合并策略可以参考 [mergeConfig.ts](https://github.com/early-autumn/axios-miniprogram/blob/master/src/helper/mergeConfig.ts) 的实现。 ### `axios.getUri(config)` -根据传入的配置生成完整的`URL`。 +根据配置中的`url`和`params`生成一个`URI`。 -```typescript -axios.defaults.baseURL = 'https://www.xxx.com'; - -// uri === 'https://www.xxx.com/test?id=1' +```typescript +// uri === '/test?id=1' const uri = axios.getUri({ url: '/test', params: { id: 1 } }); - -// uri2 === 'https://www.yyy.com/test?id=1' -const uri2 = axios.getUri({ - baseURL: 'https://www.yyy.com', - url: '/test', - params: { id: 1 } -}); ``` ### `axios.create(config)` 创建一个`自定义实例`,传入的自定义配置`config`会和`axios`的默认配置`axios.defaults`合并成`自定义实例`的默认配置。 -`自定义实例`拥有和`axios`相同的调用方式和请求方法别名。 +`自定义实例`拥有和`axios`相同的调用方式和请求方法的别名。 ```typescript axios.defaults.baseURL = 'https://www.xxx.com'; diff --git a/index.js b/index.js deleted file mode 100644 index 710ee31..0000000 --- a/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - * @Author: early-autumn - * @Date: 2020-04-05 01:56:05 - * @LastEditors: early-autumn - * @LastEditTime: 2020-04-05 01:56:06 - */ diff --git a/src/adapter/autoAdapter.ts b/src/adapter/request.ts similarity index 60% rename from src/adapter/autoAdapter.ts rename to src/adapter/request.ts index c7ad09f..5bc26f5 100644 --- a/src/adapter/autoAdapter.ts +++ b/src/adapter/request.ts @@ -2,14 +2,9 @@ * @Author: early-autumn * @Date: 2020-04-17 12:18:25 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 23:57:43 + * @LastEditTime: 2020-04-18 16:04:27 */ -import { PlatformRequest } from '../types'; - -interface Platform { - request?: PlatformRequest; - httpRequest?: PlatformRequest; -} +import { Adapter, Platform } from '../types'; /* eslint-disable no-var */ var wx: Platform; @@ -20,33 +15,33 @@ var qq: Platform; var uni: Platform; /** - * 当前平台请求函数 + * 适配器 */ -let adapter: PlatformRequest | undefined; +let request: Adapter | undefined; /** - * 设置当前平台请求函数 + * 设置当前平台适配器 */ function adaptive(): void { switch (true) { // 微信小程序 case wx !== undefined: - adapter = wx.request; + request = wx.request; // 支付宝小程序 case my !== undefined: - adapter = my.request ?? my.httpRequest; + request = my.request ?? my.httpRequest; // 百度小程序 case swan !== undefined: - adapter = swan.request; + request = swan.request; // 字节跳动小程序 case tt !== undefined: - adapter = tt.request; + request = tt.request; // QQ 小程序 case qq !== undefined: - adapter = qq.request; + request = qq.request; // uniapp case uni !== undefined: - adapter = uni.request; + request = uni.request; default: throw new Error('暂未适配此平台,您需要参阅文档使用自定义适配器手动适配当前平台'); } @@ -54,4 +49,4 @@ function adaptive(): void { adaptive(); -export default adapter; +export default request; diff --git a/src/adapter/requestConfig.ts b/src/adapter/requestConfig.ts new file mode 100644 index 0000000..4966100 --- /dev/null +++ b/src/adapter/requestConfig.ts @@ -0,0 +1,45 @@ +/* + * @Author: early-autumn + * @Date: 2020-04-17 15:05:43 + * @LastEditors: early-autumn + * @LastEditTime: 2020-04-18 15:51:02 + */ +import { Method, Data, Headers, AdapterMethod, AxiosRequestConfig, RequestConfig } from '../types'; +import transformURL from '../helper/transformURL'; + +/** + * 请求方法转全大写 + * + * @param config Axios 请求配置 + */ +function methodUppercase(config: AxiosRequestConfig): AdapterMethod { + return (config.method as Method).toUpperCase() as AdapterMethod; +} + +/** + * Axios 请求配置转换成各大平台通用请求配置 + * + * 抹平差异 + * + * @param config Axios 请求配置 + */ +export default function requestConfigOk(config: AxiosRequestConfig): RequestConfig { + const { headers, data, dataType, responseType, timeout, enableHttp2, enableQuic, enableCache, sslVerify } = config; + const url = transformURL(config); + const method = methodUppercase(config); + + return { + url, + method, + header: headers as Headers, + headers: headers as Headers, + data: data as Data, + dataType, + responseType, + timeout, + enableHttp2, + enableQuic, + enableCache, + sslVerify, + }; +} diff --git a/src/adapter/response.ts b/src/adapter/response.ts new file mode 100644 index 0000000..7980735 --- /dev/null +++ b/src/adapter/response.ts @@ -0,0 +1,41 @@ +/* + * @Author: early-autumn + * @Date: 2020-04-17 14:09:16 + * @LastEditors: early-autumn + * @LastEditTime: 2020-04-18 16:03:37 + */ +import { RequestConfig, AxiosRequestConfig, AxiosResponse, Response } from '../types'; + +/** + * 各大平台通用响应体转成 Axios 响应体 + * + * 抹平差异 + * + * @param response 通用响应体 + * @param request 通用请求配置 + * @param config Axios 请求配置 + */ + +export default function responseOk( + response: Response, + request: RequestConfig, + config: AxiosRequestConfig +): AxiosResponse { + response.status = response.status ?? response.statusCode; + response.headers = response.headers ?? response.header; + + const { status, headers, data, cookies, profile } = response; + const statusText = status === 200 ? 'OK' : status === 400 ? 'Bad Adapter' : ''; + + return { + status, + statusText, + headers, + data, + response, + request, + config, + cookies, + profile, + }; +} diff --git a/src/axios.ts b/src/axios.ts index 5673c4c..03e93c7 100644 --- a/src/axios.ts +++ b/src/axios.ts @@ -2,11 +2,10 @@ * @Author: early-autumn * @Date: 2020-04-15 12:45:18 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 21:49:05 + * @LastEditTime: 2020-04-18 15:32:21 */ import { AxiosRequestConfig, Data, AxiosResponse, AxiosBaseInstance, AxiosInstance } from './types'; import Axios from './core/Axios'; -import Cancel from './cancel/Cancel'; import CancelToken from './cancel/CancelToken'; import isCancel from './cancel/isCancel'; import mergeConfig from './helper/mergeConfig'; @@ -63,16 +62,17 @@ function createInstance(config: AxiosRequestConfig): AxiosInstance { const axios = createInstance(defaults); // 添加 create 工厂方法 -axios.create = function create(config: AxiosRequestConfig): AxiosBaseInstance { +axios.create = function create(config: AxiosRequestConfig = {}): AxiosBaseInstance { return createInstance(mergeConfig(axios.defaults, config)); }; // 添加 Axios 类 axios.Axios = Axios; -// 添加取消相关 -axios.Cancel = Cancel; +// 添加 CancelToken 类 axios.CancelToken = CancelToken; + +// 添加判断取消方法 axios.isCancel = isCancel; export default axios; diff --git a/src/core/Axios.ts b/src/core/Axios.ts index ea58b05..d158fe7 100644 --- a/src/core/Axios.ts +++ b/src/core/Axios.ts @@ -2,19 +2,14 @@ * @Author: early-autumn * @Date: 2020-04-13 18:00:27 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 19:14:14 + * @LastEditTime: 2020-04-18 16:17:08 */ -import { AxiosMethod, Params, Data, Interceptors, AxiosRequestConfig, AxiosResponse, Axios } from '../types'; -import transformURL from '../helper/transformURL'; +import { Method, Params, Data, Interceptors, AxiosRequestConfig, AxiosResponse, Axios } from '../types'; +import buildURL from '../helper/buildURL'; import mergeConfig from '../helper/mergeConfig'; import InterceptorManager from './InterceptorManager'; import dispatchRequest from './dispatchRequest'; -interface PromiseCatch { - request: Promise; - response?: Promise; -} - export default class AxiosStatic implements Axios { /** * 默认配置 @@ -26,7 +21,7 @@ export default class AxiosStatic implements Axios { */ public interceptors: Interceptors; - constructor(config: AxiosRequestConfig) { + constructor(config: AxiosRequestConfig = {}) { this.defaults = config; this.interceptors = { request: new InterceptorManager(), @@ -35,40 +30,42 @@ export default class AxiosStatic implements Axios { } /** - * baseURL + url + params 得到完整请求地址 + * 根据配置中的 url 和 params 生成一个 URI * - * @param config 请求配置 + * @param config Axios 请求配置 */ public getUri(config: AxiosRequestConfig): string { - return transformURL(mergeConfig(this.defaults, config)); + config = mergeConfig(this.defaults, config); + + return buildURL((config.url = ''), config.params, config.paramsSerializer).replace(/^\?/, ''); } /** * 发送 HTTP 请求 * - * @param config 请求配置 + * @param config Axios 请求配置 */ public request(config: AxiosRequestConfig): Promise> { config = mergeConfig(this.defaults, config); - const promise: PromiseCatch = { - request: Promise.resolve(config), - }; + let promiseRequest = Promise.resolve(config); // 执行前置拦截器 this.interceptors.request.forEach(function executor({ resolved, rejected }) { - promise.request = promise.request.then(resolved, rejected); + promiseRequest = promiseRequest.then(resolved, rejected); }, 'reverse'); // 发送请求 - promise.response = promise.request.then(dispatchRequest, (err: any) => Promise.reject(err)); + let promisePesponse = promiseRequest.then(dispatchRequest, (err: any) => Promise.reject(err)) as Promise< + AxiosResponse + >; // 执行后置拦截器 this.interceptors.response.forEach(function executor({ resolved, rejected }) { - promise.response = promise.response?.then(resolved, rejected); + promisePesponse = promisePesponse.then(resolved, rejected); }); - return promise.response as Promise>; + return promisePesponse; } /** @@ -168,7 +165,7 @@ export default class AxiosStatic implements Axios { * @param config 额外配置 */ private _requestMethodWithoutParams( - method: AxiosMethod, + method: Method, url: string, params?: Params, config: AxiosRequestConfig = {} @@ -190,7 +187,7 @@ export default class AxiosStatic implements Axios { * @param config 额外配置 */ private _requestMethodWithoutData( - method: AxiosMethod, + method: Method, url: string, data?: Data, config: AxiosRequestConfig = {} diff --git a/src/core/createError.ts b/src/core/createError.ts index 78c5a17..9b0ea13 100644 --- a/src/core/createError.ts +++ b/src/core/createError.ts @@ -2,9 +2,9 @@ * @Author: early-autumn * @Date: 2020-04-14 22:23:39 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 15:57:29 + * @LastEditTime: 2020-04-18 14:20:08 */ -import { AxiosRequestConfig, PlatformRequestConfig, AxiosResponse } from '../types'; +import { AxiosRequestConfig, RequestConfig, AxiosResponse } from '../types'; /** * AxiosError 继承自 Error @@ -21,16 +21,16 @@ class AxiosError extends Error { config: AxiosRequestConfig; /** - * 各大平台通用请求配置 + * 通用请求配置 */ - request: PlatformRequestConfig; + request: RequestConfig; /** * 响应体 */ response?: AxiosResponse; - constructor(message: string, config: AxiosRequestConfig, request: PlatformRequestConfig, response?: AxiosResponse) { + constructor(message: string, config: AxiosRequestConfig, request: RequestConfig, response?: AxiosResponse) { super(message); this.isAxiosError = true; @@ -50,13 +50,13 @@ class AxiosError extends Error { * * @param message 错误信息 * @param config Axios 请求配置 - * @param request 各大平台通用请求配置 - * @param response 响应体 + * @param request 通用请求配置 + * @param response Axios 响应体 */ export default function createError( message: string, config: AxiosRequestConfig, - request: PlatformRequestConfig, + request: RequestConfig, response?: AxiosResponse ): AxiosError { return new AxiosError(message, config, request, response); diff --git a/src/core/dispatchRequest.ts b/src/core/dispatchRequest.ts index b0d03fa..806fcca 100644 --- a/src/core/dispatchRequest.ts +++ b/src/core/dispatchRequest.ts @@ -2,16 +2,18 @@ * @Author: early-autumn * @Date: 2020-04-13 18:01:16 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 23:52:47 + * @LastEditTime: 2020-04-18 15:53:05 */ -import { AnyObject, Method, AxiosRequestConfig, AxiosResponse, Data } from '../types'; -import { merge } from '../helper/utils'; +import { AxiosRequestConfig, AxiosResponse } from '../types'; +import flattenHeaders from '../helper/flattenHeaders'; import transformData from '../helper/transformData'; import isCancel from '../cancel/isCancel'; -import requestAdapter from './requestAdapter'; +import request from './request'; /** - * 如果已经取消, 则抛出取消 + * 如果已经取消, 则抛出取消对象 + * + * @param config Axios 请求配置 */ function throwIfCancellationRequested(config: AxiosRequestConfig) { if (config.cancelToken) { @@ -22,31 +24,23 @@ function throwIfCancellationRequested(config: AxiosRequestConfig) { /** * 发送请求 * - * @param config 请求配置 + * @param config Axios 请求配置 */ export default function dispatchRequest(config: AxiosRequestConfig): Promise { throwIfCancellationRequested(config); - const { method = 'GET', data = {}, headers = {} } = config; + if (config.method === undefined) { + config.method = 'get'; + } - // 把方法转成全大写 - config.method = method.toUpperCase() as Method; + config.headers = flattenHeaders(config); - // 合并 headers - config.headers = merge( - headers.common ?? {}, - (headers[(config.method as string).toLowerCase()] ?? {}) as AnyObject, - headers - ); - - // 转换请求数据 - config.data = transformData(data, config.headers, config.transformResponse); + config.data = transformData(config.data ?? {}, config.headers, config.transformRequest); function onResolved(response: AxiosResponse): AxiosResponse { throwIfCancellationRequested(config); - // 转换响应数据 - response.data = transformData(response.data, response.headers, config.transformResponse) as Data; + response.data = transformData(response.data, response.headers, config.transformResponse); return response; } @@ -55,18 +49,13 @@ export default function dispatchRequest(config: AxiosRequestConfig): Promise { + return new Promise(function dispatchAdapter(resolve, reject): void { + const { adapter = requestAdapter, cancelToken } = config; + const request = requestConfigAdapter(config); + + /** + * 捕获错误 + * + * @param message 错误信息 + * @param response Axios 响应体 + */ + function catchError(message: any, response?: AxiosResponse): void { + if (typeof message !== 'string') { + message = '配置不正确或者网络异常'; + } + + reject(createError(message, config, request, response)); + } + + if (adapter === undefined) { + catchError('平台适配失败,您需要参阅文档使用自定义适配器手动适配当前平台'); + + return; + } + + /** + * 效验状态码 + * + * @param res 请求结果 + */ + function validateStatus(res: Response): void { + const response = responseAdapter(res, request, config); + + if (config.validateStatus === undefined || config.validateStatus(response.status)) { + resolve(response); + } else { + catchError(`请求失败,状态码为 ${status}`, response); + } + } + + // 使用适配器发送请求 + const task = adapter({ + ...request, + success: validateStatus, + fail: catchError, + }); + + // 如果存在取消令牌 + // 则调用取消令牌里的 listener 监听用户的取消操作 + if (cancelToken !== undefined) { + cancelToken.listener.then(function onCanceled(reason): void { + task.abort(); + reject(reason); + }); + } + }); +} diff --git a/src/core/requestAdapter.ts b/src/core/requestAdapter.ts deleted file mode 100644 index a6cbad4..0000000 --- a/src/core/requestAdapter.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * @Author: early-autumn - * @Date: 2020-04-16 00:48:45 - * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 22:18:07 - */ - -import { AxiosRequestConfig, AxiosResponse, PlatformResponse } from '../types'; -import transformRequest from '../helper/transformRequest'; -import transformResponse from '../helper/transformResponse'; -import autoAdapter from '../adapter/autoAdapter'; -import createError from './createError'; - -/** - * 请求适配器 - * - * @param config 请求配置 - */ -export default function requestAdapter(config: AxiosRequestConfig): Promise { - return new Promise(function dispatchRequestAdapter(resolve, reject): void { - const { adapter = autoAdapter, cancelToken } = config; - const requestConfig = transformRequest(config); - - /** - * 抛出异常 - * - * @param param0 错误信息 - * @param response 请求响应体 - */ - function catchError(message: any, response?: AxiosResponse): void { - if (typeof message !== 'string') { - message = '网络错误'; - } - - reject(createError(message, config, requestConfig, response)); - } - - if (adapter === undefined) { - catchError('平台适配失败,您需要参阅文档使用自定义适配器手动适配当前平台'); - - return; - } - - /** - * 检查请求结果的状态码 - * - * @param result 请求结果 - */ - function checkStatusCode(result: PlatformResponse): void { - const response = transformResponse(result, config); - - if (config.validateStatus === undefined || config.validateStatus(response.status)) { - resolve(response); - } else { - catchError(`请求失败,状态码为 ${status}`, response); - } - } - - // 发送请求 - const requestTask = adapter({ - ...requestConfig, - success: checkStatusCode, - fail: catchError, - }); - - // 如果存在取消令牌 - // 则调用取消令牌里的 listener 监听用户的取消操作 - if (cancelToken !== undefined) { - cancelToken.listener.then(function onCanceled(reason): void { - requestTask.abort(); - reject(reason); - }); - } - }); -} diff --git a/src/helper/processURL.ts b/src/helper/buildURL.ts similarity index 97% rename from src/helper/processURL.ts rename to src/helper/buildURL.ts index 2df640f..1bbf333 100644 --- a/src/helper/processURL.ts +++ b/src/helper/buildURL.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-13 21:45:45 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 23:54:41 + * @LastEditTime: 2020-04-18 16:12:10 */ import { AnyObject, Params } from '../types'; import { isPlainObject, isDate } from './utils'; @@ -89,7 +89,7 @@ function paramsSerializerDefault(params: AnyObject): string { * @param params 请求参数 * @param paramsSerializer 自定义参数序列化 */ -export default function processURL( +export default function buildURL( url: string, params?: Params, paramsSerializer?: (params: AnyObject) => string diff --git a/src/helper/defaults.ts b/src/helper/defaults.ts index 04023bb..da0b704 100644 --- a/src/helper/defaults.ts +++ b/src/helper/defaults.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-15 22:09:38 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 23:45:11 + * @LastEditTime: 2020-04-18 17:00:38 */ import { AxiosRequestConfig } from '../types'; @@ -29,6 +29,12 @@ const defaults: AxiosRequestConfig = { return status >= 200 && status < 300; }, timeout: 0, + dataType: 'json', + responseType: 'text', + enableHttp2: false, + enableQuic: false, + enableCache: false, + sslVerify: false, }; export default defaults; diff --git a/src/helper/flattenHeaders.ts b/src/helper/flattenHeaders.ts new file mode 100644 index 0000000..c96b9ad --- /dev/null +++ b/src/helper/flattenHeaders.ts @@ -0,0 +1,26 @@ +/* + * @Author: early-autumn + * @Date: 2020-04-18 12:00:01 + * @LastEditors: early-autumn + * @LastEditTime: 2020-04-18 15:40:23 + */ +import { AliasMethod, Headers, AxiosRequestConfig } from '../types'; +import { merge } from './utils'; + +/** + * 拉平请求头 + * + * @param config Axios 请求配置 + */ +export default function flattenHeaders(config: AxiosRequestConfig): Headers { + let { headers = {} } = config; + const method = (config.method as string).toLowerCase() as AliasMethod; + + headers = merge(headers.common ?? {}, headers[method] ?? {}, headers); + + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'].forEach((key: string) => { + delete headers[key]; + }); + + return headers; +} diff --git a/src/helper/mergeConfig.ts b/src/helper/mergeConfig.ts index fabcdb0..9d9fd50 100644 --- a/src/helper/mergeConfig.ts +++ b/src/helper/mergeConfig.ts @@ -2,7 +2,7 @@ * @Author: early-autumn * @Date: 2020-04-15 22:48:25 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-18 00:03:08 + * @LastEditTime: 2020-04-18 14:56:44 */ import { AxiosRequestConfig } from '../types'; import { isPlainObject, deepMerge } from './utils'; @@ -49,10 +49,10 @@ function priorityFromConfig2( keys: [ 'baseURL', 'method', + 'validateStatus', + 'paramsSerializer', 'transformRequest', 'transformResponse', - 'paramsSerializer', - 'validateStatus', 'cancelToken', 'dataType', 'responseType', @@ -103,24 +103,25 @@ function deepMergeConfig( } /** - * 合并配置 + * 合并 Axios 请求配置 * - * @param config1 配置 1 - * @param config2 配置 2 + * @param config1 Axios 请求配置1 + * @param config2 Axios 请求配置2 */ export default function mergeConfig(config1: AxiosRequestConfig, config2: AxiosRequestConfig): AxiosRequestConfig { const config: AxiosRequestConfig = {}; onlyFromConfig1(['adapter'], config, config1); onlyFromConfig2(['url', 'data'], config, config2); + deepMergeConfig(['headers', 'params'], config, config1, config2); priorityFromConfig2( [ 'baseURL', 'method', + 'validateStatus', + 'paramsSerializer', 'transformRequest', 'transformResponse', - 'paramsSerializer', - 'validateStatus', 'cancelToken', 'dataType', 'responseType', @@ -134,7 +135,6 @@ export default function mergeConfig(config1: AxiosRequestConfig, config2: AxiosR config1, config2 ); - deepMergeConfig(['headers', 'params'], config, config1, config2); return config; } diff --git a/src/helper/transformRequest.ts b/src/helper/transformRequest.ts deleted file mode 100644 index 306a7b4..0000000 --- a/src/helper/transformRequest.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * @Author: early-autumn - * @Date: 2020-04-17 15:05:43 - * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 22:22:50 - */ -import { Method, AxiosRequestConfig, PlatformRequestConfig } from '../types'; -import transformURL from './transformURL'; - -export default function transformRequest(config: AxiosRequestConfig): PlatformRequestConfig { - return { - ...config, - url: transformURL(config), - method: config.method as Method, - header: config.headers, - }; -} diff --git a/src/helper/transformResponse.ts b/src/helper/transformResponse.ts deleted file mode 100644 index e6f45ab..0000000 --- a/src/helper/transformResponse.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * @Author: early-autumn - * @Date: 2020-04-17 14:09:16 - * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 15:36:52 - */ -import { AxiosRequestConfig, AxiosResponse, PlatformResponse } from '../types'; - -/** - * 各大平台响应体转换成统一数据结构, 抹平差异 - * - * @param response 响应体 - * @param config 请求配置 - */ -export default function transformResponse(response: PlatformResponse, config: AxiosRequestConfig): AxiosResponse { - const { status, statusCode, headers, header, ...attrPoint } = response; - const responseStatus = status ?? statusCode; - const responseHeaders = headers ?? header; - const responseStatusText = responseStatus === 200 ? 'OK' : responseStatus === 400 ? 'Bad PlatformRequest' : ''; - - return { - ...attrPoint, - status: responseStatus, - statusText: responseStatusText, - headers: responseHeaders, - config, - }; -} diff --git a/src/helper/transformURL.ts b/src/helper/transformURL.ts index f4edc62..2e4da57 100644 --- a/src/helper/transformURL.ts +++ b/src/helper/transformURL.ts @@ -2,14 +2,14 @@ * @Author: early-autumn * @Date: 2020-04-17 00:00:21 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 00:09:47 + * @LastEditTime: 2020-04-18 16:21:17 */ import { AxiosRequestConfig } from '../types'; import { isAbsoluteURL, combineURL } from './utils'; -import processURL from './processURL'; +import buildURL from './buildURL'; /** - * baseURL + url + params 得到完整请求地址 + * 根据配置中的 baseURL 和 url 和 params 生成完整 URL * * @param config 请求配置 */ @@ -17,5 +17,5 @@ export default function transformURL(config: AxiosRequestConfig): string { const { baseURL = '', url = '', params, paramsSerializer } = config; const fullURL = isAbsoluteURL(url) ? url : combineURL(baseURL, url); - return processURL(fullURL, params, paramsSerializer); + return buildURL(fullURL, params, paramsSerializer); } diff --git a/src/index.ts b/src/index.ts index e0a7b7e..4b425ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,27 +2,13 @@ * @Author: early-autumn * @Date: 2020-04-14 23:22:52 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-17 15:41:23 + * @LastEditTime: 2020-04-18 15:17:50 */ import axios from './axios'; /** - * 导出 typescript 类型 + * typescript 类型全部导出 */ -export { - AxiosMethod, - AxiosRequestConfig, - AxiosResponse, - Axios, - AxiosBaseInstance, - AxiosInstance, - AxiosError, - CancelAction, - CancelTokenSource, - CancelToken, -} from './types'; +export * from './types'; -/** - * 导出 Axios 实例增强 - */ export default axios; diff --git a/src/types.ts b/src/types.ts index 2fd49c8..bb97dd2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,46 +2,40 @@ * @Author: early-autumn * @Date: 2020-04-13 15:23:53 * @LastEditors: early-autumn - * @LastEditTime: 2020-04-18 00:02:38 + * @LastEditTime: 2020-04-18 17:00:57 */ -// import 'miniprogram-api-typings'; - -export interface PlatformRequestTask { - /** - * 取消当前请求 - */ - abort(): void; -} - -/** - * 当前平台请求函数 - */ -export interface PlatformRequest { - (config: PlatformRequestConfig): PlatformRequestTask; -} - /** * 任意值对象 */ export declare type AnyObject = Record; /** - * Axios 请求方法 和 响应方法 类型 + * 请求方法 */ -export declare type Method = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'; +export declare type AdapterMethod = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'; /** - * Axios 请求参数 + * 请求方法别名 + */ +export declare type AliasMethod = 'options' | 'get' | 'head' | 'post' | 'put' | 'delete' | 'trace' | 'connect'; + +/** + * Axios 请求方法 + */ +export declare type Method = AliasMethod | AdapterMethod; + +/** + * Axios 参数 */ export declare type Params = AnyObject; /** - * Axios 请求数据 和 响应数据 类型 + * Axios 数据 */ export declare type Data = string | AnyObject | ArrayBuffer; /** - * Axios 请求头 和 响应头 类型 + * Axios 头 */ export interface Headers { /** @@ -92,41 +86,171 @@ export interface Headers { /** * 自定义配置 */ - [x: string]: string | Record | undefined; + [x: string]: Record | string | undefined; } /** - * 转换请求数据和响应数据函数 类型 + * 通用请求配置 + */ +export interface RequestConfig { + /** + * 接口地址 + */ + url: string; + + /** + * HTTP 请求方法 + */ + method: AdapterMethod; + + /** + * 请求数据 + */ + data: Data; + + /** + * 响应头 Header 同 headers + */ + header: AnyObject; + + /** + * 响应头 Headers 同 header + */ + headers: AnyObject; + + /** + * 返回的数据格式 + */ + dataType?: 'json' | '其他'; + + /** + * 响应的数据类型 + */ + responseType?: 'text' | 'arraybuffer'; + + /** + * 超时时间,单位为毫秒 + */ + timeout?: number; + + /** + * 开启 http2 + */ + enableHttp2?: boolean; + + /** + * 开启 quic + */ + enableQuic?: boolean; + + /** + * 开启 cache + */ + enableCache?: boolean; + + /** + * 验证 ssl 证书 + */ + sslVerify?: boolean; +} + +/** + * 通用响应体 + */ +export interface Response { + /** + * 响应状态码 + */ + statusCode: number; + + /** + * 响应头 Headers + */ + header: AnyObject; + + /** + * 响应状态码 + */ + status: number; + + /** + * 响应头 Headers + */ + headers: Headers; + + /** + * 响应数据 + */ + data: Data; + + /** + * 开发者服务器返回的 cookies,格式为字符串数组 + */ + cookies?: string[]; + + /** + * 网络请求过程中一些关键时间点的耗时信息 + */ + profile?: AnyObject; +} + +/** + * 适配器请求配置 + */ +export interface AdapterRequestConfig extends RequestConfig { + /** + * 成功的响应函数 + */ + success: (res: Response) => void; + + /** + * 失败的响应函数 + */ + fail: (err: unknown) => void; +} + +/** + * 适配器请求任务 + */ +export interface AdapterRequestTask { + /** + * 取消请求 + */ + abort(): void; +} + +/** + * 适配器 + */ +export interface Adapter { + (config: AdapterRequestConfig): AdapterRequestTask; +} + +/** + * 平台 + */ +export interface Platform { + // 请求函数 + request?: Adapter; + // 兼容支付宝小程序 + httpRequest?: Adapter; +} + +/** + * 转换数据函数 */ export interface TransformData { (data: Data, headers: Headers): Data; } /** - * Axios 请求配置方法 类型 - */ -export declare type AxiosMethod = 'options' | 'get' | 'head' | 'post' | 'put' | 'delete' | 'trace' | 'connect' | Method; - -/** - * 请求配置 + * Axios 请求配置 */ export declare interface AxiosRequestConfig { /** - * 自定义平台适配器 - * - * 适配 uniapp 示例: - * - * ```typescript - * import axios from 'axios-miniprogram'; - * - * // 使用全局默认配置进行适配 - * axios.defaults.adapter = uni.request; - * - * // 现在可以正常发送请求了 - * axios('/test') - * ``` + * 自定义适配器 */ - adapter?: PlatformRequest; + adapter?: Adapter; /** * 基础地址 @@ -134,14 +258,14 @@ export declare interface AxiosRequestConfig { baseURL?: string; /** - * 接口地址 + * 请求地址 */ url?: string; /** - * HTTP 请求方法 + * 请求方法 */ - method?: AxiosMethod; + method?: Method; /** * 请求参数 @@ -158,6 +282,16 @@ export declare interface AxiosRequestConfig { */ headers?: Headers; + /** + * 自定义合法状态码 + */ + validateStatus?: (status: number) => boolean; + + /** + * 自定义参数序列化 + */ + paramsSerializer?: (params: AnyObject) => string; + /** * 转换请求数据 */ @@ -168,123 +302,83 @@ export declare interface AxiosRequestConfig { */ transformResponse?: TransformData | TransformData[]; - /** - * 自定义参数序列化 - */ - paramsSerializer?: (params: AnyObject) => string; - - /** - * 自定义合法状态码 - */ - validateStatus?: (status: number) => boolean; - /** * 取消令牌 */ cancelToken?: CancelToken; - /** - * 返回的数据格式 - */ - dataType?: 'json' | '其他'; - - /** - * 响应的数据类型 - */ - responseType?: 'text' | 'arraybuffer'; - /** * 超时时间,单位为毫秒 */ timeout?: number; - //===以下属性均在指定平台有效===// + /** + * 响应数据格式 + */ + dataType?: 'json' | '其他'; + + /** + * 响应数据类型 + */ + responseType?: 'text' | 'arraybuffer'; /** - * wx - * * 开启 http2 */ enableHttp2?: boolean; /** - * wx - * * 开启 quic */ enableQuic?: boolean; /** - * wx - * * 开启 cache */ enableCache?: boolean; /** - * uniapp - * * 验证 ssl 证书 */ sslVerify?: boolean; } /** - * 各大平台通用请求配置 - */ -export interface PlatformRequestConfig extends AxiosRequestConfig { - /** - * 带参地址 - */ - url: string; - - /** - * 全大写 method - */ - method: Method; - - /** - * headers 的副本 - */ - header?: AnyObject; - - /** - * 成功的响应函数 - */ - success?: (res: PlatformResponse) => void; - - /** - * 失败的响应函数 - */ - fail?: (err: any) => void; -} - -/** - * 响应体 + * Axios 响应体 */ export interface AxiosResponse { /** - * 响应状态码 + * 状态码 */ status: number; /** - * 响应状态文本 + * 状态文本 */ statusText: string; /** - * 响应数据 + * 服务端返回的数据 */ data: T; /** - * 响应头 Headers + * 响应头 */ headers: Headers; /** - * 请求配置 + * 通用响应体 + */ + response: Response; + + /** + * 通用请求配置 + */ + request: RequestConfig; + + /** + * Axios 请求配置 */ config: AxiosRequestConfig; @@ -299,46 +393,6 @@ export interface AxiosResponse { profile?: AnyObject; } -/** - * 各大平台通用响应体 - */ -export interface PlatformResponse { - /** - * 响应状态码 - */ - statusCode: number; - - /** - * 响应头 Headers - */ - header: AnyObject; - - /** - * 响应状态码 - */ - status?: number; - - /** - * 响应头 Headers - */ - headers?: Headers; - - /** - * 响应数据 - */ - data: Data; - - /** - * 开发者服务器返回的 cookies,格式为字符串数组 - */ - cookies?: string[]; - - /** - * 网络请求过程中一些关键时间点的耗时信息 - */ - profile?: AnyObject; -} - /** * 拦截器成功的回调函数 */ @@ -418,7 +472,7 @@ export interface Interceptors { } /** - * Axios 原始实例 + * Axios 实例 */ export interface Axios { /** @@ -432,7 +486,7 @@ export interface Axios { interceptors: Interceptors; /** - * baseURL + url + params 得到完整请求地址 + * 根据配置中的 url 和 params 生成一个 URI * * @param config 请求配置 */ @@ -540,9 +594,9 @@ export interface AxiosError extends Error { config: AxiosRequestConfig; /** - * 各大平台通用请求配置 + * 通用请求配置 */ - request: PlatformRequestConfig; + request: RequestConfig; /** * 响应体 @@ -671,30 +725,25 @@ export interface AxiosBaseInstance extends Axios { * * 同时拓展了一些静态属性和方法 */ export interface AxiosInstance extends AxiosBaseInstance { + /** + * 创建 Axios 实例基础增强 + * + * @param config 全局配置 + */ + create(config?: AxiosRequestConfig): AxiosBaseInstance; + /** * Axios 类 */ Axios: AxiosConstructor; /** - * 创建 Axios 实例 - * - * @param config 全局配置 - */ - create(config: AxiosRequestConfig): AxiosBaseInstance; - - /** - * Cancel 类 - */ - Cancel: CancelConstructor; - - /** - * CancelToken 类 + * 取消令牌 类 */ CancelToken: CancelTokenConstructor; /** - * 是否是取消请求实例 + * 检查一个错误是不是取消错误 * * @param value 判断的值 */