Node.js Routing
We need to provide the requested URL and other required GET and POST parameters for the routing, and then the router needs to execute the corresponding code based on this data.
Therefore, we need to examine the HTTP request, extracting the requested URL and GET/POST parameters from it. It's worth discussing whether this functionality should belong to the router or the server (or even as a function of the module itself), but for now, we assume it as a feature of our HTTP server.
All the data we need will be contained in the request object, which is passed as the first parameter to the onRequest() callback function. However, to parse this data, we need additional Node.js modules, namely the url and querystring modules.
url.parse(string).query
|
url.parse(string).pathname |
| |
| |
------ -------------------
http://localhost:8888/start?foo=bar&hello=world
--- -----
| |
| |
querystring.parse(queryString)["foo"] |
|
querystring.parse(queryString)["hello"]
Of course, we can also use the querystring module to parse the parameters in the POST request body, which will be demonstrated later.
Now, let's add some logic to the onRequest() function to identify the URL path requested by the browser:
server.js File Code:
var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
Now, our application can distinguish different requests based on the requested URL path—this allows us to map requests to handlers based on the URL path using routing (not yet completed).
In the application we are building, this means requests from /start and /upload can be handled using different code. We will see how these pieces come together later.
Now, we can start writing the router. Create a file named router.js and add the following content:
router.js File Code:
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
As you can see, this code does nothing, but that's appropriate for now. Before adding more logic, let's see how to integrate the router with the server.
Our server should be aware of the router and make effective use of it. While we could hard-code this dependency into the server, experience with other programming languages tells us that would be painful, so we will use dependency injection to add the router module in a more loosely coupled manner.
First, let's extend the server's start() function to pass the routing function as a parameter. The server.js file code is as follows:
server.js File Code:
var http = require("http");
var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
We will also extend the index.js file to allow the routing function to be injected into the server:
index.js File Code:
var server = require("./server");
var router = require("./router");
server.start(router.route);
Here, the function we pass still does nothing.
If you start the application now (remember to use the command node index.js
), and then request a URL, you will see the application output the corresponding information, indicating that our HTTP server is now using the routing module and will pass the request path to the router:
$ node index.js
Server has started.
The above output has removed the annoying parts related to the /favicon.ico
request.
Visiting http://127.0.0.1:8888/ in the browser, the output is as follows:
Hello World