mirror of
https://github.com/tenrok/axios.git
synced 2026-06-20 20:00:40 +03:00
feat(fetch): add fetch, Request, Response env config variables for the adapter; (#7003)
* feat(fetch): add fetch, Request, Response env config variables for the adapter; * feat(fetch): fixed design issue for environments without fetch API globals;
This commit is contained in:
@@ -423,6 +423,9 @@ declare namespace axios {
|
|||||||
insecureHTTPParser?: boolean;
|
insecureHTTPParser?: boolean;
|
||||||
env?: {
|
env?: {
|
||||||
FormData?: new (...args: any[]) => object;
|
FormData?: new (...args: any[]) => object;
|
||||||
|
fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
|
||||||
|
Request?: new (input: (RequestInfo | URL), init?: RequestInit) => Request;
|
||||||
|
Response?: new (body?: (BodyInit | null), init?: ResponseInit) => Response;
|
||||||
};
|
};
|
||||||
formSerializer?: FormSerializerOptions;
|
formSerializer?: FormSerializerOptions;
|
||||||
family?: AddressFamily;
|
family?: AddressFamily;
|
||||||
|
|||||||
Vendored
+3
@@ -355,6 +355,9 @@ export interface AxiosRequestConfig<D = any> {
|
|||||||
insecureHTTPParser?: boolean;
|
insecureHTTPParser?: boolean;
|
||||||
env?: {
|
env?: {
|
||||||
FormData?: new (...args: any[]) => object;
|
FormData?: new (...args: any[]) => object;
|
||||||
|
fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
|
||||||
|
Request?: new (input: (RequestInfo | URL), init?: RequestInit) => Request;
|
||||||
|
Response?: new (body?: (BodyInit | null), init?: ResponseInit) => Response;
|
||||||
};
|
};
|
||||||
formSerializer?: FormSerializerOptions;
|
formSerializer?: FormSerializerOptions;
|
||||||
family?: AddressFamily;
|
family?: AddressFamily;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import utils from '../utils.js';
|
import utils from '../utils.js';
|
||||||
import httpAdapter from './http.js';
|
import httpAdapter from './http.js';
|
||||||
import xhrAdapter from './xhr.js';
|
import xhrAdapter from './xhr.js';
|
||||||
import fetchAdapter from './fetch.js';
|
import * as fetchAdapter from './fetch.js';
|
||||||
import AxiosError from "../core/AxiosError.js";
|
import AxiosError from "../core/AxiosError.js";
|
||||||
|
|
||||||
const knownAdapters = {
|
const knownAdapters = {
|
||||||
http: httpAdapter,
|
http: httpAdapter,
|
||||||
xhr: xhrAdapter,
|
xhr: xhrAdapter,
|
||||||
fetch: fetchAdapter
|
fetch: {
|
||||||
|
get: fetchAdapter.getFetch,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.forEach(knownAdapters, (fn, value) => {
|
utils.forEach(knownAdapters, (fn, value) => {
|
||||||
@@ -26,7 +28,7 @@ const renderReason = (reason) => `- ${reason}`;
|
|||||||
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
|
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getAdapter: (adapters) => {
|
getAdapter: (adapters, config) => {
|
||||||
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
||||||
|
|
||||||
const {length} = adapters;
|
const {length} = adapters;
|
||||||
@@ -49,7 +51,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adapter) {
|
if (adapter && (utils.isFunction(adapter) || (adapter = adapter.get(config)))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+237
-180
@@ -8,14 +8,18 @@ import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../h
|
|||||||
import resolveConfig from "../helpers/resolveConfig.js";
|
import resolveConfig from "../helpers/resolveConfig.js";
|
||||||
import settle from "../core/settle.js";
|
import settle from "../core/settle.js";
|
||||||
|
|
||||||
const isFetchSupported = typeof fetch === 'function' && typeof Request === 'function' && typeof Response === 'function';
|
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||||
const isReadableStreamSupported = isFetchSupported && typeof ReadableStream === 'function';
|
|
||||||
|
const {isFunction} = utils;
|
||||||
|
|
||||||
|
const globalFetchAPI = (({fetch, Request, Response}) => ({
|
||||||
|
fetch, Request, Response
|
||||||
|
}))(utils.global);
|
||||||
|
|
||||||
|
const {
|
||||||
|
ReadableStream, TextEncoder
|
||||||
|
} = utils.global;
|
||||||
|
|
||||||
// used only inside the fetch adapter
|
|
||||||
const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
|
|
||||||
((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
|
|
||||||
async (str) => new Uint8Array(await new Response(str).arrayBuffer())
|
|
||||||
);
|
|
||||||
|
|
||||||
const test = (fn, ...args) => {
|
const test = (fn, ...args) => {
|
||||||
try {
|
try {
|
||||||
@@ -25,205 +29,258 @@ const test = (fn, ...args) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportsRequestStream = isReadableStreamSupported && test(() => {
|
const factory = (env) => {
|
||||||
let duplexAccessed = false;
|
const {fetch, Request, Response} = Object.assign({}, globalFetchAPI, env);
|
||||||
|
const isFetchSupported = isFunction(fetch);
|
||||||
|
const isRequestSupported = isFunction(Request);
|
||||||
|
const isResponseSupported = isFunction(Response);
|
||||||
|
|
||||||
const hasContentType = new Request(platform.origin, {
|
if (!isFetchSupported) {
|
||||||
body: new ReadableStream(),
|
return false;
|
||||||
method: 'POST',
|
}
|
||||||
get duplex() {
|
|
||||||
duplexAccessed = true;
|
|
||||||
return 'half';
|
|
||||||
},
|
|
||||||
}).headers.has('Content-Type');
|
|
||||||
|
|
||||||
return duplexAccessed && !hasContentType;
|
const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream);
|
||||||
});
|
|
||||||
|
|
||||||
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
|
||||||
|
((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
|
||||||
|
async (str) => new Uint8Array(await new Request(str).arrayBuffer())
|
||||||
|
);
|
||||||
|
|
||||||
const supportsResponseStream = isReadableStreamSupported &&
|
const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
|
||||||
test(() => utils.isReadableStream(new Response('').body));
|
let duplexAccessed = false;
|
||||||
|
|
||||||
|
const hasContentType = new Request(platform.origin, {
|
||||||
|
body: new ReadableStream(),
|
||||||
|
method: 'POST',
|
||||||
|
get duplex() {
|
||||||
|
duplexAccessed = true;
|
||||||
|
return 'half';
|
||||||
|
},
|
||||||
|
}).headers.has('Content-Type');
|
||||||
|
|
||||||
const resolvers = {
|
return duplexAccessed && !hasContentType;
|
||||||
stream: supportsResponseStream && ((res) => res.body)
|
});
|
||||||
};
|
|
||||||
|
const supportsResponseStream = isResponseSupported && isReadableStreamSupported &&
|
||||||
|
test(() => utils.isReadableStream(new Response('').body));
|
||||||
|
|
||||||
|
const resolvers = {
|
||||||
|
stream: supportsResponseStream && ((res) => res.body)
|
||||||
|
};
|
||||||
|
|
||||||
|
isFetchSupported && ((() => {
|
||||||
|
['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
|
||||||
|
!resolvers[type] && (resolvers[type] = (res, config) => {
|
||||||
|
let method = res && res[type];
|
||||||
|
|
||||||
|
if (method) {
|
||||||
|
return method.call(res);
|
||||||
|
}
|
||||||
|
|
||||||
isFetchSupported && (((res) => {
|
|
||||||
['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
|
|
||||||
!resolvers[type] && (resolvers[type] = utils.isFunction(res[type]) ? (res) => res[type]() :
|
|
||||||
(_, config) => {
|
|
||||||
throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
|
throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
|
||||||
})
|
})
|
||||||
});
|
|
||||||
})(new Response));
|
|
||||||
|
|
||||||
const getBodyLength = async (body) => {
|
|
||||||
if (body == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(utils.isBlob(body)) {
|
|
||||||
return body.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(utils.isSpecCompliantForm(body)) {
|
|
||||||
const _request = new Request(platform.origin, {
|
|
||||||
method: 'POST',
|
|
||||||
body,
|
|
||||||
});
|
});
|
||||||
return (await _request.arrayBuffer()).byteLength;
|
})());
|
||||||
}
|
|
||||||
|
|
||||||
if(utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
const getBodyLength = async (body) => {
|
||||||
return body.byteLength;
|
if (body == null) {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(utils.isURLSearchParams(body)) {
|
if (utils.isBlob(body)) {
|
||||||
body = body + '';
|
return body.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(utils.isString(body)) {
|
if (utils.isSpecCompliantForm(body)) {
|
||||||
return (await encodeText(body)).byteLength;
|
const _request = new Request(platform.origin, {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveBodyLength = async (headers, body) => {
|
|
||||||
const length = utils.toFiniteNumber(headers.getContentLength());
|
|
||||||
|
|
||||||
return length == null ? getBodyLength(body) : length;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default isFetchSupported && (async (config) => {
|
|
||||||
let {
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
data,
|
|
||||||
signal,
|
|
||||||
cancelToken,
|
|
||||||
timeout,
|
|
||||||
onDownloadProgress,
|
|
||||||
onUploadProgress,
|
|
||||||
responseType,
|
|
||||||
headers,
|
|
||||||
withCredentials = 'same-origin',
|
|
||||||
fetchOptions
|
|
||||||
} = resolveConfig(config);
|
|
||||||
|
|
||||||
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
|
||||||
|
|
||||||
let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
|
|
||||||
|
|
||||||
let request;
|
|
||||||
|
|
||||||
const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
|
|
||||||
composedSignal.unsubscribe();
|
|
||||||
});
|
|
||||||
|
|
||||||
let requestContentLength;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
|
|
||||||
(requestContentLength = await resolveBodyLength(headers, data)) !== 0
|
|
||||||
) {
|
|
||||||
let _request = new Request(url, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: data,
|
body,
|
||||||
duplex: "half"
|
|
||||||
});
|
});
|
||||||
|
return (await _request.arrayBuffer()).byteLength;
|
||||||
let contentTypeHeader;
|
|
||||||
|
|
||||||
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
|
||||||
headers.setContentType(contentTypeHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_request.body) {
|
|
||||||
const [onProgress, flush] = progressEventDecorator(
|
|
||||||
requestContentLength,
|
|
||||||
progressEventReducer(asyncDecorator(onUploadProgress))
|
|
||||||
);
|
|
||||||
|
|
||||||
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!utils.isString(withCredentials)) {
|
if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
|
||||||
withCredentials = withCredentials ? 'include' : 'omit';
|
return body.byteLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloudflare Workers throws when credentials are defined
|
if (utils.isURLSearchParams(body)) {
|
||||||
// see https://github.com/cloudflare/workerd/issues/902
|
body = body + '';
|
||||||
const isCredentialsSupported = "credentials" in Request.prototype;
|
}
|
||||||
request = new Request(url, {
|
|
||||||
...fetchOptions,
|
if (utils.isString(body)) {
|
||||||
signal: composedSignal,
|
return (await encodeText(body)).byteLength;
|
||||||
method: method.toUpperCase(),
|
}
|
||||||
headers: headers.normalize().toJSON(),
|
}
|
||||||
body: data,
|
|
||||||
duplex: "half",
|
const resolveBodyLength = async (headers, body) => {
|
||||||
credentials: isCredentialsSupported ? withCredentials : undefined
|
const length = utils.toFiniteNumber(headers.getContentLength());
|
||||||
|
|
||||||
|
return length == null ? getBodyLength(body) : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return async (config) => {
|
||||||
|
let {
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
data,
|
||||||
|
signal,
|
||||||
|
cancelToken,
|
||||||
|
timeout,
|
||||||
|
onDownloadProgress,
|
||||||
|
onUploadProgress,
|
||||||
|
responseType,
|
||||||
|
headers,
|
||||||
|
withCredentials = 'same-origin',
|
||||||
|
fetchOptions
|
||||||
|
} = resolveConfig(config);
|
||||||
|
|
||||||
|
responseType = responseType ? (responseType + '').toLowerCase() : 'text';
|
||||||
|
|
||||||
|
let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
|
||||||
|
|
||||||
|
let request = null;
|
||||||
|
|
||||||
|
const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
|
||||||
|
composedSignal.unsubscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = await fetch(request, fetchOptions);
|
let requestContentLength;
|
||||||
|
|
||||||
const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
|
try {
|
||||||
|
if (
|
||||||
|
onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
|
||||||
|
(requestContentLength = await resolveBodyLength(headers, data)) !== 0
|
||||||
|
) {
|
||||||
|
let _request = new Request(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: data,
|
||||||
|
duplex: "half"
|
||||||
|
});
|
||||||
|
|
||||||
if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
|
let contentTypeHeader;
|
||||||
const options = {};
|
|
||||||
|
|
||||||
['status', 'statusText', 'headers'].forEach(prop => {
|
if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
|
||||||
options[prop] = response[prop];
|
headers.setContentType(contentTypeHeader)
|
||||||
});
|
|
||||||
|
|
||||||
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
|
||||||
|
|
||||||
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
|
|
||||||
responseContentLength,
|
|
||||||
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
|
||||||
) || [];
|
|
||||||
|
|
||||||
response = new Response(
|
|
||||||
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
|
||||||
flush && flush();
|
|
||||||
unsubscribe && unsubscribe();
|
|
||||||
}),
|
|
||||||
options
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
responseType = responseType || 'text';
|
|
||||||
|
|
||||||
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
|
|
||||||
|
|
||||||
!isStreamResponse && unsubscribe && unsubscribe();
|
|
||||||
|
|
||||||
return await new Promise((resolve, reject) => {
|
|
||||||
settle(resolve, reject, {
|
|
||||||
data: responseData,
|
|
||||||
headers: AxiosHeaders.from(response.headers),
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
config,
|
|
||||||
request
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
unsubscribe && unsubscribe();
|
|
||||||
|
|
||||||
if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
|
|
||||||
throw Object.assign(
|
|
||||||
new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
|
|
||||||
{
|
|
||||||
cause: err.cause || err
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
if (_request.body) {
|
||||||
|
const [onProgress, flush] = progressEventDecorator(
|
||||||
|
requestContentLength,
|
||||||
|
progressEventReducer(asyncDecorator(onUploadProgress))
|
||||||
|
);
|
||||||
|
|
||||||
|
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!utils.isString(withCredentials)) {
|
||||||
|
withCredentials = withCredentials ? 'include' : 'omit';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cloudflare Workers throws when credentials are defined
|
||||||
|
// see https://github.com/cloudflare/workerd/issues/902
|
||||||
|
const isCredentialsSupported = isRequestSupported && "credentials" in Request.prototype;
|
||||||
|
|
||||||
|
const resolvedOptions = {
|
||||||
|
...fetchOptions,
|
||||||
|
signal: composedSignal,
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
headers: headers.normalize().toJSON(),
|
||||||
|
body: data,
|
||||||
|
duplex: "half",
|
||||||
|
credentials: isCredentialsSupported ? withCredentials : undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
request = isRequestSupported && new Request(url, resolvedOptions);
|
||||||
|
|
||||||
|
let response = await (isRequestSupported ? fetch(request, fetchOptions) : fetch(url, resolvedOptions));
|
||||||
|
|
||||||
|
const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
|
||||||
|
|
||||||
|
if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
['status', 'statusText', 'headers'].forEach(prop => {
|
||||||
|
options[prop] = response[prop];
|
||||||
|
});
|
||||||
|
|
||||||
|
const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
|
||||||
|
|
||||||
|
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
|
||||||
|
responseContentLength,
|
||||||
|
progressEventReducer(asyncDecorator(onDownloadProgress), true)
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
response = new Response(
|
||||||
|
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
|
||||||
|
flush && flush();
|
||||||
|
unsubscribe && unsubscribe();
|
||||||
|
}),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseType = responseType || 'text';
|
||||||
|
|
||||||
|
let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
|
||||||
|
|
||||||
|
!isStreamResponse && unsubscribe && unsubscribe();
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
settle(resolve, reject, {
|
||||||
|
data: responseData,
|
||||||
|
headers: AxiosHeaders.from(response.headers),
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
config,
|
||||||
|
request
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
unsubscribe && unsubscribe();
|
||||||
|
|
||||||
|
if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
|
||||||
|
throw Object.assign(
|
||||||
|
new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
|
||||||
|
{
|
||||||
|
cause: err.cause || err
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw AxiosError.from(err, err && err.code, config, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw AxiosError.from(err, err && err.code, config, request);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const seedCache = new Map();
|
||||||
|
|
||||||
|
export const getFetch = (config) => {
|
||||||
|
let env = utils.merge.call({
|
||||||
|
skipUndefined: true
|
||||||
|
}, globalFetchAPI, config ? config.env : null);
|
||||||
|
|
||||||
|
const {fetch, Request, Response} = env;
|
||||||
|
|
||||||
|
const seeds = [
|
||||||
|
Request, Response, fetch
|
||||||
|
];
|
||||||
|
|
||||||
|
let len = seeds.length, i = len,
|
||||||
|
seed, target, map = seedCache;
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
seed = seeds[i];
|
||||||
|
target = map.get(seed);
|
||||||
|
|
||||||
|
target === undefined && map.set(seed, target = (i ? new Map() : factory(env)))
|
||||||
|
|
||||||
|
map = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
const adapter = getFetch();
|
||||||
|
|
||||||
|
export default adapter;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function dispatchRequest(config) {
|
|||||||
config.headers.setContentType('application/x-www-form-urlencoded', false);
|
config.headers.setContentType('application/x-www-form-urlencoded', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
|
const adapter = adapters.getAdapter(config.adapter || defaults.adapter, config);
|
||||||
|
|
||||||
return adapter(config).then(function onAdapterResolution(response) {
|
return adapter(config).then(function onAdapterResolution(response) {
|
||||||
throwIfCancellationRequested(config);
|
throwIfCancellationRequested(config);
|
||||||
|
|||||||
+4
-2
@@ -341,7 +341,7 @@ const isContextDefined = (context) => !isUndefined(context) && context !== _glob
|
|||||||
* @returns {Object} Result of all merge properties
|
* @returns {Object} Result of all merge properties
|
||||||
*/
|
*/
|
||||||
function merge(/* obj1, obj2, obj3, ... */) {
|
function merge(/* obj1, obj2, obj3, ... */) {
|
||||||
const {caseless} = isContextDefined(this) && this || {};
|
const {caseless, skipUndefined} = isContextDefined(this) && this || {};
|
||||||
const result = {};
|
const result = {};
|
||||||
const assignValue = (val, key) => {
|
const assignValue = (val, key) => {
|
||||||
const targetKey = caseless && findKey(result, key) || key;
|
const targetKey = caseless && findKey(result, key) || key;
|
||||||
@@ -352,7 +352,9 @@ function merge(/* obj1, obj2, obj3, ... */) {
|
|||||||
} else if (isArray(val)) {
|
} else if (isArray(val)) {
|
||||||
result[targetKey] = val.slice();
|
result[targetKey] = val.slice();
|
||||||
} else {
|
} else {
|
||||||
result[targetKey] = val;
|
if (!skipUndefined || !isUndefined(val)) {
|
||||||
|
result[targetKey] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -409,4 +409,85 @@ describe('supports fetch with nodejs', function () {
|
|||||||
await fetchAxios.post('/form', form);
|
await fetchAxios.post('/form', form);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('env config', () => {
|
||||||
|
it('should respect env fetch API configuration', async() => {
|
||||||
|
const { data, headers } = await fetchAxios.get('/', {
|
||||||
|
env: {
|
||||||
|
fetch() {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
foo: '1'
|
||||||
|
},
|
||||||
|
text: async () => 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(headers.get('foo'), '1');
|
||||||
|
assert.strictEqual(data, 'test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to request with lack of Request object', async() => {
|
||||||
|
const form = new FormData();
|
||||||
|
|
||||||
|
form.append('x', '1');
|
||||||
|
|
||||||
|
const { data, headers } = await fetchAxios.post('/', form, {
|
||||||
|
onUploadProgress() {
|
||||||
|
// dummy listener to activate streaming
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
Request: null,
|
||||||
|
fetch() {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
foo: '1'
|
||||||
|
},
|
||||||
|
text: async () => 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(headers.get('foo'), '1');
|
||||||
|
assert.strictEqual(data, 'test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to handle response with lack of Response object', async() => {
|
||||||
|
const { data, headers } = await fetchAxios.get('/', {
|
||||||
|
onDownloadProgress() {
|
||||||
|
// dummy listener to activate streaming
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
Request: null,
|
||||||
|
Response: null,
|
||||||
|
fetch() {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
foo: '1'
|
||||||
|
},
|
||||||
|
text: async () => 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(headers.get('foo'), '1');
|
||||||
|
assert.strictEqual(data, 'test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to the global on undefined env value', async() => {
|
||||||
|
server = await startHTTPServer((req, res) => res.end('OK'));
|
||||||
|
|
||||||
|
const { data } = await fetchAxios.get('/', {
|
||||||
|
env: {
|
||||||
|
fetch: undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(data, 'OK');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user