In this tutorial, we show how to easily run AJAX over WebSockets for browser to server communication. We also go over situations where AJAX should not be used and when real time messages sent over a WebSocket connection is preferred over using AJAX.
You are probably thinking why would I want to run AJAX over WebSockets. There are several benefits to running AJAX over WebSockets, such as:
A server back-end may provide many AJAX services, and to differentiate one service from another, each service may have its own URL. In other words, most modern AJAX calls are to REST APIs using JSON as the data serialization format. As an example, we may have a service 'math' that provides a set of mathematical services such as 'add' and 'subtract'. The 'add' service may use an URI such as "/math/add" and the 'subtract' service may use an URI such as "/math/subtract".
AJAX returns a single response to a single request. However, AJAX is asynchronous; thus, a web front-end can send any number of AJAX requests without having to wait for each response.
WebSockets on the other hand can handle multiple bidirectional messages between the browser and the server. There is no correlation between the messages sent in either direction. It is up to the application to interpret and manage these messages. If you have any TPC/IP socket experience, you can simply think of WebSockets as a TCP stream that includes a packet size header. Each WebSocket message is sent and received as one packet.
Implementing AJAX on a WebSocket connection is conceptually very easy:
Each of the asynchronous responses A', B', and C' must trigger the correct response callback function for the request A, B, and C, even if the response messages should be in a different order.
The easiest way to implement the client side AJAX callback handling is to store each callback in an object and give each callback a unique ID. The ID, which can for example be a number, is sent to the server as part of the message. The server side then bundles this ID with the response data.
When the client receives the AJAX response, the client finds the callback function by using the ID received from the server. The callback IDs need to be unique for each request/response pair. A super easy way to find a unique ID is to use a random number and look for collisions.
Say we have a math AJAX service, where we can add two numbers:
To call this from the client, we must provide the two arguments a and b, the callback function, and the math REST URI.
The AJAX response callback receives two arguments, the response data and possibly an error message. The response data 'rsp' is null if the error argument is provided.
The AJAX over WebSocket client library is implemented in a mere 22 lines of code. To get this code and a fully working example, including the server code, download our AJAX over WebSocket Example.
The 'Mako Server' and the 'Barracuda App Server library' are typically used for device management and our focus is on these type of systems. For device management, the most obvious reason for not using AJAX is when the solution requires real time data pushed from the server to the client. AJAX can be used for polling new data, but it is much easier to simply push the real time data over a WebSocket connection.
Another reason for not using AJAX is much more subtle and may not be discovered until the complete solution is designed, resulting in expensive re-design if not properly thought of during the initial design phase. As an example, let's assume we need to design a solution for controlling one light bulb. The light switch state is presented in the browser as either on or off. AJAX may seem like the perfect candidate for such a simple solution. The user clicks the light switch in the browser, an AJAX request is sent to the server, the server turns the light bulb on or off, and the AJAX response is sent to the browser. However, there is a subtle problem with this solution -- the web is a multi user system enabling multiple users to surf to the same web server. In other words, the server (and the light bulb controlled by the server) is a shared resource that may be controlled by many users. What happens if two users try to turn the light bulb on or off at the same time? The two browser interfaces displaying the light switch state will be out of synch and not show the real on/off state for the light bulb. For this reason, AJAX is not a good solution for such a system.
Instead of using AJAX, we can take full advantage of WebSocket's bi-directional features and design a reactive system that encompasses the complete solution, including all connected browsers, and the server. The following figure depicts such a reactive system, where the light switch click is propagated to the server and then to all connected browsers.
WebSockets lets us respond to user inputs in real time and reactively update all connected browser user interfaces simultaneously.
The following video shows how fast Websockets are compared to traditional REST/AJAX interfaces. You can find more information on this project at the Barracuda App Server for ESP32 GitHub page page.