Comprehensive Analysis of Front-end Network Request Methods

  ajax, fetch, Front end, javascript, Network request

First, the front-end network request concerns

In most cases, we only need to pay attention to the following points when initiating a network request at the front end:

  • Pass in basic parameters (url, request method)
  • Request parameter, request parameter type
  • Set request header
  • How to Get Response
  • Acquiring a response head, a response state and a response result
  • exception handling
  • CarrycookieSet up
  • Cross domain request

Second, the front-end network request way

  • formForms,ifreamRefresh page
  • Ajax-Founder of Asynchronous Network Requests
  • jQuery-An era
  • fetchAjaxThe replacement of
  • axios、requestSuch as many open source libraries

III. Questions about Network Requests

  • AjaxThe emergence of the solved what problem
  • NativeAjaxHow to use
  • jQueryThe network request method of
  • fetchThe usage of the and pits
  • How to Use It Correctlyfetch
  • How to choose the appropriate cross-domain approach

With the above problems and concerns, we will conduct a comprehensive analysis of several network requests.

Fourth, what problems have Ajax solved?

InAjaxBefore it appeared,webThe program works like this:

image

The defects of this interaction are obvious. Any interaction with the server needs to refresh the page, and the user experience is very poor.AjaxThe emergence of the has solved this problem.AjaxFull nameAsynchronous JavaScript + XML(AsynchronousJavaScriptAndXML)

UseAjax, web applications can quickly present incremental updates on the user interface without reloading (refreshing) the entire page.

AjaxIt is not a new technology in itself, but is used to describe a technical scheme implemented by using a collection of existing technologies, the browser’sXMLHttpRequestIs to achieveAjaxThe most important objects (IE6The following usesActiveXObject)。

AlthoughXInAjaxChinese representativeXMLHowever, due toJSONMany of the advantages of, for example, lighter weight and asJavascriptPart of the currentJSONThe usage ratio ofXMLMore common.

V usage of native Ajax

The main analysis here isXMLHttpRequestObject, the following is a basic use of it:

var xhr = new XMLHttpRequest();
xhr.open('post','www.xxx.com',true)
//Receive 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 request header
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//exception handling
xhr.onerror = function() {
console.log('Network request failed')
}
//Cross-domain cookie
xhr.withCredentials = true;
//make a request
xhr.send(postData);

The following are respectively rightXMLHttpRequestAnalysis of functions, attributes and events commonly used by objects.

image

Function

open

Used to initialize a request, usage:

xhr.open(method, url, async);
  • method: request method, such asget、post
  • url: requestedurl
  • async: Is it an asynchronous request

send

For sendingHTTPRequest, that is, after calling the methodHTTPThe request will only be sent out.

xhr.send(param)
  • param: Parameters for http requests, which can bestring、BlobEqual type.

abort

Used to terminate oneajaxRequest, after calling this methodreadyStateWill be set to0, usage:

xhr.abort()

setRequestHeader

For settingHTTPRequest header, this method must be inopen()Methods andsend()Call between, usage:

xhr.setRequestHeader(header, value);

getResponseHeader

For obtaininghttpReturn header. If there are multiple identical names in the return header, the returned value will be a string separated by commas and spaces. Usage:

var header = xhr.getResponseHeader(name);

Attribute

readyState

Used to identify the currentXMLHttpRequestThe state of the object,XMLHttpRequestThe object is always in one of the following states:

value state describe
0 UNSENT The proxy was created but has not been calledopen()Methods.
1 OPENED open()The method has already been called.
2 HEADERS_RECEIVED send()The method has been called and the header and state are already available.
3 LOADING Downloading;responseTextProperty already contains some data.
4 DONE Download operation completed.

status

showhttpThe status of the request, with an initial value of0. If the server does not explicitly specify a status code, thenstatusWill be set to the default value, i.e.200.

responseType

Indicates the data type of the response and allows us to set it manually. If it is empty, the default istextType, can have the following values:

value describe
"" willresponseTypeSet to empty string and set to"text"Same, is the default type (actuallyDOMString)。
"arraybuffer" responseIs a containing binary dataJavaScript ArrayBuffer.
"blob" responseIs a containing binary dataBlobObject.
"document" Response is aHTML DocumentOrXML XMLDocumentDepending on the MIME type of data received.
"json" responseIs a JavaScript object. This object is created by treating the received data type asJSONFrom the analysis.
"text" responseIs contained inDOMStringThe text in the object.

