Exchanging JSON with client - novalexei/mod_servlet GitHub Wiki

Exchanging JSON with client

So far we have generated only html output from our servlets and filters. But we can generate any output we want. Modern web sites more and more employ JavaScript frameworks like AngularJS or React. These frameworks allow to build dynamic web pages, which don't need to be reloaded every time something changes. Instead JavaScript connects to the server and sends/receives the necessary data only. Usually this data is exchanged in JSON format.

Let's see, how we can use mod_servlet to work with JavaScript on the client side.

First, let's get some JavaScript application. Just for the demonstration purpose I used AngularJS to-do list example from www.w3schools.com. This little application allows you to maintain to-do list right on the web page. Cute, isn't it?

But the problem with it is that every time you reload the page the stored data is gone. We want it to persist and be distributed, so that we could connect it from different locations. We need a backend. Let's try to write a servlet for this.

Would be nice to store this kind of information to Database, but this is outside of the scope of this tutorial, so we will use a map instead of Database. Here is the code:

class to_do_servlet : public http_servlet
{
    linked_tree_map<std::string, bool> m;
public:
    void do_get(http_request& req, http_response& resp) override
    {
        auto action = req.get_parameter("action");
        if (action)
        {
            auto todoText = req.get_parameter("todoText");
            if (*action == "add")
            {
                if (todoText) m.put(*todoText, false);
            }
            else if (*action == "remove")
            {
                if (todoText) m.erase(*todoText);
                return; // Client doesn't expect response on remove
            }
        }
        toJSON(resp);
    }
private:
    void toJSON(http_response& resp)
    {
        resp.set_content_type("application/json");
        std::ostream &out = resp.get_output_stream();
        out << '[';
        bool first = true;
        for (auto &&item : m)
        {
            if (!first) out << ",\n";
            first = false;
            out << "{\"todoText\":\"" << item.first << "\", \"done\":false}";
        }
        out << ']';
    }
};
SERVLET_EXPORT(toDoServlet, to_do_servlet)

As you can see this can serve 3 actions:

  • add - adds item to the map and returns the JSON for the map;
  • remove - removes item from the map;
  • by default it just returns the to-do list in JSON format.

Note that I'm using here linked_tree_map. This class allows to preserve the order of elements as they were added. JSON serialization is very basic, but you can use any of the JSON libraries available if your application requires more advanced functionality.

Now the client side:

<!-- Original file from http://www.w3schools.com/angular/tryit.asp?filename=try_ng_todo_app -->
<!DOCTYPE html>
<html>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js"></script>
<body ng-app="myApp" ng-controller="todoCtrl">
<h2>My Todo List</h2>

<form ng-submit="todoAdd()">
    <input type="text" ng-model="todoInput" size="50" placeholder="Add New">
    <input type="submit" value="Add New">
</form>

<br>
<div ng-repeat="x in todoList">
    <input type="checkbox" ng-model="x.done"> <span ng-bind="x.todoText"></span>
</div>
<p><button ng-click="remove()">Remove marked</button></p>

<script>
var app = angular.module('myApp', []); 
app.controller('todoCtrl', function($scope, $http)
{
    $http.get("/to-do/db").then(function (response)
    {
        $scope.todoList = response.data;
    });

    $scope.todoAdd = function()
    {
        $http.get("/to-do/db?action=add&todoText="+$scope.todoInput).
            then(function (response)
        {
            $scope.todoList = response.data;
        });
        $scope.todoInput = "";
    };

    $scope.remove = function() {
        angular.forEach($scope.todoList, function(x)
        {
            if (x.done)
            {
                $http.get("/to-do/db?action=remove&todoText="+x.todoText);
            }
        });
        $http.get("/to-do/db").then(function (response)
        {
            $scope.todoList = response.data;
        });
    };
});
</script>

</body>
</html>

On this page I only changed app.controller so that instead of maintaining to-do list by itself the script would query the server. You can see that on every operation it notifies the server with appropriate action and requests the whole list.

So now let's create new web application "to-do", deploy there the library we built with to_do_servlet. Copy the html file to the root of our web application directory (let's call it to-do.html).

One last thing is a web.xml configuration. All we need to have there is:

<web-app>
    <servlet>
        <servlet-name>ToDoServlet</servlet-name>
        <servlet-factory>libtodo.so:toDoServlet</servlet-factory>
    </servlet>
    <servlet-mapping>
        <servlet-name>ToDoServlet</servlet-name>
        <url-pattern>/db</url-pattern>
    </servlet-mapping>
</web-app>

Now start the server and try the application at /to-do/to-do.html.

Note that the servlet doesn't synchronize the modifications to the list. If you want the application to be concurrent you'll need to take care of it.

OK, let's check now some topics to help you writing advanced web applications. Logging in your web applications

To The Programming Guide

⚠️ **GitHub.com Fallback** ⚠️