Axios
Promise based HTTP client for the browser and Node.js – Promise based HTTP library that can be used in browsers and NodeJs
In the previous article, I explained how AXIos calls, interceptors, and so on. This time, it mainly analyzes dispatchRequest initiating request, canceling request, data conversion and XSRF defense.
dispatchRequest
In the interceptor’s call chain, the first call function placed is dispatchRequest, which is defined in core/dispatchRequest.js. Here is the source code:
var transformData = require('./transformData');
// Cancel the request exception handling
function throwIfCancellationRequested(config) {
if(config.cancelToken) { config.cancelToken.throwIfRequested(); }}module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Support baseURL config Supports baseURL concatenation
if(config.baseURL && ! isAbsoluteURL(config.url)) { config.url = combineURLs(config.baseURL, config.url); }// Ensure headers exist
config.headers = config.headers || {};
// Transform request data Requests data conversion
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Modify headers
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
// Adapter means adapter. In axios there are two types of adapter: 1, XHR, and 2, HTTP (HTTPS) for nodejs.
// The adapter of the browser environment is XHR, the adapter logic will be explained later
var adapter = config.adapter || defaults.adapter;
// Make the request promise
return adapter(config).then(function onAdapterResolution(response) {
// Request a successful callback
throwIfCancellationRequested(config);
// Transform response data Transforms response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
// Failed callback request
if(! isCancel(reason)) { throwIfCancellationRequested(config);// Transform response data Transforms response data
if(reason && reason.response) { reason.response.data = transformData( reason.response.data, reason.response.headers, config.transformResponse ); }}return Promise.reject(reason);
});
};
Copy the code
As you can see, when the request is initiated, the Config operation is performed and the transformData function is executed several times to process the request data and the response data. This is *** data conversion ***. Finally, an Adapter is returned.
emmmm… So let’s insert adapter and then we’ll talk about transformData later.
adapter
The adapter is mounted on the config. Normally we don’t specify this configuration. Use the default, which is defined in defaults.js by default:
function getDefaultAdapter() {
var adapter;
// Only node.js has a process variable that is of [[Class]] process nodeJS
if (typeofprocess ! = ='undefined'
&& Object.prototype.toString.call(process) === '[object process]'
) {
// For node use HTTP adapter
adapter = require('./adapters/http');
} else if (typeofXMLHttpRequest ! = ='undefined') { // Check the browser environment
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
}
return adapter;
}
var defaults = {
adapter: getDefaultAdapter(),
/* something ... * /
}
module.exports = defaults;
Copy the code
The getDefaultAdapter method internally checks the current axiOS execution environment and introduces different adapters according to the environment. This time, we will only examine the implementation of the browser environment, so that /adapters/xhr.js is our focus.
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
/* something ... Gets the argument */
// Familiar XMLHttpRequest
var request = new XMLHttpRequest();
/* something ... Auth * /
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
request.onreadystatechange = function handleLoad() {
if(! request || request.readyState ! = =4) {
return;
}
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') = = =0)) {
return;
}
/* something ... * /
// Change the encapsulated Promise state
settle(resolve, reject, response);
};
/* The middle part is the listener for some events, such as: cancel, timeout, error, request progress, XSRF, cancel request, etc. */
request.send(requestData);
});
};
Copy the code
Voila! You should already see the XMLHttpRequest familiar to you as a front-end developer.
transformData
Back to the transformData data conversion, axios implements JSON conversion functions here. Let’s look at the definition of transformData:
// transformData.js
module.exports = function transformData(data, headers, fns) {
// Call the incoming FNS loop to handle the incoming data, headers
utils.forEach(fns, function transform(fn) {
data = fn(data, headers);
});
/ / return data
return data;
};
Copy the code
This loop calls the incoming config.transformResponse or config.transformRequest to process data and headers, and returns the processed data. After the request is processed is the result of the request data.
So let’s take the veil off config.transformResponse or config.transformRequest:
// defaults.js
var defaults = {
transformRequest: [function transformRequest(data, headers) {
// Format Accept and content-type of headers
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
// If it is FormData ArrayBuffer Buffer Stream File Blob is not processed and returned directly
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
// If the ArrayBuffer view model is used, the contents of the ArrayBuffer entity are fetched
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
// If an object is converted to a string
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json; charset=utf-8');
return JSON.stringify(data);
}
returndata; }].transformResponse: [function transformResponse(data) {
// Automatic conversion to JSON is here ~~~ check if it is a string, try to convert JSON
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore Ignore processing, otherwise an error will be reported, the result is not */}}returndata; }]./* something... * /
}
Copy the code
As you can see, data conversion is a default behavior of AXIos. As we learned earlier, when users use AXIos, the custom configuration takes precedence over the default configuration. Therefore, if you are not sure whether to abandon the default data conversion behavior, Add a custom transformation configuration to the default behavior when overwriting:
Such as:
axios({
transformRequest: [
function(data) {
/* do something */
return data;
},
...(axios.defaults.transformRequest)
],
transformResponse: [
...(axios.defaults.transformResponse),
function(data) {
/* do something */
returndata; }].url: 'xxxx'.data: {}
}).then((res) = > {
console.log(res.data)
})
Copy the code
Cancel the request
Axios makes clever use of the Promise feature when implementing the cancellation request. Let’s find out
Let’s take a look at how the request is unsent using AXIos:
1 / / way
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('xxxx', {
cancelToken: source.token
})
// Cancel the request (request reason is optional)
source.cancel('Actively cancel request');
2 / / way
const CancelToken = axios.CancelToken;
let cancel;
axios.get('xxxx', {
cancelToken: new CancelToken(function executor(c) { cancel = c; })}); cancel('Actively cancel request');
Copy the code
Canceltoken.js canceltoken.js canceltoken.js canceltoken.js canceltoken.js canceltoken.js canceltoken.js canceltoken.js canceltoken.js
function CancelToken(executor) {
if (typeofexecutor ! = ='function') {
throw new TypeError('executor must be a function.');
}
// Define a pending state promise on CancelToken, assigning the resolve callback to the external variable resolvePromise
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
// Execute the incoming Executor function immediately, passing the actual Cancel method as an argument.
ResolvePromise (resolvePromise, resolvePromise, resolvePromise, resolvePromise)
// The canceltoken.promise. then method defined in XHR is executed, and the request is cancelled internally
executor(function cancel(message) {
// Determine if the request has already been canceled to avoid multiple executions
if (token.reason) {
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
CancelToken.source = function source() {
The source method returns an instance of CancelToken, the same operation as using new CancelToken directly
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
// Returns the created CancelToken instance and the cancellation method
return {
token: token,
cancel: cancel
};
};
Copy the code
The action to cancel the request is actually done in xhr.js with the response
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if(! request) {return;
}
// Cancel the request
request.abort();
reject(cancel);
});
}
Copy the code
The trick is that the executor function in CancelToken controls the state of a promise by passing and executing the resolve function.
The end of the
Most of the axios process implementation has been briefly analyzed up to this point. If there is any unreasonable or wrong place, you are welcome to point out.
The original address