Back to Home

XMLHttpRequest

XMLHttpRequest is a built-in browser object that allows to make HTTP requests in JavaScript. Despite having the word “XML” in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more. Right now, there’s another, more modern method fetch, that somewhat deprecates XMLHttpRequest. In modern web-development XMLHttpRequest is used for three reasons: 1. Historical reasons: we need to support existing scripts with XMLHttpRequest. 2. We need to support old browsers, and don’t want polyfills (e.g. to keep scripts tiny). 3. We need something that fetch can’t do yet, e.g. to track upload progress. Does that sound familiar? If yes, then all right, go on with XMLHttpRequest. Otherwise, please head on to info:fetch.

The basics

XMLHttpRequest has two modes of operation: synchronous and asynchronous. Let’s see the asynchronous first, as it’s used in the majority of cases. To do the request, we need 3 steps: 1. Create XMLHttpRequest:

let xhr = new XMLHttpRequest();

The constructor has no arguments. 2. Initialize it, usually right after new XMLHttpRequest:

xhr.open(method, URL, [async, user, password])

This method specifies the main parameters of the request: - method – HTTP-method. Usually “GET” or “POST”. - URL – the URL to request, a string, can be URL object. - async – if explicitly set to false, then the request is synchronous, we’ll cover that a bit later. - user, password – login and password for basic HTTP auth (if required). Please note that open call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of send. 3. Send it out.

xhr.send([body])

This method opens the connection and sends the request to server. The optional body parameter contains the request body. Some request methods like GET do not have a body. And some of them like POST use body to send the data to the server. We’ll see examples of that later. 4. Listen to xhr events for response. These three events are the most widely used: - load – when the request is complete (even if HTTP status is like 400 or 500), and the response is fully downloaded. - error – when the request couldn’t be made, e.g. network down or invalid URL. - progress – triggers periodically while the response is being downloaded, reports how much has been downloaded.

xhr.onload = function() {
alert(Loaded: ${xhr.status} ${xhr.response});
xhr.onerror = function() { // only triggers if the request couldn't be made at all
alert(Network Error);
xhr.onprogress = function(event) { // triggers periodically
// event.loaded - how many bytes downloaded
// event.lengthComputable = true if the server sent Content-Length header
// event.total - total number of bytes (if lengthComputable)
alert(Received ${event.loaded} of ${event.total});
status

HTTP status code (a number): 200, 404, 403 and so on, can be 0 in case of a non-HTTP failure. statusText

HTTP status message (a string): usually OK for 200, Not Found for 404, Forbidden for 403 and so on. response (old scripts may use responseText)

The server response body. We can also specify a timeout using the corresponding property: If the request does not succeed within the given time, it gets canceled and timeout event triggers. let url = new URL(’https://google.com/search’); url.searchParams.set(‘q’, ‘test me!’); // the parameter ‘q’ is encoded xhr.open(‘GET’, url); // https://google.com/search?q=test+me%21

Response Type

We can use xhr.responseType property to set the response format:

Sets the request header with the given name and value. For instance:


xhr.setRequestHeader('Content-Type', 'application/json');

Several headers are managed exclusively by the browser, e.g. Referer and Host.
The full list is in the specification-method).
XMLHttpRequest is not allowed to change them, for the sake of user safety and correctness of the request.
warn header="Can't remove a header" Another peculiarity of XMLHttpRequest is that one can't undo setRequestHeader. Once the header is set, it's set. Additional calls add information to the header, don't overwrite it. For instance: ```js xhr.setRequestHeader('X-Auth', '123'); xhr.setRequestHeader('X-Auth', '456'); // the header will be: // X-Auth: 123, 456 ``` getResponseHeader(name)

Gets the response header with the given name (except Set-Cookie and Set-Cookie2). For instance:


xhr.getResponseHeader('Content-Type')
getAllResponseHeaders()

Returns all response headers, except Set-Cookie and Set-Cookie2. Headers are returned as a single line, e.g.:

Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT

The line break between headers is always “

” (doesn’t depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space “: “. That’s fixed in the specification. So, if we want to get an object with name/value pairs, we need to throw in a bit JS. Like this (assuming that if two headers have the same name, then the latter one overwrites the former one):

let headers = xhr
.getAllResponseHeaders()
.split('\
\
')
.reduce((result, current) => {
let [name, value] = current.split(': ');
result[name] = value;
return result;
}, {});
// headers['Content-Type'] = 'image/png'

POST, FormData

To make a POST request, we can use the built-in FormData object. The syntax: We create it, optionally fill from a form, append more fields if needed, and then:

  1. xhr.open(‘POST’, …) – use POST method.

  2. xhr.send(formData) to submit the form to the server. For instance: The form is sent with multipart/form-data encoding. Or, if we like JSON more, then JSON.stringify and send as a string. Just don’t forget to set the header Content-Type: application/json, many server-side frameworks automatically decode JSON with it: The .send(body) method is pretty omnivore. It can send almost any body, including Blob and BufferSource objects.

    Upload progress

    The progress event triggers only on the downloading stage. That is: if we POST something, XMLHttpRequest first uploads our data (the request body), then downloads the response. If we’re uploading something big, then we’re surely more interested in tracking the upload progress. But xhr.onprogress doesn’t help here. There’s another object, without methods, exclusively to track upload events: xhr.upload. It generates events, similar to xhr, but xhr.upload triggers them solely on uploading:

// 1. Create a new XMLHttpRequest object
let xhr = new XMLHttpRequest();

// 2. Configure it: GET-request for the URL /article/.../load
xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. Send the request over the network
xhr.send();

// 4. This will be called after the response is received
xhr.onload = function() {
  if (xhr.status != 200) { // analyze HTTP status of the response
    alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
  } else { // show the result
    alert(`Done, got ${xhr.response.length} bytes`); // response is the server response
  }
};

xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    alert(`Received ${event.loaded} of ${event.total} bytes`);
  } else {
    alert(`Received ${event.loaded} bytes`); // no Content-Length
  }

};

xhr.onerror = function() {
  alert("Request failed");
};
Example:

Follow the lesson from Microsoft Web-Dev-For-Beginners course

Tags: web,development