Adding RESTful XMLHttpRequest To an HTML5 Web App Using Intel® XDK
Introduction
This article is a continuation of the prior document I published on creating an HTML5 Web App Using Intel® XDK. This document demonstrates how web developers can use JavaScript* and an XMLHttpRequest object to send an HTTP request to an Apache* Tomcat backend server. It outlines the steps developers need to take to use JavaScript* and an XMLHttpRequest object to receive and process the JSON response from the server. It also illustrates to developers how to enable a cross origin request to allow access to cross domain-boundaries.
In the previous article, we reviewed how to create a log-in form using HTML5, JavaScript and CSS3. The log-in form is used in the Little Chef Restaurant web based application to allow customers or managers to log in. When the customers log in, they can see their order history, reward points, and coupons. If the users are managers, they can update customer information, add a new customer or delete an existing customer. We will continue using Intel® XDK to develop, test, and debug the web app to the backend server.
Building a RESTful Web Service with Spring IO*
Spring IO* provides a framework that allows a web service client and an Apache Tomcat server to communicate over a REST interface. The server side uses Ubuntu* Linux* 14 for the operating system, MongoDB* for the database, and the Spring IO platform for the REST service. The article below shows how to set up the server and client for the Restaurant Android* app to access the MongoDB database. Whether the client is an Android app or a web app, the environment setup for the server side is the same. Accessing the database and parsing the JSON response for a web app is different and we’ll talk about it later in this article. To set up the environment for the Apache Tomcat backend server side, see the link below:
Accessing a REST Based Database Backend From an Android* App
Enabling Cross Origin Requests
Enabling cross origin resource sharing is needed when the client web application that loaded in one domain interacts with resources in a different domain. Setting Access-Control-Allow-Origin to “*” indicates that any origin of the requests is allowed. SimpleCORSFilter only responds to all requests that defined in the headers. Access-Control-Allow-Methods defines the list of HTTP POST, GET, PUT, OPTIONS and DELETE requests from clients that are allowed. See the steps below for enabling cross origin requests on the server side.
After setting up the Spring starter project from the article Accessing a REST Based Database Backend From an Android* App, change into the initial source directory
cd gs-accessing-mongodb-data-rest/initial/src/main/java/hello
Create the SimpleCORSFilter object (SimpleCORSFilter.java)
package hello; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; @Component public class SimpleCORSFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Origin, x-requested-with, Content-Type, Accept"); chain.doFilter(req, res); } public void init(FilterConfig filterConfig) {} public void destroy() {} }
Code Example 1: SimpleCORSFilter.java - Enable Cross Origin Requests
Add @ComponentScan to the Application object (Application.java)
package hello; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; @Configuration @EnableMongoRepositories @Import(RepositoryRestMvcConfiguration.class) @EnableAutoConfiguration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Code Example 2: Application.java - Enable Cross Origin Requests
The main() method in Application.java uses Spring Boot’s SpringApplication.run() method to launch an application. @ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the SimpleCORSFilter.java. For further information about enabling cross origin requests, visit:
Enabling Cross Origin Requests for a RESTful Web service
Communicating with the Server
To communicate with the Apache Tomcat server, the web app sends HTTP requests in JavaScript via XMLHttpRequest. The methods for a request-response between a client and server are GET, POST, PUT and DELETE. The HTTP GET is for the login call, HTTP POST is for the register call, HTTP PUT is for the update call, and HTTP DELETE is for the delete call. When the customer or manager logs in to our Restaurant web app, we’ll send the REST requests to the server to retrieve the user information.
Figure 1: Web app Log-in
Making a GET Call to the Server
When the user logs in, we make a GET call to our Apache Tomcat backend server. The send() method will send the request to the server. It accepts any of the following types: DOMString, Document, FormData, File, Blob and ArrayBufferView. The example below demonstrates sending data using DOMString, which is the default. The "true" flag means it is an asynchronous request. If the request is asynchronous, this method returns as soon as the request is sent, which lets the browser continue to work as normal while the server handles the request. If the request is synchronous, this method doesn’t return until the response has arrived.
After a successful request, the XMLHttpRequest’s response property will contain the requested data as a DOMString. The XMLHttpRequest’s response will be null if the request was unsuccessful. In the example below, the array of all the users will be returned in XMLHttpRequest.responseText after the successful GET call. Later we will call validateUsernamePassword() to validate the user information.
usersList.readyState stores the state of the XMLHttpRequest and its value varies between 0 and 4. If the request is complete and the response is ready, the readyState will hold the value 4. usersList.status stores the HTTP status code of the response of the request.
var url_user = "http://10.2.3.55:8080/users"; function xmlhttpRequestUser() { var usersList = new XMLHttpRequest(); usersList.onreadystatechange = function() { if (usersList.readyState === 4 && usersList.status === 200) { // Save the resonseText for later use usersRespObj.value = usersList.responseText; … } }; // The "true" flag means it is an asynchronous request usersList.open("GET", url_user, true); usersList.send(); }
Code Example 3: GET Call to Rest Based Database Backend Server **
Returning a JSON Response
For the GET call to our Spring IO server, XMLHttpRequest.responseText will return a JSON response. The following JSON response example of the GET call defines an users object with an array of two user records. This user information is saved in the REST database in the Apache Tomcat server.
{ "_links" : { "self" : { "href" : "http://192.168.128.33:8080/users{?page,size,sort}", "templated" : true }, "search" : { "href" : "http://192.168.128.33:8080/users/search" } }, "_embedded" : { "users" : [ { "userId" : 12345, "email" : "dplate@gmail.com", "password" : "password123", "firstName" : Don, "lastName" : Plate, "accessLevel" : “manager”, "_links" : { "self" : { "href" : "http://192.168.128.33:8080/users/5500e58e0cf26b1628989a4c" } } }, { "userId" : 12346, "email" : "jsmith@gmail.com", "password" : "password", "firstName" : "Joe", "lastName" : "Smith", "accessLevel" : "customer", "_links" : { "self" : { "href" : "http://192.168.128.33:8080/users/5500e79d0cf26b1628989a4d" } } } ] }, "page" : { "size" : 20, "totalElements" : 3, "totalPages" : 1, "number" : 0 } }
JSON Response Example in XMLHttpRequest.responseText **
Parsing the JSON Response
The JavaScript JSON.parse() converts a JSON string into a JavaScript object arr. We then use dot or “[]” to retrieve its members. To access the users array, we use arr["_embedded"].users[index]. validateUsernamePassword() function below is an example of looping through the users array to access the user information. If the username and password of the user are found in the response text, validateUsernamePassword() function will return true. The validateUsernamePassword() function also saves the href of the user for the PUT and DELETE calls.
function validateUsernamePassword () { // usersRespObj.value is XMLHttpRequest.responseText, a string as JSON returned from the // above GET call to the REST based database backend server. var arr = JSON.parse(usersRespObj.value); var index = 0; for (index = 0; index < arr["_embedded"].users.length; index ++) { // The type of userId in the server is integer. var userId = parseInt(userObj.username); if ((arr["_embedded"].users[index].userId === userId) && (arr["_embedded"].users[index].password.toLowerCase() === userObj.password.toLowerCase())) { console.log("Successfully logged in "); // Retrieve and save the user info except the password. Save href of the user for the PUT call. userObj.accesslevel = arr["_embedded"].users[index].accessLevel; userObj.firstname = arr["_embedded"].users[index].firstName; userObj.lastname = arr["_embedded"].users[index].lastName; userObj.username = arr["_embedded"].users[index].userId; userObj.id = arr["_embedded"].users[index]._links.self.href; userObj.email = arr["_embedded"].users[index].email; return true; } } return false; }
Code Example 4: Parsing the JSON response from the GET call **
Making a POST Call to the Server
To perform a first-time registration, we’ll make a POST call to our Apache Tomcat backend server to add the new user to the REST database. The user information will be stored in a MongoDB on the Apache Tomcat server. We send the proper header information along with the request. The following example demonstrates sending data using a JSON format.
function xmlhttpRequestRegister(admin_form) { var user_list = new XMLHttpRequest(); user_list.onreadystatechange = function() { if (user_list.readyState === 4 && user_list.status === 201) { alert("Registration Complete"); handleLoginSuccess(admin_form); } }; var firstnameTag = "firstName"; var lastnameTag = "lastName"; var passwordTag = "password"; var usernameTag = "userId"; var accessLevelTag = "accessLevel"; var emailTag = "email"; // The user registration information was retrieved from registration pop-up form and saved // in the userObj. accessLevel was default to customer. var params = '{"' + firstnameTag + '":"' + userObj.firstname + '","' + lastnameTag + '":"' + userObj.lastname + '","' + usernameTag + '":"' + userObj.username + '","' + passwordTag + '":"' + userObj.password + '","' + accessLevelTag + '":"' + userObj.accesslevel + '","' + emailTag + '":"' + userObj.email + '"}'; // The "true" flag means it is an asynchronous request user_list.open("POST", url_user, true); // Send the proper header information user_list.setRequestHeader("Content-Type", "application/json"); user_list.send(params); }
Code Example 5: POST Call to Rest Based Database Backend Server **
Making a PUT Call to the Server
When the existing user needs to reset the password or the manager needs to update the user information, we make a GET call to the Apache Tomcat server to find out the href and followed by a PUT call to update the user information in the REST based database. Similarly to the POST call, we send the data using JSON format. We also send the proper header information along with the request.
function xmlhttpRequestResetPassword(admin_form) { var user_list = new XMLHttpRequest(); user_list.onreadystatechange = function() { if (user_list.readyState === 4 && user_list.status === 204) { usersRespObj.value = user_list.responseText; alert("Reset Password Complete"); // If the usersTable exists, just update the table. Otherwise, display the table var tableExist = document.getElementById("usersTable"); if (tableExist) { UpdateUserRow(); close_popup("modal_reset_pw_wrapper"); } else { handleLoginSuccess(admin_form); } } }; var firstnameTag = "firstName"; var lastnameTag = "lastName"; var passwordTag = "password"; var usernameTag = "userId"; var accessLevelTag = "accessLevel"; var emailTag = "email"; var params = '{"' + firstnameTag + '":"' + userObj.firstname + '","' + lastnameTag + '":"' + userObj.lastname + '","' + usernameTag + '":"' + userObj.username + '","' + passwordTag + '":"' + userObj.newPassword + '","' + accessLevelTag + '":"' + userObj.accesslevel + '","' + emailTag + '":"' + userObj.email + '"}'; // The "true" flag means it is an asynchronous request user_list.open("PUT", userObj.id, true); user_list.setRequestHeader("Content-Type", "application/json"); user_list.send(params); }
Code Example 6: PUT Call to Rest Based Database Backend Server **
Making a DELETE Call to Server
Only the existing manager has authority to remove a user account. Similarly to the POST call, the DELETE call also requires the href of the user on the server. First we do a GET call to the Apache Tomcat server to retrieve the href. Then we perform a DELETE call to the server to remove the user information from the REST based database.
function xmlhttpRequestUserDelete() { var del_user = new XMLHttpRequest(); del_user.onreadystatechange = function() { if (del_user.readyState === 4 && del_user.status === 200) { DeleteUserRow(row); } }; // href of the user was saved in userObj.id from the GET call right before this DELETE call var url_del = userObj.id; console.log("url_del = " + url_del); del_user.open("DELETE", url_del, true); del_user.send(); }
Code Example 7: DELETE Call to Rest Based Database Backend Server **
Summary
This article demonstrated how a client web app communicates with the Apache Tomcat server using an HTTP call via XMLHttpRequest. It parses the JSON response received from the server. It also included how to enable cross origin request to allow client web apps access cross domain-boundaries.
References
Accessing a REST Based Database Backend From an Android* App
Enabling Cross Origin Requests for a RESTful Web service
About the Author
Nancy Le is a software engineer at Intel in Software and Services Group working on the Intel® AtomTM processor scale enabling projects.
*Other names and brands may be claimed as the property of others.
**This sample source code is released under the Intel Sample Source Code License