Daily development requires front-end and back-end data interaction, so understanding a request library is critical. This article analyzes AXIOS deeply through the interpretation of axiOS request library usage and feature source code, and takes you to master AXIos thoroughly.
Features
- Make XMLHttpRequests from the browser
- Make http requests from node.js
- Supports the
Promise
API - Intercept request and response
- Transform request and response data
- Cancel requests
- Automatic transforms for JSON data
- Client side support for protecting against XSRF
Config order of precedence
The following priorities are in ascending order
- Axios default configuration, in lib/defaults.js
- global axios defaults
axios.defaults.baseURL = 'https://api.example.com';
Copy the code
- Custom instance defaults
const instance = axiso.create({
baseURL: 'https://api.example.com'
});
Copy the code
- request config
instance.get('/users', {
baseURL: 'https://api.github.com'
})
Copy the code
Supports the Promise API
Usage
// Promise
instance.get('/users').then(res= > {
console.log(res);
})
// Async | Await
const res = await instance.get('/users');
console.log(res);
Copy the code
Source
// Take adapter/xhr.js as an example
function xhrAdapter (config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
let requestData = config.data;
let request = new XMLHttpRequest();
request.open(config.method.toUpperCase(), URL, true); // The request has not yet been sent
request.timeout = config.timeout;
request.onreadystatechange = function handleLoad() {
if(! request || request.readyState ! = =4) {
return;
}
const response = {
data,
status: request.status,
statusText: request.statusText,
headers,
config,
request
};
settle(resolve, reject, response);
request = null;
}
if(! requestData) { requestData =null;
}
// send the request
request.send(requestData);
});
}
// Resolve or reject a Promise based on response status
function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus;
if(! response.status || ! validateStatus || validateStatus(response.status)) { resolve(response); }else {
reject(createError('... ')); }}Copy the code
Adapter
Node.js is based on V8 JavaScript Engine. The top-level object is Global. There is no window object or browser host, so XMLHttpRequest object or FETCH API cannot be used. For request libraries that need to adapt to both the browser and node.js environment, use HTTP in the Node.js environment and XMLHttpRequest/Fetch in the browser. Adaptors for different environments were created. Code lib/ Adapters
Usage
const res = await instance.get('/users', {
adapter: function customAdapter(config) {
returnfetch(config.url); }});Copy the code
Source
function dispatchRequest (config) {
const adapter = config.adapter || defaults.adapter; // You can customize adapters in the configuration, priority custom adapter > Default adapter
return adapter(config).then(function onAdapterResolution(response) {
// ...
}, function onAdapterRejection(reason) {
// ...})}const defaults = {
adapter: getDefaultAdapter()
};
// Use different adapters depending on the environment
function getDefaultAdapter () {
let adapter;
if (typeofXMLHttpRequest ! = ='undefined') {
// For browsers use XHR adapter
adapter = require('./adapter/xhr');
} else if (typeofprocess ! = ='undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapter/http');
}
return adapter;
}
Copy the code
Transform request and response data
Usage
const res = await instance.get('/user', {
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
returndata; }].transformResponse: function (data, headers) {
// Do whatever you want to transform the data
return JSON.parse(data); }});Copy the code
Source
function dispatchRequest(config) {
// Transform request data
config.data = transformData(config.data, config.headers, config.transformRequest); // Customize the request conversion function
return adapter(config).then(function onAdapterResolution(response) {
// Transform response data
response.data = transformData(response.data, response.headers, config.transformResponse);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason) {
if(reason && reason.response) { reason.response.data = transformData(reason.response.data, reason.response.headers, config.transformResponse); }})})}/ * * @ param {Array | Function} FNS: A single Function or an Array of functions provides the to transform the data by data and headers * /
function transformData(data, headers, fns) {
utils.forEach(fns, function transform(fn) {
data = fn(data, headers);
});
return data;
}
Copy the code
Automatic transforms for JSON data
Source
const defaults = {
transformResponse: [function transformResponse(data) {
// Automatically parse the JSON string
if (typeof data === 'string') {
data = JSON.parse(data);
}
returndata; }};Copy the code
Interceptors
Usage
// Interceptor registration
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
Copy the code
Source
/ / interceptor collection/lib/core/InterceptorManager js] (https://github.com/axios/axios/blob/master/lib/core/InterceptorManager.js)
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
// Interceptor firing
Axios.prototype.request = function request(config) {
const chain = [dispatchRequest, undefined];
const promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors (interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors (interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// chain = [requestFulfilled, requestRejected, dispatchRequest, undefined, responseFulfilled, responseRejected]
while(chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
}
// Interceptor sequence: Register => collect => trigger
// Trigger order: request interceptor => Request => response interceptor
Copy the code
Cancel Requests
Axios passes a cancelToken in config. The cancelToken. Promise is actually a promise instance in a PENDING state. When the user calls the cancel method, Causes the Promise instance to change its PENDING state to RESOLVE, triggers the listener onCanceled, calls Request.Abort (), and cancels XHR.
Usage
// Carried when sending the request
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
instance.get('/user/12345', {
cancelToken: source.token // Can only be cancelToken
});
// Cancel the request
source.cancel();
Copy the code
Source
function CancelToken(executor) {
let resolvePromise;
this.promise = new Promise(resolve= > {
resolvePromise = resolve;
});
const token = this;
executor(function cancel(message) {
if (token.reason) return;
token.reason = new Cancel(message);
resolvePromise(token.reason)
});
}
/** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */
CancelToken.source = function source() {
let cancel; // Cancel is the function cancel method above. When cancel is performed, the resolvePromise method is called
const token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
// xhr.js 或者 http.js
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if(! request) {return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
Copy the code
XSRF
XSRF, also called CROSS-site Request Forgery (CSRF), is an attack that hijacks trusted users to send unexpected requests to the server. Under normal circumstances, CSRF attack means that the attacker uses the victim’s Cookie to win the trust of the server and sends forged requests to the attacked server in the victim’s name without the victim’s knowledge, so as to perform operations under permission protection without authorization. Generally, the following three ways are used to prevent:
- Verification code
- Referer Check
- Token
The AXIos library takes the approach of passing tokens in requests to protect against XSRF attacks.
// Axios is configured by default. Custom xsrfCookieName and xsrfHeaderName are passed in
const defaults = {
xsrfCookieName: 'XSRF-TOKEN'.xsrfHeaderName: 'X-XSRF-TOKEN'
};
// add XSRF header
if (utils.isStandardBrowserEnv()) {
const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : undefined;
if(xsrfValue) { requestHeaders[config.xsrfHeaderName] = xsrfValue; }}Copy the code
reference
- Learn Axios: Encapsulate a neatly structured Fetch library
- Let’s talk about XSS and CSRF
- axios README