I have recently started working with node.js, express & mongodb. Since express uses connect to provide middleware support, I started reading up on middleware and connect.
I came across the following example on howtonode.org:
return function logItHandle(req, res, next) {
var writeHead = res.writeHead; // Store the original function
counter++;
// Log the incoming request
console.log("Request " + counter + " " + req.method + " " + req.url);
// Wrap writeHead to hook into the exit path through the layers.
res.writeHead = function (code, headers) {
res.writeHead = writeHead; // Put the original back
// Log the outgoing response
console.log("Response " + counter + " " + code + " " + JSON.stringify(headers));
res.writeHead(code, headers); // Call the original
};
// Pass through to the next layer
next();
};
Could someone explain to me what is happening in this closure? The author calls it a
wrapping idiom to hook into the call to writeHead
What does that mean?
It's intercepting calls to res.writeHead, adding some logging and then delegating calls to the original res.writeHead.
It's like a super-simple take on AOP for method interception.
Let's decompose what is happening here
wrapping idiom to hook into the call to writeHead
In a standard express flow, the request (req) is received and the response (res) is prepared. The (req, res) pair can be cascaded through a serie of filters that can modify the req and prepare the res.
At one point in the flow, the res will be deemed sufficiently prepared that the headers of the response can be sent to the client. The function res.writeHead* will be called for that purpose.
The prototype of this function being function (code, headers), in order to log the headers that are going to be sent, you need to hook the code at this very moment and do a
console.log("Response " + code + " " + JSON.stringify(headers));
In a way, if the orgininal function in the code was
res.writeHead = function(code, headers) {
// original code
}
you would like to replace this code with a
res.writeHead = function(code, headers) {
console.log("Response " + code + " " + JSON.stringify(headers));
// original code
}
In a way, you want on "insert" a snippet of code at the beginning of the writeHead function.
But you should not try and modify the original writeHead code, because you might not even know where this code is written and you don't want to start looking. So you want to hijack this function : When a piece of code will call res.writeHead, you want your function to be called instead.
A way to to this is simply
return function logItHandle(req, res, next) {
res.writeHead = function (code, headers) {
console.log("Response " + code + " " + JSON.stringify(headers));
}
next();
}
but if you only do this you're into a bit of a trouble because the original writeHead code is lost and will not be called. Hence the headers will be logged but not sent to the client !
you need a way to "remember" the original code and call it at the end of you writeHead variant :
return function logItHandle(req, res, next) {
// save the original writeHead function so that it can be referenced in the closure
var originalWriteHead = res.writeHead;
res.writeHead = function (code, headers) {
// log the response headers
console.log("Response " + code + " " + JSON.stringify(headers));
// make as if our hook never existed in the flow
res.writeHead = originalWriteHead ;
// call the original writeHead because otherwise the external code
// called our implementation of writeHead instead of the original code
res.writeHead(code, headers);
}
next();
}