First, the front-end network request focus
In most cases, when making a network request at the front end, we only need to focus on the following points:
- Pass in the base argument (
url
, request mode) - Request parameters, request parameter types
- Set the request header
- How to get the response
- Gets the response header, response status, and response result
- Exception handling
- carry
cookie
Set up the - Cross-domain request
Two, the front-end network request way
form
The form,ifream
, refresh the pageAjax
– The originator of asynchronous network requestsjQuery
– An erafetch
–Ajax
The replacement ofAxios, request
And many open source libraries
Three, about the network request question
Ajax
What problem is solved by the emergence of- native
Ajax
How to use jQuery
Network request modefetch
The usage and pit point- How to use it correctly
fetch
- How to choose the appropriate cross-domain approach
With the above questions and concerns, we conduct a comprehensive analysis of several network requests.
What problems has Ajax solved
Before Ajax, web programs worked like this:
The drawbacks of this interaction are obvious. Any interaction with the server requires a page refresh, and the user experience is very poor. The advent of Ajax has solved this problem. Asynchronous JavaScript + XML
Using Ajax, web applications can quickly render incremental updates on the user interface without reloading (refreshing) the entire page.
Ajax itself is not a new technology, but rather describes a technical solution implemented using a collection of existing technologies, and the browser’s XMLHttpRequest is the most important object to implement Ajax (ActiveXObject is used in IE6 below).
Although X stands for XML in Ajax, JSON is now more commonly used than XML because of its many advantages, such as being lightweight and being part of Javascript.
The use of native Ajax
We’ll focus on the XMLHttpRequest object, and here are some basic uses:
var xhr = new XMLHttpRequest();
xhr.open('post'.'www.xxx.com'.true)
// Receive the return value
xhr.onreadystatechange = function(){
if(xhr.readyState === 4) {if(xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {console.log(xhr.responseText); }}}// Process request parameters
postData = {"name1":"value1"."name2":"value2"};
postData = (function(value){
var dataString = "";
for(var key in value){
dataString += key+"="+value[key]+"&";
};
return dataString;
}(postData));
// Set the request header
xhr.setRequestHeader("Content-type"."application/x-www-form-urlencoded");
// Exception handling
xhr.onerror = function() {
console.log('Network request failed')}// Carry cookies across domains
xhr.withCredentials = true;
// Make a request
xhr.send(postData);
Copy the code
The following sections analyze the functions, attributes, and events commonly used by the XMLHttpRequest object.
function
open
Used to initiate a request. Usage:
xhr.open(method, url, async);
Copy the code
method
: Request mode, such asThe get and post
url
: the requesturl
async
: Indicates whether the request is asynchronous
send
This method is used to send an HTTP request, that is, the HTTP request will not be sent until it is called.
xhr.send(param)
Copy the code
param
: HTTP request parameter, which can beString, Blob
Such as type.
abort
This method is used to terminate an Ajax request. ReadyState is set to 0 after calling this method.
xhr.abort()
Copy the code
setRequestHeader
This method must be called between open() and send(). Usage:
xhr.setRequestHeader(header, value);
Copy the code
getResponseHeader
Gets the HTTP return header. If there are more than one of the same names in the return header, the value returned is a string separated by commas and Spaces. Usage:
var header = xhr.getResponseHeader(name);
Copy the code
attribute
readyState
Identifies the current state of the XMLHttpRequest object, which is always in one of the following states:
value | state | describe |
---|---|---|
0 | UNSENT |
The proxy was created, but has not yet been invokedopen() Methods. |
1 | OPENED |
open() Method has been called. |
2 | HEADERS_RECEIVED |
send() The method has been called, and the header and state are available. |
3 | LOADING |
Download;responseText Property already contains some data. |
4 | DONE |
The download operation is complete. |
status
Indicates the status of the HTTP request. The initial value is 0. If the server does not explicitly specify a status code, status is set to the default value of 200.
responseType
Indicates the data type of the response, and allows us to set it manually. If null, the default is text, which can have the following values:
value | describe |
---|---|
"" |
willresponseType Set to empty string versus set to"text" Same, is the default type (actually isDOMString ). |
"arraybuffer" |
response It’s a binary dataJavaScript ArrayBuffer 。 |
"blob" |
response It’s a binary dataBlob Object. |
"document" |
The response is aHTML Document orXML XMLDocument Depending on the MIME type of the received data. |
"json" |
response Is a JavaScript object. This object is created by treating the received data type asJSON Analytic. |
"text" |
response Is included in theDOMString Object. |
response
Returns the body of the response, of the type determined by the responseType above.
withCredentials
Ajax requests by default carry cookies for same-origin requests, while cross-domain requests do not. Setting the withCredentials attribute of XHR to true allows cross-domain cookies.
Event callback
onreadystatechange
xhr.onreadystatechange = callback;
Copy the code
Callback is triggered when the readyState property changes.
onloadstart
xhr.onloadstart = callback;
Copy the code
Before the Ajax request is sent (after readyState==1, before readyState==2), the callback will be triggered.
onprogress
xhr.onprogress = function(event){
console.log(event.loaded / event.total);
}
Copy the code
The function returns the total size of the loaded resource as loaded, which is used to calculate the loading progress.
onload
xhr.onload = callback;
Copy the code
Callback is triggered when a resource and its dependent resources have finished loading, and normally we process the return value in the onLoad event.
Exception handling
onerror
xhr.onerror = callback;
Copy the code
The callback is triggered when the Ajax resource fails to load.
ontimeout
xhr.ontimeout = callback;
Copy the code
Callback is triggered when progress terminates due to a predetermined time expiration, which can be set using the timeout attribute.
JQuery encapsulates Ajax
For a long time, people used jQuery’s Ajax wrapper for web requests, including $. Ajax, $. Get, $. Post, etc., which I still find useful today.
$.ajax({
dataType: 'json'.// Set the return value type
contentType: 'application/json'.// Set the parameter type
headers: {'Content-Type'.'application/json'},// Set the request header
xhrFields: { withCredentials: true }, // Carry cookies across domains
data: JSON.stringify({a: [{b:1.a:1}}),// Pass parameters
error:function(xhr,status){ // Error handling
console.log(xhr,status);
},
success: function (data,status) { // Get the result
console.log(data,status); }})Copy the code
$. Ajax takes a single parameter, which takes a set of configurations and wraps itself into a jqXHR object. Read the Jquary-Ajax source code
Common configurations:
url
Current page address. The address from which the request is sent.
type
Type: String Request type (“POST” or “GET”). The default is “GET”. Note: Other HTTP request methods, such as PUT and DELETE, are also available, but only supported by some browsers.
timeout
Type: Number Specifies the request timeout period (ms). This setting overrides global Settings.
success
Type: Function Callback Function after a successful request.
jsonp
Override the name of the callback function in a JSONP request. This value is used instead of the “callback=? The “callback” part of the URL parameter in this GET or POST request.
Error type: Function. This function is called when the request fails.
Note: Error determination in source code:
isSuccess = status >= 200 && status < 300 || status === 304;
Copy the code
Return values other than these status codes will carry an error callback.
dataType
"xml": Returns an XML document, which can be processed using jQuery."html": Returns plain text HTML information; The included script tag is executed when the DOM is inserted."script": Returns plain text JavaScript code. Results are not cached automatically. Unless it's set"cache"Parameters. Note: In the case of remote requests (not in the same domain), all POST requests are converted to GET requests. (Because it will be loaded using the DOM's script tag)"json"Returns theJSONThe data."jsonp": JSONP format. When a function is called using the JSONP form, for example"myurl? callback=?"JQuery will automatically replace? Is the correct function name to execute the callback function."text": Returns a plain text stringCopy the code
data
Type: String Uses json. stringify transcoding
complete
Type: Function Callback Function after request completion (called after request success or failure).
async
Type: Boolean Default value :true. By default, all requests are asynchronous. If you need to send synchronization requests, set this option to false.
contentType
Type: String Default: “Application/X-www-form-urlencoded”. Type of content encoding when sending information to the server.
This organization of key-value pairs is fine in general. This is generally done without nested JSON, i.e. simple JSON, which looks something like this:
{
a: 1.b: 2.c: 3
}
Copy the code
But in some complicated cases there is a problem. For example in the Ajax you to preach a complex json to like, also as object embedded arrays, including object in the array, you pass like this: application/x – WWW – form – urlencoded this form is that there is no way to organize the key value from complex json form.
{
data: {
a: [{
x: 2}}}]Copy the code
You can pass complex JSON objects as follows
$.ajax({
dataType: 'json'.contentType: 'application/json'.data: JSON.stringify({a: [{b:1.a:1}]})})Copy the code
Alternatives to jQuery
With the development of front-end MV* in recent years, people are using jQuery less and less. It is impossible for us to introduce it just to use jQuery’s Ajax API. Inevitably, we need to find a new technical solution.
In his document, Yu Creek recommends using Axios for web requests. Axios wraps the native XHR around Promise in a very elegant way. Axios also provides node support, making it the preferred solution for network requests.
In the future, there will certainly be more excellent encapsulation, which has very comprehensive consideration and detailed documentation. Here, we will not do more research, but focus on the underlying APIfetch.
The Fetch API is a powerful native API for accessing and manipulating HTTP pipes.
This functionality was previously implemented using XMLHttpRequest. Fetch provides a better alternative that can easily be used by other technologies, such as Service Workers. Fetch also provides a single logical location to define other HTTP-related concepts, such as CORS and HTTP extensions.
You can see that FETCH appears as an alternative to XMLHttpRequest.
With FETCH, you don’t need to load an additional external resource. But it’s not fully supported by browsers yet, so you’ll still need a Polyfill.
The use of FETCH
A basic fetch request:
const options = {
method: "POST".// Request parameters
headers: { "Content-Type": "application/json"}, // Set the request header
body: JSON.stringify({name:'123'}), // Request parameters
credentials: "same-origin"./ / cookie Settings
mode: "cors"./ / across domains
}
fetch('http://www.xxx.com',options)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson); // Response data
})
.catch(function(err){
console.log(err); // Exception handling
})
Copy the code
The Fetch API provides a global Fetch () method and several helper objects to initiate a network request.
fetch()
The fetch() method is used to initiate a request for a resource. It returns a promise that will be resolved after requesting a Response and returns a Response object.
Headers
You can create your own Headers object using the Headers() constructor, which is equivalent to the response/ Request Headers and allows you to query these Headers or do different things for different results.
var myHeaders = new Headers();
myHeaders.append("Content-Type"."text/plain");
Copy the code
Request
The Request() constructor creates a Request object that can be used as the second argument to the fetch function.
Response
Fetch () returns a Response instance after Promises are processed. You can also create a Response instance manually.
Fetch Polyfill source code analysis
Fetch is a very low-level API, so we cannot further explore its underlying level, but we can explore its basic principles and find out the pit points with the help of its polyfill.
The code structure
As you can see from the code, Polyfill encapsulates the four main objects provided by the Fetch API:
The fetch encapsulation
The code is very clear:
- To construct a
Promise
Object and return - To create a
Request
object - To create a
XMLHttpRequest
object - Take out the
Request
Requests in objectsurl
, sent by the requesting party,open
axhr
Request and willRequest
Object stored inheaders
Take out and assign to XHR xhr onload
After take outresponse
thestatus
,headers
,body
encapsulationResponse
Object, callresolve
.
Exception handling
As you can see, there are three possibilities for calling Reject:
-
1. The request timed out
-
2. The request fails
Note: OnError cannot be raised when you establish a profile with the server and receive an exception status code from the server such as 404, 500, etc. The onError flag is reject only when the network is faulty or the request is blocked. For example, if the network is trans-domain, the URL does not exist, or the network is abnormal, onError is triggered.
Therefore, fetch will enter THEN instead of Catch when receiving an abnormal status code. These error requests are often handled manually.
- 3. Manually terminate the service
You can pass the Signal object to the request argument and add the ABORT event listener to the signal object. When xhr.readyState changes to 4 (the response has been parsed), the abort event listener is removed.
This means that a FETCH request can be terminated by calling signal.abort before it ends. You can create a controller in a browser using the AbortController() constructor and then use the abortController.signal property
This is an experimental feature that is still under development in some browsers
Headers to encapsulate
A map object is maintained in a header object. The constructor can pass in a header object, an array, a header of a normal object type, and maintain all the values in the map.
ForEach: fetch forEach: forEach: forEach: forEach: forEach: forEach: forEach: forEach: forEach
See that the traversal of the header is the traversal of its internal map.
The Header also provides methods such as Append, DELETE, GET, and set, which operate on the internal map object.
The Request object
The two parameters received by the Request object are the two parameters received by the FETCH function. The first parameter can be passed either directly to the URL or to a constructed Request object. The second parameter is the Option object that controls the different configurations.
You can pass in attributes such as credentials, headers, method, mode, signal, and referrer.
Note here:
- The incoming
headers
Be used as aHeaders
Constructor to construct the Header object.
Cookie handling
Fetch also has the following code:
if (request.credentials === 'include') {
xhr.withCredentials = true
} else if (request.credentials === 'omit') {
xhr.withCredentials = false
}
Copy the code
The default credentials type is same-Origin, and they can carry the Coodkie of the same origin request.
Then I find that the implementation of polyfill here is inconsistent with MDN- using Fetch and a lot of data:
MDN: By default, FETCH does not send or receive any cookies from the server
Therefore, I tested the cases of using polyfill and native FETCH to carry cookies respectively, and found that both of them carry same-origin cookies by default when the credentials are not set, which is inconsistent with the description in the document. Fetch does not carry cookies by default. Here is how to use a native FETCH in a browser:
Then I noticed in MDN-fetch -Request that the default value of the new browser credentials has been changed to SAME-origin and the previous version is still omit.
It is true that MDN- using Fetch here the document is not updated in time, which is misleading…
The Response object
The Response object is the return value of a successful fetch call:
Review the operation on Response in fetch:
xhr.onload = function () {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || ' ')
}
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
Copy the code
Response constructor:
It can be seen that in the constructor, status, statusText, headers and URL of options are processed and mounted to the Response object respectively.
The responseText constructor does not explicitly handle the responseText function. The responseText constructor does not explicitly declare the _initBody property, and the responseText constructor calls the Body function. The _initBody function is mounted to Response via the Body function.
As you can see, the _initBody function depends on the type of xhr.response (Blob, FormData, String…). Assign values to different parameters that have different uses in the Body method. Here’s what else the Body function does:
The Body function also mounts four functions for the Response object, text, json, blob, and formData. The operations in these functions are to return different types of values from _initBody.
This also indicates that after the fetch is completed, the return value cannot be directly obtained in response, but must be obtained by calling functions such as text() and json().
One more point: several functions have logic like the following:
var rejected = consumed(this)
if (rejected) {
return rejected
}
Copy the code
Consumed function:
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
Copy the code
The bodyUsed variable is set to true after each call to text(), json(), etc., indicating that the return value has been read. The next call raises TypeError(‘Already read’). This also follows the principles of native FETCH:
Since the Responses objects were set up as stream, they could only be read once
X. The pit point of fetch
Fetch is described in the VUE documentation as follows:
There are a number of other considerations when using FETCH, which is why people still prefer Axios more at this point. Of course, that could change in the future.
Since FETCH is a very low-level API, it is not wrapped very much, and there are many issues to deal with:
- Can’t pass directly
JavaScript
Object as a parameter - You need to determine the type of return value and execute the method that gets the return value in response
- The return value method can only be called once, not more than once
- Failed to catch an exception properly
- Older browsers don’t carry it by default
cookie
- Does not support
jsonp
11. Fetch encapsulation
Request parameter handling
Different parameter types can be passed in:
function stringify(url, data) {
var dataString = url.indexOf('? ') = =- 1 ? '? ' : '&';
for (var key in data) {
dataString += key + '=' + data[key] + '&';
};
return dataString;
}
if (request.formData) {
request.body = request.data;
} else if (/^get$/i.test(request.method)) {
request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) {
request.headers.set('Content-Type'.'application/x-www-form-urlencoded; charset=UTF-8');
request.body = stringify(request.data);
} else {
request.headers.set('Content-Type'.'application/json; charset=UTF-8');
request.body = JSON.stringify(request.data);
}
Copy the code
Cookies carry
Fetch has started to carry same-origin cookie by default in the new version of the browser, but it will not carry same-origin cookie by default in the old version of the browser. We need to set it uniformly:
request.credentials = 'same-origin'; // Homologous carry
request.credentials = 'include'; // Can be carried across domains
Copy the code
Exception handling
When an HTTP status code representing an error is received, the Promise returned from fetch() is not marked as reject, even if the HTTP response’s status code is 404 or 500. Instead, it marks the Promise state as resolve (but sets the OK attribute of the return value of Resolve to false), and only as Reject when the network fails or the request is blocked.
Therefore, we need to handle fetch exceptions uniformly
.then(response= > {
if (response.ok) {
return Promise.resolve(response);
}else{
const error = new Error('Request failed! Status code:${response.status}, failure message:${response.statusText}`);
error.response = response;
return Promise.reject(error); }});Copy the code
Return value processing
Call the receiver function for different types of return values. The type must be determined in order to avoid multiple calls to the return value method:
.then(response= > {
let contentType = response.headers.get('content-type');
if (contentType.includes('application/json')) {
return response.json();
} else {
returnresponse.text(); }});Copy the code
jsonp
Fetch itself does not provide support for JSONP, and JSONP itself is not a very good way to solve cross-domain. It is recommended to use CORS or NginX to solve cross-domain. Please refer to the following sections for details.
Fetch is wrapped and ready to be used happily.
HMM, Axios works so well…
Xii. Cross-domain summary
When it comes to network requests, cross domains have to be mentioned.
The browser’s same-origin policy restricts how documents or scripts loaded from the same source can interact with resources from another source. This is an important security mechanism for isolating potentially malicious files. Read operations between different sources are generally not allowed.
Cross – domain conditions: protocol, domain name, port, if there is a difference is cross – domain.
Here are a few ways to work across domains:
nginx
Cross-domain implementation using nginx reverse proxies, see my article: NGINx Essentials for Front-end Developers
cors
CORS is a W3C standard, which stands for “Cross-origin Resource Sharing”. It allows the browser to issue XMLHttpRequest requests across source servers.
To enable CORS, set access-Control-allow-Origin on the server. This attribute indicates which domain names can access resources. If a wildcard is set, all websites can access resources.
app.all(The '*'.function (req, res, next) {
res.header("Access-Control-Allow-Origin"."*");
res.header("Access-Control-Allow-Headers"."X-Requested-With");
res.header("Access-Control-Allow-Methods"."PUT,POST,GET,DELETE,OPTIONS");
next();
});
Copy the code
jsonp
The link in the SRC attribute of the script tag can access cross-domain JS scripts. With this feature, the server no longer returns JSON data, but returns a piece of JS code that calls a function, which is called in SRC, so that cross-domain is achieved.
Jquery support for JSONP
$.ajax({
type : "get".url : "http://xxxx"
dataType: "jsonp".jsonp:"callback".jsonpCallback: "doo".success : function(data) {
console.log(data); }});Copy the code
Fetch, Axios, etc., do not provide direct support for JSONP. If this is needed, we can try manual encapsulation:
(function (window,document) {
"use strict";
var jsonp = function (url,data,callback) {
// 1. Convert the incoming data into a URL string
// {id:1,name:'jack'} => id=1&name=jack
var dataString = url.indexof('? ') = =- 1? '? ': '&';
for(var key in data){
dataString += key + '=' + data[key] + '&';
};
// 2 Handles callbacks in urls
// cbFuncName Specifies the name of the callback function: my_json_cb_ prefix + random number
var cbFuncName = 'my_json_cb_' + Math.random().toString().replace('. '.' ');
dataString += 'callback=' + cbFuncName;
// 3. Create a script tag and insert it into the page
var scriptEle = document.createElement('script');
scriptEle.src = url + dataString;
// 4. Mount callback function
window[cbFuncName] = function (data) {
callback(data);
// After processing the callback data, remove the jSONp script tag
document.body.removeChild(scriptEle);
}
document.body.appendChild(scriptEle);
}
window.$jsonp = jsonp; }) (window.document)
Copy the code
PostMessage cross-domain
The postMessage() method allows scripts from different sources to communicate asynchronously in a limited manner, enabling cross-text file, multi-window, cross-domain messaging.
/ / capture the iframe
var domain = 'http://scriptandstyle.com';
var iframe = document.getElementById('myIFrame').contentWindow;
// Send a message
setInterval(function(){
var message = 'Hello! The time is: ' + (new Date().getTime());
console.log('blog.local: sending message: ' + message);
//send the message and target URI
iframe.postMessage(message,domain);
},6000);
Copy the code
// Respond to events
window.addEventListener('message'.function(event) {
if(event.origin ! = ='http://davidwalsh.name') return;
console.log('message received: ' + event.data,event);
event.source.postMessage('holla back youngin! ',event.origin);
},false);
Copy the code
PostMessage is applicable to the following scenarios: Cross-domain communication between multiple Windows of a browser and cross-domain communication between IFrames.
WebSocket
WebSocket is a bidirectional communication protocol. After a connection is established, both the WebSocket server and client can actively send or receive data to each other without being restricted by the same Origin policy.
function WebSocketTest(){
if ("WebSocket" in window){
alert("Your browser supports WebSocket!");
// Open a Web socket
var ws = new WebSocket("ws://localhost:3000/abcd");
ws.onopen = function(){
// The Web Socket is connected, use the send() method to send data
ws.send("Send data");
alert("Data in transit...");
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
alert("Data received...");
};
ws.onclose = function(){
/ / close the websocket
alert("Connection closed...");
};
} else{
// Browsers do not support WebSocket
alert("Your browser does not support WebSocket!"); }}Copy the code
The article first
To read more excellent articles, or to read the mind mapping source files for articles, follow my Github blog at star✨.
Fundebug is a very useful BUG monitoring tool
If there are any mistakes in this article, please correct them in the comments section. Thank you for reading.
After concern public number reply [add group] pull you into high quality front end communication group.