response

Returns the body of the response, the type returned by the aboveresponseTypeDecision.

withCredentials

ajaxThe request will carry a homologous request by defaultcookieWhile cross-domain requests are not carriedcookie, settingsxhrThewithCredentialsThe property of istrueCross-domain portability will be allowedcookie.

Event callback

onreadystatechange

xhr.onreadystatechange = callback;

WhenreadyStateCallback is triggered when the property changes.

onloadstart

xhr.onloadstart = callback;

InajaxBefore the request is sent (readyState==1After that,readyState==2Before),callbackWill be triggered.

onprogress

xhr.onprogress = function(event){
console.log(event.loaded / event.total);
}

The callback function can obtain the total size of the resourcetotal, the size of the loaded resourceloadedWith these two values, the loading progress can be calculated.

onload

xhr.onload = callback;

Triggered when a resource and its dependent resources have finished loadingcallback, usually we will be inonloadThe return value is processed in the event.

exception handling

onerror

xhr.onerror = callback;

WhenajaxTriggered when a resource fails to loadcallback.

ontimeout

xhr.ontimeout = callback;

Triggered when progress is terminated due to expiration of the scheduled timecallback, timeout can be usedtimeoutProperty.

Six, jQuery Encapsulation of Ajax

For a long time, people usedjQueryProvidedajaxEncapsulating for network request, including$.ajax、$.get、$.postWait, I still think these methods are very practical now.

$.ajax({
 DataType: 'json', // Sets the return value type
 ContentType:' Application/JSON',//Set Parameter Type
 Headers: {'content-type',' application/JSON'},//set request headers
 Xhrfields: {with credentials: true},//cross-domain cookie
 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 results
 console.log(data,status);
 }
 })

$.ajaxOnly one parameter is received, which receives a series of configurations and encapsulates one of its ownjqXHRObject, you can read it if you are interested.JQuary-ajax source code

image

Common configurations:

url

Current page address. The address from which the request was sent.

type

Type:StringRequest mode ("POST"Or"GET"), the default is"GET". Note: OtherHTTPRequest methods such asPUTAndDELETEIt can also be used, but only some browsers support it.

timeout

Type:NumberSet the request timeout (milliseconds). This setting overrides the global setting.

success

Type:FunctionCallback function after successful request.

jsonp

In onejsonpRewrite the name of the callback function in the request. This value is used instead of"callback=?"suchGETOrPOSTIn requestURLOf the parameters"callback"Part of it.

errorType:Function. This function is called when the request fails.

Note: Error judgment in source code:

isSuccess = status >= 200 && status < 300 || status === 304;

The return value will enter in addition to these status codes.errorCallback.

dataType

"xml": returns an XML document that can be processed by jQuery.
"html": return plain text HTML information;  Contains script tags that are executed when dom is inserted.
"script": returns plain text JavaScript code.  Results will not be cached automatically.  Unless the "cache" parameter is set.  Note: When a remote request is made (not under the same domain), all POST requests will be converted to GET requests.  (because DOM's script tag will be used to load)
"json": returns JSON data.
"jsonp": JSONP format.  When a function is called in JSONP form, such as "myurl?  callback=?"  JQuery will be replaced automatically?  Is the correct function name to execute the callback function.
"text": Returns a plain text string

data

Type:StringUseJSON.stringifyTranscoding

complete

Type:FunctionCallback function after completion of request (called after success or failure of request).

async

Type:BooleanDefault value:true. By default, all requests are asynchronous. If you need to send a synchronization request, set this option tofalse.

contentType

Type:StringDefault value:"application/x-www-form-urlencoded". Content encoding type when sending information to server.

There is no problem with such an organization of key-value pairs under normal circumstances. What is said here is that there is no nested type.JSON, that is, simpleJSON, shaped like this:

{
a: 1,
b: 2,
c: 3
}

But there are problems in some complicated situations. For example, inAjaxYou are going to pass on a complicatedjsonFor example, it is said to be an array of embedded objects. The array includes objects. You pass it like this:application/x-www-form-urlencodedThere is no way for this form to be complicatedJSONOrganized into key-value pairs.

{
 data: {
 a: [{
 x: 2
 }]
 }
 }

ComplexjsonObject

$.ajax({
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({a: [{b:1, a:1}]})
})

Seven, jQuery’s replacement

In recent years, the front endMV*With the development and expansion of, people are using it less and less.jQueryWe cannot use it alonejQueryTheAjax apiTo introduce him alone, inevitably, we need to find new technical solutions.

Evan You recommended it in his document.axiosMake a network request.axiosBased onPromiseFor nativeXHRIt is fully packaged and used in a very elegant way. In addition,axiosAlso provided innodeSupport under the environment is the preferred solution for network requests.

There will definitely be better packaging in the future. They have very thorough consideration and detailed documents. We don’t do much research here. We focus on the lower API.fetch.

Fetch APIIs a powerful native API for accessing and manipulating HTTP pipes.

This function was previously implemented using XMLHttpRequest. Fetch provides a better alternative and can be easily 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.

VisiblefetchYes asXMLHttpRequestFor the first time.

Usefetch, you don’t need to load an additional external resource. But it is not yet fully supported by browsers, so you still need onepolyfill.

VIII. Use of fetch

A basic fetch request:

const options = {
 Method: "POST", // request parameters
 Headers: {"content-type": "application/JSON"},//set request header
 Body: JSON. stringfy ({name:' 123'}),//request parameters
 Credentials: "same-origin",//cookie settings
 Mode: "cors", // Cross-domain
 }
 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
 })

