fix: data 不支持对象以外的类型

pull/49/head
zjx0905 2023-04-18 23:08:49 +08:00
parent d714ed23c0
commit 4d8ec80f29
12 changed files with 283 additions and 156 deletions

View File

@ -11,8 +11,14 @@ import {
AxiosRequestHeaders,
} from './core/Axios';
/**
*
*/
export type AxiosAdapterRequestType = 'request' | 'download' | 'upload';
/**
*
*/
export type AxiosAdapterRequestMethod =
| 'OPTIONS'
| 'GET'
@ -24,8 +30,19 @@ export type AxiosAdapterRequestMethod =
| 'TRACE'
| 'CONNECT';
/**
*
*/
export type AxiosAdapterRequestData = string | AnyObject | ArrayBuffer;
/**
*
*/
export type AxiosAdapterResponseData = string | ArrayBuffer | AnyObject;
/**
*
*/
export interface AxiosAdapterResponse extends AnyObject {
/**
*
@ -45,6 +62,9 @@ export interface AxiosAdapterResponse extends AnyObject {
data: AxiosAdapterResponseData;
}
/**
*
*/
export interface AxiosAdapterResponseError extends AnyObject {
/**
*
@ -64,6 +84,9 @@ export interface AxiosAdapterResponseError extends AnyObject {
data?: AnyObject;
}
/**
*
*/
export interface AxiosAdapterRequestConfig extends AnyObject {
/**
*
@ -84,7 +107,7 @@ export interface AxiosAdapterRequestConfig extends AnyObject {
/**
*
*/
data?: AnyObject;
data?: AxiosAdapterRequestData;
/**
*
*/
@ -111,14 +134,30 @@ export interface AxiosAdapterRequestConfig extends AnyObject {
fail(error: AxiosAdapterResponseError): void;
}
/**
*
*/
export interface AxiosAdapterBaseOptions extends AxiosAdapterRequestConfig {
header?: AxiosRequestHeaders;
success(response: AxiosAdapterResponse): void;
fail(error: AxiosAdapterResponseError): void;
}
/**
*
*/
export type AxiosAdapterRequestOptions = AxiosAdapterBaseOptions;
/**
*
*/
export interface AxiosAdapterDownloadOptions extends AxiosAdapterBaseOptions {
filePath?: string;
}
/**
*
*/
export interface AxiosAdapterUploadOptions
extends AxiosAdapterBaseOptions,
AxiosRequestFormData {
@ -126,22 +165,30 @@ export interface AxiosAdapterUploadOptions
formData?: AnyObject;
}
export interface AxiosAdapterDownloadOptions extends AxiosAdapterBaseOptions {
filePath?: string;
}
/**
*
*/
export interface AxiosAdapterRequest {
(config: AxiosAdapterRequestOptions): AxiosAdapterTask;
}
export interface AxiosAdapterUpload {
(config: AxiosAdapterUploadOptions): AxiosAdapterTask;
(config: AxiosAdapterRequestOptions): AxiosAdapterPlatformTask;
}
/**
*
*/
export interface AxiosAdapterDownload {
(config: AxiosAdapterDownloadOptions): AxiosAdapterTask;
(config: AxiosAdapterDownloadOptions): AxiosAdapterPlatformTask;
}
/**
*
*/
export interface AxiosAdapterUpload {
(config: AxiosAdapterUploadOptions): AxiosAdapterPlatformTask;
}
/**
*
*/
export interface AxiosAdapterPlatform {
/**
*
@ -157,7 +204,10 @@ export interface AxiosAdapterPlatform {
upload: AxiosAdapterUpload;
}
export type AxiosAdapterTask =
/**
*
*/
export type AxiosAdapterPlatformTask =
| undefined
| void
| {
@ -166,10 +216,16 @@ export type AxiosAdapterTask =
offProgressUpdate?(callback: AxiosProgressCallback): void;
};
/**
*
*/
export interface AxiosAdapter {
(config: AxiosAdapterRequestConfig): AxiosAdapterTask;
(config: AxiosAdapterRequestConfig): AxiosAdapterPlatformTask;
}
/**
*
*/
export function getDefaultAdapter() {
const platform = revisePlatformApiNames(getPlatform());
@ -220,13 +276,20 @@ export function getDefaultAdapter() {
return createAdapter(platform);
}
/**
*
*
* @param platform API
*/
export function createAdapter(platform: AxiosAdapterPlatform) {
assert(isPlainObject(platform), 'platform 不是一个 object');
assert(isFunction(platform.request), 'request 不是一个 function');
assert(isFunction(platform.upload), 'upload 不是一个 function');
assert(isFunction(platform.download), 'download 不是一个 function');
function adapter(config: AxiosAdapterRequestConfig): AxiosAdapterTask {
function adapter(
config: AxiosAdapterRequestConfig,
): AxiosAdapterPlatformTask {
const baseOptions = transformOptions(config);
switch (config.type) {
@ -242,14 +305,14 @@ export function createAdapter(platform: AxiosAdapterPlatform) {
function processRequest(
request: AxiosAdapterRequest,
baseOptions: AxiosAdapterBaseOptions,
): AxiosAdapterTask {
): AxiosAdapterPlatformTask {
return request(baseOptions);
}
function processUpload(
upload: AxiosAdapterUpload,
baseOptions: AxiosAdapterBaseOptions,
): AxiosAdapterTask {
): AxiosAdapterPlatformTask {
const { name, filePath, fileType, ...formData } =
baseOptions.data as AxiosRequestFormData;
const options = {
@ -273,7 +336,7 @@ export function createAdapter(platform: AxiosAdapterPlatform) {
function processDownload(
download: AxiosAdapterDownload,
baseOptions: AxiosAdapterBaseOptions,
): AxiosAdapterTask {
): AxiosAdapterPlatformTask {
const options: AxiosAdapterDownloadOptions = {
...baseOptions,
filePath: baseOptions.params?.filePath,

View File

@ -10,6 +10,9 @@ import { mergeConfig } from './core/mergeConfig';
import { createAdapter } from './adapter';
import defaults from './defaults';
/**
* axios
*/
export interface AxiosInstanceDefaults extends AxiosRequestConfig {
/**
*
@ -17,6 +20,9 @@ export interface AxiosInstanceDefaults extends AxiosRequestConfig {
headers: Required<AxiosRequestHeaders>;
}
/**
* axios
*/
export interface AxiosInstance extends AxiosDomainRequest, Axios {
/**
*
@ -24,6 +30,9 @@ export interface AxiosInstance extends AxiosDomainRequest, Axios {
defaults: AxiosInstanceDefaults;
}
/**
* axios
*/
export interface AxiosStatic extends AxiosInstance {
/**
* Axios
@ -44,11 +53,11 @@ export interface AxiosStatic extends AxiosInstance {
*/
createAdapter: typeof createAdapter;
/**
* Cancel
* true
*/
isCancel: typeof isCancel;
/**
* AxiosError
* true
*/
isAxiosError: typeof isAxiosError;
}

View File

@ -5,7 +5,7 @@ import { isFunction, isPromise, isString } from '../helpers/isTypes';
import {
AxiosAdapter,
AxiosAdapterRequestMethod,
AxiosAdapterTask,
AxiosAdapterPlatformTask,
AxiosAdapterRequestConfig,
AxiosAdapterResponseData,
} from '../adapter';
@ -14,9 +14,11 @@ import { mergeConfig } from './mergeConfig';
import { CancelToken } from './cancel';
import { dispatchRequest } from './dispatchRequest';
import { AxiosTransformer } from './transformData';
import AxiosDomain from './AxiosDomain';
/**
*
*/
export type AxiosRequestMethod =
| AxiosAdapterRequestMethod
| 'options'
@ -29,6 +31,9 @@ export type AxiosRequestMethod =
| 'trace'
| 'connect';
/**
*
*/
export interface AxiosRequestHeaders extends AnyObject {
/**
*
@ -68,6 +73,9 @@ export interface AxiosRequestHeaders extends AnyObject {
connect?: AnyObject;
}
/**
*
*/
export interface AxiosRequestFormData extends AnyObject {
/**
*
@ -79,10 +87,23 @@ export interface AxiosRequestFormData extends AnyObject {
filePath: string;
}
export type AxiosRequestData = AnyObject | AxiosRequestFormData;
/**
*
*/
export type AxiosRequestData =
| string
| AnyObject
| ArrayBuffer
| AxiosRequestFormData;
/**
*
*/
export type AxiosResponseData = undefined | number | AxiosAdapterResponseData;
/**
*
*/
export interface AxiosProgressEvent {
/**
*
@ -98,10 +119,21 @@ export interface AxiosProgressEvent {
totalBytesExpectedToSend: number;
}
/**
*
*/
export interface AxiosProgressCallback {
(event: AxiosProgressEvent): void;
(
/**
*
*/
event: AxiosProgressEvent,
): void;
}
/**
*
*/
export interface AxiosRequestConfig
extends Partial<
Omit<AxiosAdapterRequestConfig, 'type' | 'success' | 'fail'>
@ -176,6 +208,9 @@ export interface AxiosRequestConfig
validateStatus?: (status: number) => boolean;
}
/**
*
*/
export interface AxiosResponse<
TData extends AxiosResponseData = AxiosResponseData,
> extends AnyObject {
@ -202,9 +237,12 @@ export interface AxiosResponse<
/**
*
*/
request?: AxiosAdapterTask;
request?: AxiosAdapterPlatformTask;
}
/**
*
*/
export interface AxiosResponseError extends AnyObject {
/**
*
@ -233,9 +271,12 @@ export interface AxiosResponseError extends AnyObject {
/**
*
*/
request?: AxiosAdapterTask;
request?: AxiosAdapterPlatformTask;
}
/**
* Axios
*/
export interface AxiosConstructor {
new (config: AxiosRequestConfig): Axios;
}

View File

@ -8,71 +8,48 @@ import {
AxiosResponseData,
} from './Axios';
/**
*
*/
export interface AxiosDomainRequest {
<TData extends AxiosResponseData>(config: AxiosRequestConfig): Promise<
AxiosResponse<TData>
>;
<TData extends AxiosResponseData>(
/**
*
*/
config: AxiosRequestConfig,
): Promise<AxiosResponse<TData>>;
<TData extends AxiosResponseData>(
/**
*
*/
url: string,
/**
*
*/
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>>;
}
export interface AxiosDomainRequestMethod {
<TData extends AxiosResponseData>(
/**
*
/**
*
*/
export type AxiosDomainRequestMethod = <TData extends AxiosResponseData>(
url: string,
/**
*
*/
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>>;
}
) => Promise<AxiosResponse<TData>>;
export interface AxiosDomainRequestMethodWithParams {
<TData extends AxiosResponseData>(
/**
*
/**
*
*/
export type AxiosDomainRequestMethodWithParams = <
TData extends AxiosResponseData,
>(
url: string,
/**
*
*/
params?: AnyObject,
/**
*
*/
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>>;
}
) => Promise<AxiosResponse<TData>>;
export interface AxiosDomainRequestMethodWithData {
<TData extends AxiosResponseData>(
/**
*
/**
*
*/
export type AxiosDomainRequestMethodWithData = <
TData extends AxiosResponseData,
>(
url: string,
/**
*
*/
data?: AxiosRequestData,
/**
*
*/
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>>;
}
) => Promise<AxiosResponse<TData>>;
/**
*
@ -187,7 +164,7 @@ for (const method of requestMethodWithParamsNames) {
config = {},
) {
config.method = method;
config.params = config.params ? deepMerge(params, config.params) : params;
config.params = deepMerge(params, config.params ?? {});
return this.request(url, config);
};
}
@ -195,11 +172,11 @@ for (const method of requestMethodWithParamsNames) {
for (const method of requestMethodWithDataNames) {
AxiosDomain.prototype[method] = function processRequestMethodWithData(
url,
data = {},
data,
config = {},
) {
config.method = method;
config.data = config.data ? deepMerge(data, config.data) : data;
config.data = data;
return this.request(url, config);
};
}

View File

@ -1,19 +1,12 @@
export interface CancelAction {
(
/**
*
*/
message?: string,
): void;
(message?: string): void;
}
export interface CancelExecutor {
(
/**
*
*/
cancel: CancelAction,
): void;
(cancel: CancelAction): void;
}
export interface CancelTokenSource {

View File

@ -1,19 +1,19 @@
import { cleanStack } from '../helpers/error';
import { AxiosAdapterTask } from '../adapter';
import { AxiosAdapterPlatformTask } from '../adapter';
import { AxiosRequestConfig, AxiosResponse, AxiosResponseError } from './Axios';
export type AxiosErrorResponse = AxiosResponse | AxiosResponseError;
class AxiosError extends Error {
config: AxiosRequestConfig;
request: AxiosAdapterTask;
request: AxiosAdapterPlatformTask;
response: AxiosErrorResponse;
constructor(
message: string,
config: AxiosRequestConfig,
response: AxiosErrorResponse,
request: AxiosAdapterTask,
request: AxiosAdapterPlatformTask,
) {
super(message);
@ -29,7 +29,7 @@ export function createError(
message: string,
config: AxiosRequestConfig,
response: AxiosErrorResponse,
request: AxiosAdapterTask,
request: AxiosAdapterPlatformTask,
) {
const axiosError = new AxiosError(message, config, response, request);
cleanStack(axiosError);

View File

@ -9,13 +9,6 @@ import { transformURL } from './transformURL';
import { AxiosErrorResponse } from './createError';
import { requestMethodWithDataNames } from './AxiosDomain';
function throwIfCancellationRequested(config: AxiosRequestConfig) {
const { cancelToken } = config;
if (isCancelToken(cancelToken)) {
cancelToken.throwIfRequested();
}
}
/**
* data
*/
@ -24,6 +17,13 @@ const requestMethodWithDataRE = new RegExp(
'i',
);
/**
*
*
*
*
* @param config
*/
export function dispatchRequest(config: AxiosRequestConfig) {
throwIfCancellationRequested(config);
@ -67,3 +67,10 @@ export function dispatchRequest(config: AxiosRequestConfig) {
return request(config).then(onSuccess, onError);
}
function throwIfCancellationRequested(config: AxiosRequestConfig) {
const { cancelToken } = config;
if (isCancelToken(cancelToken)) {
cancelToken.throwIfRequested();
}
}

View File

@ -4,7 +4,7 @@ import {
AxiosAdapterRequestMethod,
AxiosAdapterResponse,
AxiosAdapterResponseError,
AxiosAdapterTask,
AxiosAdapterPlatformTask,
} from '../adapter';
import {
AxiosProgressCallback,
@ -16,27 +16,13 @@ import { isCancelToken } from './cancel';
import { AxiosErrorResponse, createError } from './createError';
import { generateType } from './generateType';
function tryToggleProgressUpdate(
adapterConfig: AxiosAdapterRequestConfig,
progressUpdate?: (callback: AxiosProgressCallback) => void,
) {
const { onUploadProgress, onDownloadProgress } = adapterConfig;
if (isFunction(progressUpdate)) {
switch (adapterConfig.type) {
case 'upload':
if (isFunction(onUploadProgress)) {
progressUpdate(onUploadProgress);
}
break;
case 'download':
if (isFunction(onDownloadProgress)) {
progressUpdate(onDownloadProgress);
}
break;
}
}
}
/**
*
*
*
*
* @param config
*/
export function request(config: AxiosRequestConfig) {
return new Promise<AxiosResponse>((resolve, reject) => {
const { adapter, url, method, cancelToken } = config;
@ -50,7 +36,7 @@ export function request(config: AxiosRequestConfig) {
fail,
};
let adapterTask: AxiosAdapterTask;
let adapterTask: AxiosAdapterPlatformTask;
try {
adapterTask = adapter!(adapterConfig);
} catch (err) {
@ -60,8 +46,8 @@ export function request(config: AxiosRequestConfig) {
});
}
function success(adapterResponse: AxiosAdapterResponse): void {
const response = adapterResponse as AxiosResponse;
function success(baseResponse: AxiosAdapterResponse): void {
const response = baseResponse as AxiosResponse;
response.status = response.status ?? 200;
response.statusText = response.statusText ?? 'OK';
response.headers = response.headers ?? {};
@ -75,8 +61,8 @@ export function request(config: AxiosRequestConfig) {
}
}
function fail(adapterResponseError: AxiosAdapterResponseError): void {
const responseError = adapterResponseError as AxiosResponseError;
function fail(baseResponseError: AxiosAdapterResponseError): void {
const responseError = baseResponseError as AxiosResponseError;
responseError.isFail = true;
responseError.status = responseError.status ?? 400;
responseError.statusText = responseError.statusText ?? 'Fail Adapter';
@ -111,3 +97,24 @@ export function request(config: AxiosRequestConfig) {
}
});
}
function tryToggleProgressUpdate(
adapterConfig: AxiosAdapterRequestConfig,
progressUpdate?: (callback: AxiosProgressCallback) => void,
) {
const { onUploadProgress, onDownloadProgress } = adapterConfig;
if (isFunction(progressUpdate)) {
switch (adapterConfig.type) {
case 'upload':
if (isFunction(onUploadProgress)) {
progressUpdate(onUploadProgress);
}
break;
case 'download':
if (isFunction(onDownloadProgress)) {
progressUpdate(onDownloadProgress);
}
break;
}
}
}

View File

@ -1,3 +1,4 @@
import { isPlainObject } from '../helpers/isTypes';
import { buildURL } from '../helpers/buildURL';
import { combineURL } from '../helpers/combineURL';
import { dynamicURL } from '../helpers/dynamicURL';
@ -8,7 +9,11 @@ export function transformURL(config: AxiosRequestConfig) {
let url = config.url ?? '';
if (!isAbsoluteURL(url)) url = combineURL(config.baseURL ?? '', url);
url = dynamicURL(url, config.params, config.data);
url = dynamicURL(
url,
config.params,
isPlainObject(config.data) ? config.data : {},
);
url = buildURL(url, config.params, config.paramsSerializer);
return url;

View File

@ -19,14 +19,14 @@ export type {
AxiosAdapterResponse,
AxiosAdapterResponseData,
AxiosAdapterResponseError,
AxiosAdapterPlatform,
AxiosAdapterRequest,
AxiosAdapterRequestOptions,
AxiosAdapterDownload,
AxiosAdapterDownloadOptions,
AxiosAdapterUpload,
AxiosAdapterUploadOptions,
AxiosAdapterTask,
AxiosAdapterPlatform,
AxiosAdapterPlatformTask,
} from './adapter';
export type {
AxiosInstanceDefaults,

View File

@ -196,33 +196,20 @@ describe('src/core/AxiosDomain.ts', () => {
requestMethodWithParamsNames.forEach((k) => a[k]('test', p, c));
});
test('应该支持深度合并 data', () => {
test('应该只取传入的 data', () => {
const ds = {
baseURL: 'http://api.com',
};
const d = {
v1: 1,
v2: {
v1: 1,
},
v: 1,
};
const c = {
data: {
v2: {
v2: 2,
},
v3: 3,
},
v: 2,
};
const a = new AxiosDomain(ds, async (config) => {
expect(config.data).toEqual({
v1: 1,
v2: {
v1: 1,
v2: 2,
},
v3: 3,
v: 1,
});
return {} as AxiosResponse;
@ -230,4 +217,36 @@ describe('src/core/AxiosDomain.ts', () => {
requestMethodWithDataNames.forEach((k) => a[k]('test', d, c));
});
test('应该支持多种类型 data', () => {
const ds = {
baseURL: 'http://api.com',
};
const str = '11';
const obj = {};
const buff = new ArrayBuffer(0);
const testStr = new AxiosDomain(ds, async (config) => {
expect(config.data).toBe(str);
return {} as AxiosResponse;
});
const testObj = new AxiosDomain(ds, async (config) => {
expect(config.data).toBe(obj);
return {} as AxiosResponse;
});
const testBuff = new AxiosDomain(ds, async (config) => {
expect(config.data).toBe(buff);
return {} as AxiosResponse;
});
requestMethodWithDataNames.forEach((k) => {
testStr[k]('test', str);
testObj[k]('test', obj);
testBuff[k]('test', buff);
});
});
});

View File

@ -7,7 +7,13 @@ import {
asyncTimeout,
} from 'scripts/test.utils';
import axios from 'src/axios';
import { Cancel, isCancel, CancelToken, isCancelToken } from '@/core/cancel';
import {
Cancel,
isCancel,
CancelToken,
isCancelToken,
CancelAction,
} from '@/core/cancel';
describe('src/helpers/cancel.ts', () => {
test('应该支持空参数', () => {
@ -31,7 +37,7 @@ describe('src/helpers/cancel.ts', () => {
});
test('应该可以取消', () => {
let ca!: () => void;
let ca!: CancelAction;
const ct = new CancelToken((a) => (ca = a));
expect(ct.throwIfRequested()).toBeUndefined();