XMLHttpRequest Older Way of Handling HTTP Requests

This guide introduces you to XMLHttpRequest, the older way of handling HTTP requests in JavaScript. You will learn how to create and use XMLHttpRequest, send different types of requests, handle responses, and manage security considerations.

Introduction to XMLHttpRequest

What is XMLHttpRequest?

XMLHttpRequest, often abbreviated as XHR, is a built-in browser object that allows you to interact with servers. It can send HTTP or HTTPS requests to a server asynchronously, and it allows you to handle responses from the server without reloading the page. XHR is a cornerstone of AJAX (Asynchronous JavaScript and XML), a technique used to create dynamic and interactive web applications.

Why Use XMLHttpRequest?

Before the introduction of Fetch API and modern web technologies, XMLHttpRequest was the primary method to perform AJAX operations. Understanding XHR is important because it helps you grasp the fundamentals of web communication, client-server interactions, and asynchronous programming.

Basic Usage Example

Let's start with a simple example of using XMLHttpRequest to fetch data from a server.

// Create a new XMLHttpRequest object
var xhr = new XMLHttpRequest();

// Configure it: GET-request for the URL /info
xhr.open('GET', '/info', true);

// Set up a function to handle the response
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) { // check if the request was successful
    console.log('Response:', xhr.responseText); // responseText is the server response as a string
  } else {
    console.error('Error:', xhr.statusText); // statusText holds a text message corresponding to the status code
  }
};

// Send the request
xhr.send();

In this example, we create an XHR object, configure it to make a GET request to the server, set up an event handler to handle the response, and finally, send the request.

Setting Up the XMLHttpRequest Object

Creating XMLHttpRequest Instance

To use XMLHttpRequest, you first need to create an instance of it.

var xhr = new XMLHttpRequest();

This line of code initializes a new XHR object that you can use to send requests to the server.

Opening a Request

The open method initializes a request. It takes three main parameters: the request method, the URL, and a boolean indicating whether the request should be asynchronous.

xhr.open('GET', '/info', true);
  • GET: This is the HTTP method. GET is used to request data from a server.
  • /info: This is the URL from which you want to fetch data.
  • true: This boolean parameter specifies that the request should be asynchronous.

HTTP Methods (GET, POST, PUT, DELETE)

  • GET: Requests data from a specified resource.
  • POST: Submits data to be processed to a specified resource.
  • PUT: Replaces all current representations of the target resource with the uploaded content.
  • DELETE: Removes a specific resource.

URL Parameter

The URL parameter specifies the location of the server or resource you want to access.

Asynchronous Parameter

The boolean parameter true makes the request asynchronous. If set to false, the request is synchronous, meaning the code execution will pause until the server responds, which is generally not recommended for a smooth user experience.

Sending a Request

GET Requests

GET requests are used to retrieve data from a server.

Example of a GET Request

Let's fetch some JSON data from a public API.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.send();

In this example, we send a GET request to a public API and log the JSON response to the console.

POST Requests

POST requests are used to send data to a server.

Setting Request Headers

Before sending a POST request, you often need to set the Content-Type header.

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

Sending Data with POST Requests

You can send data with POST requests using the send method.

Example of a POST Request

Let's send some data to a server using a POST request.

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.send(JSON.stringify({
  title: 'foo',
  body: 'bar',
  userId: 1
}));

In this example, we send a POST request to a public API with a JSON object and log the response.

Handling Responses

Receiving the Response

The response from the server can be accessed through the responseText or response property of the XHR object.

Response Properties

  • responseText: Contains the response data as a string.
  • responseType: Lets you specify the expected response type (e.g., text, arraybuffer, blob, document, json).

Status Codes

The server responds with a status code that indicates the result of the request.

Checking Response Status

You can check the response status to determine if the request was successful.

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Success:', xhr.responseText);
  } else {
    console.error('Error:', xhr.statusText);
  }
};

Handling Success

If the request was successful, you can handle the response as needed.

Handling Errors

If the request failed, you can handle the error appropriately.

Ready State Change

Understanding readyState

The readyState property describes the current status of the request and its response. It has five possible values:

  • 0: UNSENT - The request has been set up, but not yet sent.
  • 1: OPENED - The open method has been called. At this point, the request is ready to be sent but has not been sent yet.
  • 2: HEADERS_RECEIVED - The send method has been called, and the response headers and status are available.
  • 3: LOADING - The response body is being downloaded. The responseText property contains the partial data.
  • 4: DONE - The request is complete, and the response is ready to be accessed.

Handling readyState Changes

You can handle changes in the readyState using the onreadystatechange event handler.

Example of readyState Handling

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) { // Check if the request is complete
    if (xhr.status >= 200 && xhr.status < 300) { // Check if the request was successful
      console.log('Data:', JSON.parse(xhr.responseText));
    } else {
      console.error('Error:', xhr.statusText);
    }
  }
};

xhr.send();

In this example, we handle the readyState change to determine when the request is complete and whether it was successful.

Aborting a Request

Using abort()

The abort() method can be used to cancel a request.

Example of Aborting a Request

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.onerror = function() {
  console.error('Network Error');
};

xhr.send();

// Abort the request after 5 seconds
setTimeout(function() {
  xhr.abort();
}, 5000);

