The development history of XMLHttpRequest

At the heart of Ajax technology is the XMLHttpRequest object. We use an XMLHttpRequest object to send an Ajax request. This feature was first introduced by Microsoft, and other browser vendors have since provided the same implementation.

XMLHttpRequest has been so widely accepted that it has been standardized by the W3C and put forward the XMLHttpRequest standard. The XMLHttpRequest standard is divided into Level 1 and Level 2.

Not all browsers fully implement the XMLHttpRequest Level 2 specification, but all browsers implement some of its requirements.

XMLHttpRequest Level 1 has the following disadvantages:

  1. Cannot send binary files (such as pictures, video, audio, etc.), can only send plain text data.
  2. In the process of sending and obtaining data, you cannot obtain real-time progress information, but can only judge whether the data is completed.
  3. Cross-domain requests cannot be sent because of the same origin policy.

Level 2 Is an improvement on Level 1. The following functions are added to XMLHttpRequest Level 2:

  1. Cross-domain requests can be sent if the server allows it.
  2. Supports sending and receiving binary data.
  3. newformDataObject that supports sending form data.
  4. You can obtain progress information when sending and retrieving data.
  5. You can set the timeout period for the request.

The following line of code creates the XMLHttpRequest object.

const xhr = new XMLHttpRequest()
Copy the code

Compatibility query: caniuse.com/#search=XML…

XMLHttpRequest object sends request related API