Fetch APIProvides a globalfetch()Method and several auxiliary objects to initiate a network request.

image

  • fetch()

fetch()The method is used to initiate a request to acquire resources. It returns apromiseThispromiseAfter the request response is receivedresolveAnd returnsResponseObject.

  • Headers

ThroughHeaders()Constructor to create your ownheadersObject, equivalent toresponse/requestThe header information can enable you to query the header information or do different operations for different results.

var myHeaders = new Headers();
 myHeaders.append("Content-Type", "text/plain");
  • Request

viaRequest()The constructor can create aRequestObject that can be used as afetchThe second argument of the function.

  • Response

Infetch()Finish processingpromisesThen return oneResponseInstance, you can also create one manuallyResponseExamples.

Nine, fetch polyfill source code analysis

Due tofetchIs a very low levelAPI, so we can’t further explore its underlying, but we can use itpolyfillExplore its basic principles and find out the pits.

Code structure

image

As can be seen from the code,polyfillMainly toFetchFour objects provided by API are encapsulated:

Fetch package

image

The code is very clear:

  • Construct aPromiseObject and returns
  • Create aRequestObject
  • Create aXMLHttpRequestObject
  • Take outRequestRequest in objecturl, the requesting party sends,openOnexhrRequest and willRequestObject stored in theheadersTake out the assignment to xhr
  • xhr onloadTake out afterresponseThestatusheadersbodyEncapsulationResponseObject, callingresolve.

exception handling

image

As you can see, callingrejectThere are three possibilities:

  • 1. The request timed out
  • 2. The request failed

Note: when establishing a brief introduction with the server and receiving the abnormal status code of the server, such as404、500Wait does not triggeronerror. Only when the network fails or the request is blocked will it be marked asreject, such as cross domain,urlDoes not exist, network anomaly, etc. will triggeronerror.

Therefore, fetch will enter then instead of catch when receiving abnormal status codes. These erroneous requests are often handled manually.

  • 3. Manual termination

Can be found inrequestIncoming parameterssignalObject, and tosignalObject additionabortEvent monitoring, whenxhr.readyStatebecome4(Response Content Parsing Completed) Remove abort Event Monitoring of signal Object.

This means that in onefetchYou can call before the request ends.signal.abortTerminate it. Can be used in the browserAbortController()The constructor creates a controller and then uses theAbortController.signalAttribute

This is an experimental function. Some browsers for this function are still under development.

Headers package

image

One is maintained in the header objectmapObject that can be passed into the constructorHeaderObject, array, common object typeheaderAnd maintain all values tomapChina.

BeforefetchThe call is seen in the functionheaderTheforEachMethod, the following is its implementation:

image

VisibleheaderThe traversal of is its interiormapThe traversal of the.

In additionHeaderAlso provided areappend、delete、get、setSuch as method, are internal to itmapObject.

collectionlpropertylmethod] variable