In this example, we send a request and abort it after 5 seconds using the setTimeout function.

Cross-Origin Requests

Understanding Cross-Origin Resource Sharing (CORS)

CORS is a security feature implemented by web browsers that allows a server to specify which domain(s) can access its resources.

Setting up CORS on the Server

To allow cross-origin requests, the server must include specific headers in its response.

Handling preflight Requests

When making "complex" requests, the browser performs a preflight request to determine if the server allows the request.

Example of CORS with XMLHttpRequest

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://example.com/data', true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.onerror = function() {
  console.error('Network Error');
};

xhr.send(JSON.stringify({
  name: 'John',
  age: 30
}));

In this example, we send a POST request to a different domain, demonstrating how CORS works in practice.

XMLHttpRequest Event Handlers

Event Types

XHR objects can trigger various events. Here are some of the most common:

onload

Triggered when the request is complete and successful.

onerror

Triggered when the request encounters a network error.

onabort

Triggered when the request is aborted with the abort() method.

onreadystatechange

Triggered every time the readyState changes.

onprogress

Triggered periodically to report the progress of the request.

Adding Event Handlers

You can attach event handlers using both inline methods and addEventListener.

Inline Event Handlers

xhr.onload = function() {
  console.log('Request completed successfully');
};

Using addEventListener

xhr.addEventListener('load', function() {
  console.log('Request completed successfully');
});

Example of Adding Event Handlers

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

xhr.onload = function() {
  console.log('Data:', JSON.parse(xhr.responseText));
};

xhr.onerror = function() {
  console.error('Request failed');
};

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

xhr.send();

In this example, we set up multiple event handlers to handle different events triggered by the XHR object.

Modifying Request Headers

Understanding Request Headers

Headers provide additional information about the request or response. You can set request headers using the setRequestHeader method.

Setting Request Headers

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.setRequestHeader('Authorization', 'Bearer your_access_token_here');

Common Headers

  • Content-Type: Specifies the media type of the resource.
  • Authorization: Provides credentials for HTTP authentication.

Accessing Response Headers

You can access response headers using the getAllResponseHeaders and getResponseHeader methods.

getAllResponseHeaders()

xhr.onload = function() {
  console.log(xhr.getAllResponseHeaders()); // Logs all response headers
};

getResponseHeader(headerName)

xhr.onload = function() {
  console.log(xhr.getResponseHeader('Content-Type')); // Logs the Content-Type header
};

Example of Modifying Headers

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.send();

In this example, we set the Content-Type header and handle the response.

Uploading Files with XMLHttpRequest

File Input

To upload files, you often use an HTML file input element.

FormData Object

The FormData API provides a way to easily construct sets of key/value pairs to represent form fields and their values.

Example of File Upload

<input type="file" id="fileInput">
<button id="uploadButton">Upload</button>

<script>
document.getElementById('uploadButton').addEventListener('click', function() {
  var fileInput = document.getElementById('fileInput');
  var file = fileInput.files[0];
  var formData = new FormData();
  formData.append('file', file);

  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/upload', true);

  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('Upload successful:', xhr.responseText);
    } else {
      console.error('Error:', xhr.statusText);
    }
  };

  xhr.onerror = function() {
    console.error('Network Error');
  };

  xhr.send(formData);
});
</script>

In this example, we use an HTML file input to select a file, create a FormData object, and send it using a POST request.

Security Considerations

Potential Security Vulnerabilities

  • Cross-Site Scripting (XSS): Attacker-injected scripts manipulate web pages.
  • Cross-Site Request Forgery (CSRF): Attacker forces a victim to perform unwanted actions.

Security Best Practices

  • Sanitizing Inputs: Always sanitize input data to prevent XSS attacks.
  • Using CORS Properly: Configure CORS to allow only trusted domains.

Example of Secure XMLHttpRequest

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);

xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Data:', JSON.parse(xhr.responseText));
  } else {
    console.error('Error:', xhr.statusText);
  }
};

xhr.onerror = function() {
  console.error('Network Error');
};

xhr.send();

In this example, we send a secure GET request to a server.

Comparing XMLHttpRequest and Fetch API

Key Differences

  • Simplified Syntax: Fetch API has a more modern, promise-based syntax.
  • Promises-based: Fetch API uses promises, making it easier to work with asynchronous code.
  • Methods and Options: Fetch API supports more options for configuring requests.
  • Legacy Support: XHR is supported in all browsers, including older versions of Internet Explorer.

When to Use XMLHttpRequest

Use XMLHttpRequest when you need to support very old browsers or when you want finer control over your requests.

Summary and Next Steps

Recap of Key Points

  • XMLHttpRequest is a built-in browser object for making HTTP requests.
  • You can set various request headers and handle different response types.
  • Properly handle CORS and security considerations to protect your application.
  • Fetch API is the modern alternative to XHR, offering a more modern and promise-based approach.

Moving Forward with Asynchronous JavaScript

Now that you understand the basics of XMLHttpRequest, you can explore more advanced topics such as the Fetch API, which provides a more modern approach to handling HTTP requests. Learning JavaScript and its capabilities for asynchronous operations will greatly enhance your ability to build dynamic and interactive web applications.

By understanding and mastering XMLHttpRequest, you will have a solid foundation for client-server communication in JavaScript, enabling you to create richer web applications.