Request header correlation

  • Accept: Type of content that the client can process. Such as:Accept: */*.
  • Accept-Charset: Type of character set that the client can handle. Such as:Accept-Charset: utf8.
  • Accept-Encoding: Compression encoding that the client can handle. Such as:Accept-Encoding: gzip, deflate, br.
  • Accept-Language: Indicates the language currently set by the client. Such as:Accept-Language: zh-CN,zh; Q = 0.9, en. Q = 0.8.
  • Connection: Indicates the type of connection between the client and the server. Such as:Connection: keep-alive.
  • Cookie: Any of the current page SettingsCookie.
  • Host: Domain where the request page is issued.
  • Referer: indicates the address of the source page of the currently requested page, that is, the current page is accessed through a link in this source page.
  • User-Agent: user agent character string of the client. Generally, information about the browser, browser kernel, and operating system is displayed.
  • Content-Type: The client tells the server what type of data is actually sent. Such as:Content-Type: application/x-www-form-urlencoded.

More references: developer.mozilla.org/zh-CN/docs/…

Open () method

The open() method is used to initialize a request.

The open() method takes three arguments:

  1. The first parametermethod: Type of request to send. Such asGET,POST,PUT,DELETEAnd so on.
  2. Second parameterurl: the requestURL.
  3. The third parameterasync: Boolean value for whether to send requests asynchronously.trueSend requests asynchronously.
const xhr = new XMLHttpRequest()
xhr.open('get'.'/userInfo'.true)
Copy the code

Calling the open() method does not actually send the request, but just starts a request ready to be sent.

The send () method

The send() method is used to send HTTP requests.

The send() method takes one argument:

  1. The first parameterdata: Data sent as the body of the request. If the data does not need to be sent through the request body, it must be passed innull. This parameter can accept strings,FormData,Blob,ArrayBufferSuch as type.
const xhr = new XMLHttpRequest()
xhr.send(null)
Copy the code

SetRequestHeader () method

The setRequestHeader() method sets custom request header information.

The setRequestHeader() method takes two arguments:

  1. The first parameterheader: The name of the header field.
  2. Second parametervalue: The value of the header field.

To successfully send the request header information, this method must be called between open() and send().

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.true)
xhr.setRequestHeader('MyHeader'.'MyValue')
xmlhttp.send()
Copy the code

ReadyState property and onReadyStatechange event

The readyState property represents the current active phase of the request/response process. The values of this attribute are as follows:

  • 0 (UNSENT) is not initialized. Has not yet calledopen()Methods.
  • 1 (OPENEDStart). Already callopen()Method, but not calledsend()Methods.
  • 2 (HEADERS_RECEIVEDSent). Already callsend()Method, but no response has been received.
  • 3 (LOADINGReceived). Partial response data has been received.
  • 4 (DONECompleted). All response data has been received.

An onReadyStatechange event is emitted whenever the value of the readyState property changes. Use this event to detect the value of readyState after each state change. Normally, only phases with readyState of 4 are processed, when all the data is ready.

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.true)
xhr.onreadystatechange = function () {
  if(xhr.readyState ! = = 4) {return  
  }
  if(xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText)
  }
}
xhr.send(null)
Copy the code

The timeout property and the onTimeout event

The timeout attribute indicates how many milliseconds the request has to wait for the response to terminate. If the browser has not received a response within the specified time, the onTimeout event handler is fired.

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.trueXhr.ontimeout = 3000 // After the request times out, the request automatically terminates and the onTimeout event handler xhr.ontimeout = is calledfunction(){
    console.log('Request timed out')
}
xhr.send(null)
Copy the code

OverrideMimeType () method

The overrideMimeType() method overrides the MIME type returned by the server to allow the browser to handle it differently.

If the data type returned by the server is TEXT/XML, and for some reason the browser fails to parse, the data will not be retrieved. To retrieve the raw data, we can change the MIME type to text/plain so that the browser doesn’t parse it automatically and we can retrieve the original text.

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.true)
xhr.overrideMimeType('text/plain')
xhr.send(null)
Copy the code

ResponseType attribute

The responseType property is a string representing the type of data returned by the server. Use the xhr.response attribute to receive.

This property is writable and can be set after the open() method is called but before the send() method to tell the server to return the specified type of data. If responseType is set to an empty string, this is equivalent to the default text.

The responseType property can be set to the following format types:

responseTypeThe value of the attribute responseThe data type of the property instructions
"" Stringstring The default value is equal totext(In no settingresponseTypeWhen)
"text" Stringstring The server returns text data
"document" Documentobject Hope to return toXMLWhen formatting data
"json" javaScriptobject IE10/IE11Does not support
"blob" Blobobject The server returns binary objects
"arrayBuffer" ArrayBufferobject The server returns a binary array

When setting responseType to a specific type, you need to make sure that the type returned by the server is compatible with the type you set to return. So if the two types are incompatible, the data returned by the server will become NULL, even if the server returned data.

Setting responseType to a synchronous request raises an InvalidAccessError exception.

Const XHR = new XMLHttpRequest() xhr.open()'get'.'/server/image.png'.true)
xhr.responseType = 'blob'
xhr.onload = function(e) {
  if (xhr.status >= 200 && xhr.status < 300) {
    const blob = this.response
    // ...
  }
}
xhr.send(null)
Copy the code

WithCredentials attribute

The withCredentials attribute is a Boolean value that indicates whether credentials (such as cookies, HTTP authentication, and client SSL certificates) are coaccompanied with cross-domain requests. The default is false.

If you need to send cookies across domain Ajax requests, you need to set the withCredentials attribute to true. If xhr.withCredentials are configured in the same domain, the effect is the same regardless of whether you configure true or false.

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.true)
xhr.withCredentials = true
xhr.send(null)
Copy the code

When the withCredentials are set to true, the response header access-Control-allow-origin must be added at the back end, and the domain name must be specified instead of *. Add the access-Control-allow-credentials header to true.

response.addHeader("Access-Control-Allow-Origin"."http://example.com")
response.addHeader("Access-Control-Allow-Credentials"."true")
Copy the code

Abort () method and onabort event

The abort() method is called to cancel the asynchronous request before the response is received. When a request is terminated, its readyState property is set to 0. After terminating the request, the XMLHttpRequeat object should also be dereferenced.

When abort() is called, the onabort event is fired.

const xhr = new XMLHttpRequest()
xhr.open('get'.'/server'.true)
xmlhttp.onabort = function () {
  console.log('Request aborted'} xmlhttp.send() // Calls the onabort callback function xmlhttp.abort() that we defined above.Copy the code

A GET request

Appending the query string argument to the end of the URL sends the information to the server.

The encoding mode of GET parameters cannot be manually interfered, which leads to different encoding modes of different browsers. Therefore, the most reliable scheme is manual pre-encoding and manual decoding, thus forbidding the interference of browser encoding.

Const XHR = new XMLHttpRequest() // encodeURIComponent() const tempParam = encodeURIComponent()'age')
const tempValue = encodeURIComponent('20')
xhr.open('get'.'/server? tempParam=tempValue&money=100'.true)
Copy the code

A POST request

The POST request submits the data as the body of the request. Here are four common ways to submit data in a POST request.

application/x-www-form-urlencoded

A browser’s native

form, if encType is not set, will eventually submit data as Application/X-www-form-urlencoded.

multipart/form-data

When a form uploads a file, the

form’s encType must be equal to multipart/form-data.

application/json

When sending an Ajax request, use application/ JSON as the request header to tell the server that the body of the message is a serialized JSON string.

text/xml

Using HTTP as the transport protocol and XML as the encoded remote invocation specification.

Simulate form submission using XMLHttpRequest

Set the Content-Type header to Application/X-www-form-urlencoded. You can use the XMLHttpRequest object to mimic form submission.

const xhr = new XMLHttpRequest()
xhr.open('post'.'/server'.true)
xhr.setRequestHeader('Content-Type'.'application/x-www-form-urlencoded')
const form = document.getElementById('myForm') // serialize() for the form serialization method xhr.send(serialize(form))Copy the code

You can also serialize FormData using XMLHttpRequest level 2 FormData.

const xhr = new XMLHttpRequest()
xhr.open('post'.'/server'.true)
const form = document.getElementById('myForm')
const formData = new FormData(form)
formData.append("id"."123456")
xhr.send(formData)
Copy the code

Using FormData you don’t have to explicitly set the request header on the XMLHttpRequest object. The XMLHttpRequest object recognizes that the incoming data type is an instance of FormData and configates the appropriate header information.

XMLHttpRequest Progress event-related API

onloadstart

Fired when the XMLHttpRequest object starts transmitting data, which is when the send() method is called (the HTTP request is sent).

xhr.onloadstart = function () {
  console.log('Start making requests... ')}Copy the code

onprogress

Fires continuously during the receiving of a response.

The onProgress event handler receives an Event object whose target attribute is an XMLHttpRequest object, and the event contains three additional attributes: Loaded, Total, and lengthComputable.

  1. event.loaded: Amount of data transferred (number of bytes received).
  2. event.total: Total amount of data (according toContent-LengthExpected number of bytes determined by the response header).
  3. event.lengthComputable: Boolean value for whether progress information is available.

With this information, you can create an Ajax request progress bar.

const xhr = new XMLHttpRequest()
xhr.onprogress = function (event) {
    if(! event.lengthComputable) {return console.log('Unable to calculate progress')} const percentComplete = event.loaded/event.total * 100 console.log(' percent of progress:${percentComplete}%`)
}
xhr.open('post'.'/server'.true)
xhr.send(null)
Copy the code

onerror

Triggered when a request error occurs. This event is triggered only when an exception occurs at the NetWork level. For an application-level exception, for example, the statusCode returned in the response is 4XX, which is not a NetWork Error event. Therefore, an onError event is triggered instead of an onError event.

xhr.onerror = function(e) {
 console.log('Data receiving error')}Copy the code

onabort

Triggered when the abort() method is called to terminate the request.

onload

Fired when the request is successful and the complete response data is received.

The onLoad event can be used instead of the readyStatechange event. Since the onLoad event is triggered when the response is received, there is no need to check the readyState property. Whenever the browser receives a response from the server, regardless of its state, the Load event is triggered.

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
  console.log('Data received completed')
  if(xhr.status >= 200 && xhr.status < 300) {
    console.log(xhr.responseText)
  }
}
xhr.open('post'.'/server'.true)

xhr.send(formData)
Copy the code

To ensure proper execution, the onProgress event handler must be added before calling the open() method.

onloadend

Fired after the request ends, either in success or failure, or after an ERROR, ABORT, or LOAD event is triggered.

xhr.onloadend = function(e) {
  console.log('Request ended, status unknown')}Copy the code

Each request starts with the triggering of a loadStart event, followed by one or more progress events, then one of error, ABORT, or Load events, and finally ends with the triggering of a loadEnd event.

The upload properties

XMLHttpRequest can send not only requests, but also files, Ajax file uploads.

After sending the file, you get an XMLHttpRequestUpload object using the xmlHttprequest.upload attribute. From this object, you can know the progress of the upload. The implementation is to listen for events on this object: onLoadStart, onProgress, onabort, onError, onLoad, onTimeout, onloadEnd.

When a file is uploaded, you can get the progress of the upload by specifying a listener for the progress event on the Upload attribute.

const xhr = new XMLHttpRequest()
if (xhr.upload) {
    xhr.upload.onprogress = function progress(e) {
        if (e.total > 0) {
            e.percent = e.loaded / e.total * 100
        }
    }
}
Copy the code

XMLHttpRequest object receives response related API

After receiving the response, the first step is to check the status property. To verify that the response has returned successfully. Use an HTTP status code of 200 as a sign of success. A status code of 304 indicates that the requested resource has not been modified, can be used directly with the cached version in the browser, and is considered valid.

Response header correlation

  • Content-Type: The server tells the client the type and character encoding of the response content. Such as:Content-Type: text/html; charset=utf-8.
  • Content-Length: The server tells the client the size of the response entity. Such as:Content-Length: 8368.
  • Content-Encoding: The server tells the client to return the compression encoding format. Such as:Content-Encoding: gzip, deflate, br.

More references: developer.mozilla.org/zh-CN/docs/…

The status attribute

The status property returns an integer representing the HTTP status code that the server responded to. If the server does not return a status code, this property defaults to 200. This property is 0 before the request is issued. This property is read-only.

if (xhr.readyState === 4) {
  if(xhr.status >= 200&&xhr.status < 300) {// Process the data returned by the server}}Copy the code

StatusText attribute

The statusText property returns a string representing the status statement sent by the server. Like OK and Not Found. The value of this property is an empty string until the request is sent. If the server does not return a status prompt, the value of this property defaults to OK. This property is read-only.

To detect the status attribute to determine what to do next, do not rely on statusText, which is less reliable across browsers.

The response properties

The Response property represents the data returned by the server. It can be any data type, such as string, object, binary objects, etc., the specific type by XMLHttpRequest. ResponseType attribute decision. This property is read-only.

This property is equal to null if the request is unsuccessful or the data is incomplete.

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    console.log(xhr.response)
  }
}
Copy the code

The responseText property

The responseText property returns the string received from the server, which is read-only.

if (xhr.readyState === 4) {
  if(xhr.status >= 200&& xhr.status < 300) {// Process the server's return data console.log(xhr.responsetext)}}Copy the code

ResponseXML property

If the content type of the response is “text/ XML “or “application/ XML”, this property holds the HTML or XML document object that contains the response data. This property is read-only.

Regardless of the content type, the content of the response body is stored in the responseText property. For non-XML data, the responseXML property will be null.

ResponseURL properties

The responseURL property is a string that represents the url of the server that sent the data. Returns an empty string if the URL is empty. If the URL has an anchor, the content after URL# is deleted. This property returns the url where the data was actually returned in the event of a server-side jump.

const xhr = new XMLHttpRequest()
xhr.open('GET'.'http://example.com/test'.true)
xhr.onload = function() {// return to http://example.com/test
  console.log(xhr.responseURL)
}
xhr.send(null)
Copy the code

GetResponseHeader () method

The getResponseHeader() method returns the value of the specified field in the HTTP header, or null if no response has been received from the server or if the specified field does not exist. The method’s arguments are case insensitive.

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
   console.log(xhr.getResponseHeader('Content-Type'))
}
xhr.open('post'.'/server'.true)
xhr.send(null)
Copy the code

If there are multiple fields with the same name, their values are concatenated into a string, separated by commas + Spaces.

The getAllResponseHeaders () method

The getAllResponseHeaders() method returns a string representing all HTTP headers sent by the server. The format is a string. Each header is separated by CRLF (carriage return + line feed). If no response is received from the server, this property is null. If a network error occurs, this property is an empty string.

const xhr = new XMLHttpRequest()
xhr.onload = function onload() {
 const responseHeaders = 'getAllResponseHeaders' in xhr ? xhr.getResponseHeaders() : null
}
xhr.open('post'.'/server'.true)
xhr.send(null)
Copy the code

The code above is used to get all the header information returned by the server. The return value might be a string like the following.

content-encoding: gzip\r\n
content-length: 2020\r\n
content-type: text/html; charset=utf-8\r\n
Copy the code

This string needs to be processed to be used correctly.

const str = 'date: Fri, 08 Dec 2017 21:04:30 GMT\r\n'
  + 'content-encoding: gzip\r\n'

function trim(str) {
  return str.replace(/^\s*/, ' ').replace(/\s*$/, ' ')}function parseHeaders(headers) {
  if(! headers) {return {}
  }
  const parsed = {}
  let key, val, i
  const arr = headers.split(/[\r\n]+/)
  arr.forEach((line) => {
    i = line.indexOf(':')
    key = trim(line.substr(0, i)).toLowerCase()
    val = trim(line.substr(i + 1))
    if (key) {
      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val
    }
  })
  return parsed
}
//{date: "Fri, 08 Dec 2017 21:04:30 GMT", content-encoding: "gzip"}
console.log(parseHeaders(str))
Copy the code

Refer to the article

Book.douban.com/subject/105…

Juejin. Cn/post / 684490…

Segmentfault.com/a/119000000…

www.html5rocks.com/zh/tutorial…

Imququ.com/post/four-w…

www.zhihu.com/question/28…

Javascript.ruanyifeng.com/bom/ajax.ht…