image

RequestThe two parameters that the object receives arefetchFunction receives two parameters, the first parameter can be passed directlyurlYou can also pass a constructedrequestObject. The second parameter controls different configurationsoptionObject.

Can be passed incredentials、headers、method、mode、signal、referrerEqual attribute.

Note here:

  • incidentheadersBe treated asHeadersConstructor to construct a header object.

Cookie processing

The fetch function also has the following code:

if (request.credentials === 'include') {
xhr.withCredentials = true
} else if (request.credentials === 'omit') {
xhr.withCredentials = false
}

DefaultcredentialsType issame-origin, you can carry the coodkie of the same origin request.

Then I found out that the implementation of polyfill here andMDN- use FetchAnd a lot of data are inconsistent:

Mdn: By default, fetch will not send or receive any cookies from the server.

So I tested the next use separatelypolyfillAnd the use of nativefetchThe case of carrying a cookie is found in not settingcredentialsUnder the condition of incredibly are carrying homologous by defaultcookieYes, this is not consistent with the description in the document. After consulting many materials, it is said thatfetchCookie will not be carried by default, the following is the use of nativefetchWhen the browser makes a request:

image

And then I found out that inMDN-Fetch-RequestA new version of the browser has been pointed out.credentialsThe default value has been changed tosame-origin, the old version is stillomit.

IndeedMDN- use FetchSome of the documents here are not updated in time, which is misleading. …

Response object

ResponseThe object isfetchReturn value after successful call:

Looking backfetchRightAction for Response’:

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))
}

ResponseConstructor:

image

It can be seen that in the constructor the main pairoptionshit the targetstatus、statusText、headers、urlAfter being processed and mounted to theResponseObject.

There is no pair in the constructorresponseTextClear processing, finally handed over to the_initBodyFunction processing, whileResponseThere is no voluntary statement_initBodyProperty, code last usedResponseCalledBodyFunction, in fact_initBodyThe function passes throughBodyThe function is mounted to theResponseTake a look at what’s on your body first._initBodyFunctions:

image

Obviously,_initBodyFunction basisxhr.responseType of (Blob、FormData、String ...To assign values to different parametersBodyThe method has been applied in different ways. Let’s take a look at it in detail.BodyWhat other operations did the function do?

image

BodyFunction is alsoResponseThe object mounts four functions,text、json、blob、formDataThe operation in these functions is to return different types of return values obtained in _initBody.

This also shows that infetchAfter the completion of the implementation, not directly inresponseMust be called to get the return value fromtext()、json()Wait for the function to get the return value.

There is another point to be made here: several functions have logic similar to the following:

var rejected = consumed(this)
if (rejected) {
return rejected
}

Consumed function:

function consumed(body) {
 if (body.bodyUsed) {
 return Promise.reject(new TypeError('Already read'))
 }
 body.bodyUsed = true
 }

Every calltext()、json()After waiting for the function, thebodyUsedVariable becomestrue, used to identify the return value has been read, the next read directly thrownTypeError('Already read'). This also follows the originalfetchThe principle of:

Because the Responses objects are set to the stream mode, they can only be read once.

Ten, fetch pit

VUEThe rightfetchThere is the following description:

UsefetchThere are many other matters needing attention, which is why everyone still prefers it at this stage.axiosMore. Of course, this matter may change in the future.

Due tofetchIs a very low levelAPI, it has not been packaged in many ways, and there are still many problems to deal with:

  • Not directly deliveredJavaScriptObject as parameter
  • You need to judge the type of return value and execute the method of obtaining the return value in response.
  • The method to get the return value can only be called once, not many times.
  • Unable to normally capture exception
  • The old browser will not be carried by default.cookie
  • nonsupportjsonp

XI. Packaging fetch

Request parameter processing

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);
 }

Cookie carrying

fetchIn the new version of the browser has begun to carry homologous defaultcookieHowever, it will not be carried by default in the old browser. We need to set it uniformly:

request.credentials =  'same-origin';  //homologous carrying
 request.credentials =  'include';  //can be carried across domains

exception handling

When an HTTP status code representing an error is received, the Promise returned from fetch () will not be marked reject, even if the status code of the HTTP response is 404 or 500. Instead, it marks the Promise status as resolve (but sets the ok attribute of the return value of resolve to false) and will mark reject only when the network fails or the request is blocked.

