Now we are going to make our server realistic in the sense
that routes are assigned handlers that will perform the
actual activities associated with each route. First we
create a separate router.js file that will
hold the code implementing the principle.
The handlers are changed accordingly, and modified to adapt
to the DRY principle. Don't Repeat Yourself.
myg63/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
myg63/bin/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}/`);
});
}
}myg63/routes/router.js
"use strict";
const handlers = require("../private/handlers"); // handlers module
const httpStatus = require("http-status-codes");
const contentTypes = {
"text": { "Content-Type": "text/plain; charset=utf-8" },
"start": { "Content-Type": "text/html; charset=utf-8" },
"js": { "Content-Type": "application/js" },
"css": { "Content-Type": "text/css" },
"png": { "Content-Type": "image/png" },
"jpg": { "Content-Type": "image/jpg" },
"gif": { "Content-Type": "image/gif" },
"ico": { "Content-Type": "image/x-icon" },
"svg": { "Content-Type": "image/svg+xml" }
};
const routes = { // register handles to routes
"GET": {
"/start": handlers.getAndRespond,
"/side": handlers.getAndRespond,
"/about": handlers.getAndRespond,
"/contact": handlers.getAndRespond,
"js": handlers.getAndRespond,
"css": handlers.getAndRespond,
"png": handlers.getAndRespond,
"jpg": handlers.getAndRespond,
"gif": handlers.getAndRespond,
"ico": handlers.getAndRespond,
"svg": handlers.getAndRespond
},
"POST": {
"/contact": handlers.receiveData
}
};
exports.route = function(req, res, body) { // routing
let asset;
let type;
let routedUrl;
if (req.url.indexOf(".js") !== -1) { // check for asset types
asset = "js";
routedUrl = "public/javascripts" + req.url;
type = contentTypes.js;
} else if (req.url.indexOf(".css") !== -1) {
asset = "css";
routedUrl = "public/stylesheets" + req.url;
type = contentTypes.css;
} else if (req.url.indexOf(".png") !== -1) {
asset = "png";
routedUrl = "public/images" + req.url;
type = contentTypes.png;
} else if (req.url.indexOf(".jpg") !== -1) {
asset = "jpg";
routedUrl = "public/images" + req.url;
type = contentTypes.jpg;
} else if (req.url.indexOf(".gif") !== -1) {
asset = "gif";
routedUrl = "public/images" + req.url;
type = contentTypes.gif;
} else if (req.url.indexOf(".svg") !== -1) {
asset = "svg";
routedUrl = "public/images" + req.url;
type = contentTypes.svg;
} else if (req.url.indexOf(".ico") !== -1) {
asset = "ico";
routedUrl = req.url;
type = contentTypes.ico;
} else {
if (req.url.charAt(req.url.length - 1) === "/") {
asset = "/start";
routedUrl = "views/index.html";
type = contentTypes.html;
} else if (req.url === "/start") {
asset = req.url;
routedUrl = "views/index.html";
type = contentTypes.html;
} else {
asset = req.url;
routedUrl = "views" + req.url + ".html";
type = contentTypes.html;
}
}
try {
if (routes[req.method][asset]) { // does handler exist to this route
console.log("routing: " + asset + " file: " + routedUrl);
routes[req.method][asset](routedUrl, type, res); // yes, call it with params
} else { // no, return error msg
console.log("nmlnf: " + req.url);
res.writeHead(httpStatus.NOT_FOUND, contentTypes.text);
res.end(`route for <kbd>${req.url}</kbd> not found`);
}
} catch (ex) { // routing exception
console.log("Log: Routing exception: " + ex);
}
};
myg63/private/handlers.js
'use strict';
/*
* handlers.js
* Requesthandlers to be called by the routing mechanism
*/
const fs = require("fs"); // file system access
const httpStatus = require("http-status-codes");
const goError = function(res) {
res.writeHead(httpStatus.NOT_FOUND, { // http page not found, 404
"Content-Type": "text/html; charset=utf-8"
});
res.write("<h1>404 Not Found</h1>");
res.end();
};
exports.getAndRespond = function(path, contentType, res) {
console.log(path);
if (fs.existsSync(path)) { // does file exist, sync
fs.readFile(path, function(err, data) { // read
if (err) { // if read error
console.log("nml: " + err); // inform server
goError(res); // inform user
return; // back to caller
}
res.writeHead(httpStatus.OK, contentType); // prep header
res.write(data); // prep body with read data
res.end(); // send response
});
} else {
goError(res); // doesnt exist error
}
};
On your CLI do npm test to start the server.
Then go to your browser and test each of the following
urls.
Check the browser screen as well as the console log in each case.