Server Reads Requested Assets, Dynamic Routes, More Asset Types

At the end of the previous section we saw some serious drawbacks in the way we expected our webserver to work. Here we shall do what it takes to remedy that. This basically means expanding the routing so that is aware of the interaction af the browser and the http.

Yet again, following the best practice outlined in the section called “Project Creation - Best Practice” we create a project, this time myg62 remotely, clone it, prepare the package.json, and before we get to the server, we create a project subdirectory views, and in that we create a boilerplate index.html. This time we add a few more directories so that we may get a good structure in our server environment. We create a public directory, and inside that a images, a css, and a js directory. Other files are copied from the most recent incarnation. It will look similar to this:

.
├── node_modules
│   └── http-status-codes
│       ├── LICENSE
│       ├── README.md
│       ├── index.d.ts
│       ├── index.js
│       └── package.json
├── public
│   ├── css
│   │   └── side.css
│   ├── images
│   │   └── iau_rocks.png
│   └── js
│       └── side.js
├── views
│   ├── index.html
│   └── side.html
├── README.md
├── handlers.js
├── main.js
├── package-lock.json
├── package.json
├── router.js
└── server.js

Still, the main.js, and the server.js is unchanged. The router and the handlers are, as often repeated, project specific and must therefore be adapted with each project. This time the router has changed to cater for routes based on embedded requests such as css, js, and images. When the browser renders a primary request, eg for and HTML5 page, it issues requests for the urls it meets in the rendering. These requests are recognised by file extensions, based on the router dissection of the request url.

Example 17.11. Routed and Modularized Application, myg62/main.js, Unchanged!
"use strict";

var server = require("./bin/server");               // make server module available
var router = require("./routes/router");            // router module

server.start(router);                               // start server
                                                    // callback to route

Example 17.12. The Server Module, myg62/server.js, Unchanged!
"use strict";
/*
 *  new server.js adds request body data
 */
const http = require("http");                   // http module
const lib = require("../private/libWebUtil");   // home grown utilities
const hostname = "localhost";
const port = Number(process.argv[2]) || 3000;

module.exports = {
    start(router) {
        const server = http.createServer();

        server.on("request", function (req, res) {      // eventhandler for "request"
            console.log(lib.makeLogEntry(req));         // home made utility for logging
            let body = [];
            req.on("data", function (bodyData) {        // eventhandling for data reception
                body.push(bodyData);                    // bodyData is an object
            });
            req.on("end", function () {                 // eventhandling for end-of-data
                body = Buffer.concat(body).toString();  // body2string
                router.route(req, res, body);           // pass to router
            });
        });

        server.listen(port, hostname, function () {
            console.log(`Log: Server started on http://${hostname}:${port}/`);
        });
    }
}

Example 17.13. The Router Module, myg62/router.js
"use strict";
/*
 * check if routed handler function exists
 * if yes call it, else complain
 */
const handlers = require("./handlers");               // handlers module
const requestHandlers = {                             // application urls here
    "/": handlers.home,
    "/start": handlers.home,
    "/side": handlers.side,
    "/notfound": handlers.notfound,
    "js": handlers.js,
    "css": handlers.css,
    "png": handlers.png
}

module.exports = {
    route(req, res, body) {
        let arr = req.url.split(".");
        let ext = arr[arr.length - 1];
        if (typeof requestHandlers[req.url] === 'function') {  // look for route
            requestHandlers[req.url](req, res);                // if found use it
        } else if (typeof requestHandlers[ext] === "function") {
            requestHandlers[ext](req, res);
        } else {
            console.log("5: " + ext);
            requestHandlers["/notfound"](req, res);        // use notfound
        }
    }
}

Example 17.14. Handlers, myg62/handlers.js
'use strict';
/*
 * handlers.js
 * Requesthandlers to be called by the router mechanism
 */
const fs = require("fs");                           // file system access
const httpStatus = require("http-status-codes");    // http sc

module.exports = {
    home(req, res) {
        let path = "views/index.html";
        fs.readFile(path, function(err, data) {
            if (err) {
                console.log(`Not found file: ${path}.`);
            }
            res.writeHead(httpStatus.OK, {      // yes, write header
                "Content-Type": "text/html; charset=utf-8"
            });
            console.log(`served routed file: ${path}.`);
            res.write(data);
            res.end();
        });
    },
    side(req, res) {
        let path = "views/side.html";
        fs.readFile(path, function(err, data) {
            if (err) {
                console.log(`Not found file: ${path}.`);
            }
            res.writeHead(httpStatus.OK, {      // yes, write header
                "Content-Type": "text/html; charset=utf-8"
            });
            console.log(`served routed file: ${path}.`);
            res.write(data);
            res.end();
        });
    },
    js(req, res) {
        let path = "public/js" + req.url;
        fs.readFile(path, function(err, data) {
            if (err) {
                console.log(`Not found file: ${path}.`);
            }
            res.writeHead(httpStatus.OK, {      // yes, write header
                "Content-Type": "application/javascript; charset=utf-8"
            });
            console.log(`served routed file: ${path}.`);
            res.write(data);
            res.end();
        });
    },
    css(req, res) {
        let path = "public/css" + req.url;
        fs.readFile(path, function(err, data) {
            if (err) {
                console.log(`Not found file: ${path}`);
            }
            res.writeHead(httpStatus.OK, {      // yes, write header
                "Content-Type": "text/css; charset=utf-8"
            });
            console.log(`served routed file: ${path}.`);
            res.write(data);
            res.end();
        });
    },
    png(req, res) {
        let path = "public/images" + req.url;
        fs.readFile(path, function(err, data) {
            if (err) {
                console.log(`Not found file: ${path}`);
            }
            res.writeHead(httpStatus.OK, {      // yes, write header
                "Content-Type": "image/png"
            });
            console.log(`served routed file: ${path}.`);
            res.write(data);
            res.end();
        });
    },
    notfound(req, res) {
        console.log(`Handler 'notfound' was called for route ${req.url}`);
        res.end();
    }
}

On your CLI do npm test to start the server.

Now we show http://localhost:3000/start and then http://localhost:3000/side to check for the assets in the HTML5.