So we have to be rightfetchThe exception of the unified processing

.then(response => {
if (response.ok) {
return Promise.resolve(response);
}else{
Const error = new Error (`request failed!  Status code: ${response.status}, failure information: ${response.statusText} `;
error.response = response;
return Promise.reject(error);
}
});

Return value processing

Call different functions to receive different return value types. You must judge the type in advance and cannot call the method to obtain the return value many times:

.then(response => {
 let contentType = response.headers.get('content-type');
 if (contentType.includes('application/json')) {
 return response.json();
 } else {
 return response.text();
 }
 });

jsonp

fetchIt does not provide the rightjsonpThe support of,jsonpIt does not belong to a very good way to solve cross-domain problems. It is recommended to use it.corsOr ..nginxTo solve cross-domain problems, please refer to the following sections.

Fetch is packaged and can be used happily.

Well, axios works really well. …

XII. Cross-domain Summary

When it comes to network requests, we have to mention cross-domain.

The browser’s homology policy restricts how documents or scripts loaded from the same source 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 condition: if there is a difference between protocol, domain name and port, cross-domain is considered.

Here are some ways to solve cross-domain problems:

nginx

UsenginxReverse proxy implements cross-domain, refer to my article:Nginx knowledge for front-end developers

cors

CORSIs aW3CStandard, full name is “cross-domain resource sharing”(Cross-origin resource sharing). It allows browsers to sendXMLHttpRequestRequest.

Server settingsAccess-Control-Allow-OriginCan be openedCORS. This attribute indicates which domain names can access resources, and if wildcards are set, all websites can access resources.

app.all('*', 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();
 });

jsonp

scriptTaggedsrcLinks in the property can access cross-domainjsScript, using this feature, the server will not return any moreJSONFormat of data, but return a call to a functionjsCode, insrcIn this way, cross-domain is realized.

jqueryYesjsonpSupport for:

$.ajax({
 type : "get",
 url : "http://xxxx"
 dataType: "jsonp",
 jsonp:"callback",
 jsonpCallback: "doo",
 success : function(data) {
 console.log(data);
 }
 });

fetch、axiosSuch as did not directly provide the rightjsonp, if you need to use this method, we can try manual packaging:

(function (window,document) {
 "use strict";
 var jsonp = function (url,data,callback) {
 
 // 1. Convert the incoming data data into url string form
 // {id:1,name:'jack'} => id=1&name=jack
 var dataString = url.indexof('?'  ) == -1?   '?'  : '&';
 for(var key in data){
 dataString += key + '=' + data[key] + '&';
 };
 
 // 2 Handle callback function in url
 //name of cbfuncname callback function: prefix of my_json_cb_ name+random number (decimal point removed)
 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 data of the callback function, delete the script tag of jsonp
 document.body.removeChild(scriptEle);
 }
 
 document.body.appendChild(scriptEle);
 }
 
 window.$jsonp = jsonp;
 
 })(window,document)

PostMessage cross-domain

postMessage()The method allows scripts from different sources to carry out limited communication in an asynchronous manner, and can realize cross-text file, multi-window and cross-domain message transmission.

//capture iframe
var domain = 'http://scriptandstyle.com';
var iframe = document.getElementById('myIFrame').contentWindow;

//Send 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);
//Response Event
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);

postMessageCross-domain is applicable to the following scenarios: cross-domain communication between multiple windows with browser,iframeInter-domain communication.

WebSocket

WebSocketIt is a two-way communication protocol. After the connection is established,WebSocketTheserverAndclientCan actively send or receive data to each other without being limited by homologous policies.

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(){
 // Web Socket is connected, and send () method is used to send data
 Ws.send ("sending data");
 Alert ("in data transmission ...");
 };
 ws.onmessage = function (evt) {
 var received_msg = evt.data;
 Alert ("Data Received ...");
 };
 ws.onclose = function(){
 //close websocket
 Alert ("Connection Closed ...");
 };
 } else{
 //the browser does not support WebSocket.
 Alert ("your browser does not support WebSocket!"  );
 }
 }

If there are any mistakes in the article, please correct them in the comment area. Thank you for reading.

Recommended for everyoneFundebug, a very useful BUG monitoring tool ~

The article started

bVboR1p?w=476&h=222