Hey, guys! Let’s continue our lesson about Express basics and Middleware
.
The result is (add to app.js
):
app.use(function(req, res, next) { if (req.url == '/') { res.end("Hello"); } else { next(); } });
The function next
serves to deliver control further down the chain to the next Middleware
, or to the next function that is announced through app.use
. The second Middleware
can also check something and transfer control further:
// Middleware app.use(function(req, res, next) { if (req.url == '/') { res.end("Hello"); } else { next(); } }); app.use(function(req, res, next) { if (req.url == '/test') { res.end("Test"); } else { next(); } });
Launch it in a following way and see what we’ve got.
http://localhost:3000/
http://localhost:3000/test
Everything works.
And what will happen, if we follow a page that does not exist?
http://localhost:3000/nopage
The integrated Express handler has worked. If there is no Middleware
, next has been called, and the next Middleware
does not exist, Express outputs a ‘Not Found’ page by default. In order to influence it somehow, let us create one more Middleware
to receive req
and res
, the one that will always be the last in this chain:
app.use(function(req, res) { res.send(404, "Page Not Found Sorry"); });
Launch it. Note, there is a correct 404
status in console
. Here we’ve just used the method res.send
. Standard req
and res
elements lack it, but you can find it in Express, for the reason it expands req
and res
objects prior to calling a Middleware
chain, expands objects through inheriting and adds some of its methods to them. These methods can be found at expressjs.com. There you will find API Reference that includes a lot of things – in particular, we need Response
and a send
method. Its simple form is: res.send(hello world);
– to send a line, but there are some more challenging and interesting variants:
res.send(new Buffer('whoop')); res.send({ some: 'json' }); res.send('<p>some html</p>'); res.send(404, 'Sorry, we cannot find that!'); res.send(500, { error: 'something blew up' }); res.send(200);
Send
can send various things: Buffer
, json
, text; and if the first argument is a number, it puts a respective status, too. So, this method is quite versatile and handy.
But what will happen, if we’ve got an error somewhere? Let us create a special Middleware
, call it error
, and there will actually be an error:
app.use(function(req, res, next) { if (req.url == '/error') { BLABLA() } else { next(); } });
Go to :
http://localhost:3000/error
The built-in Express error handler has worked. It works only when Middleware
has throw.
app.use(function(req, res, next) { if (req.url == '/error') { throw new Error ('......') } else { next(); } });
Of course, it won’t work, if this throw
is wrapped into setTimeout
. That’s what the principle of JavaScript is.
Now let us deal with the correct handling of all errors – the ones that occur in the average process of work of our website. Let us pretend, a user has followed a url
that is forbidden for entering. In this case we can either notify him immediately (res.send(401)
), or sometimes it is even more convenient to deliver the error further down the chain. That’s how it looks like (let us change our previous function):
app.use(function(req, res, next) { if (req.url == '/forbidden') { next(new Error("wops, denied")); } else { next(); } });
If there is some argument inside next
, Express knows it is an error and delivers it to an error handler. By default, a handler of this kind, as we’ve already seen, outputs a stack, which is inappropriate in real situations. So, we can create our own handler. It is programmed in the same way as Middleware
, while app.use is just a function with 4 instead of 3 elements:
app.use(function(err, req, res, next) {
In JavaScript every function has a length
property that contains a number of arguments in its declaration, that’s why when seeing a function with 4 arguments, Express is able to understand it is an error handler. Respectively, if an error occurs – either throw
or next
has been called with an argument – the control gets immediately transferred to
app.use(function(err, req, res, next) { }
Here we can already output an error: in a development case it will be stack
, and in real life it will be an error code, template, etc.
How can we know whether the script includes a development case or a real launch? For that reason, we’ve got a special value that can be received using app.get('env')
. If a special NODE_ENV
environment variable is not specified, this thing will be development
. But if it does include that variable, it will be equal to the value of this variable:
app.use(function(err, req, res, next) { // NODE_ENV = 'production' if (app.get('env') == 'development') {
In real-life launch it has a value production
. Respectively, if it is development
, let us output the error beautifully. For that reason, we’ve got a special built-in Middleware
– express.errorHandler.
Let us take it out of a generated template and insert here:
app.use(function(err, req, res, next) { // NODE_ENV = 'production' if (app.get('env') == 'development') { app.use (express.errorHandler()); } });
What kind of thing is that? Let us take a precise look at our Express sources. What Express exports does not include errorHandler
. To find it, we should look deeper in what’s going on here, in particular, in our loop:
for (var key in connect.middleware) { Object.defineProperty( exports , key , Object.getOwnPropertyDescriptor(connect.middleware, key)); }
Еxpress is a framework created around another framework called connect
. (starting from the version Express 4 it is not so anymore, but our lesson is built on the version Express 3). It contains various Middleware
that get included into Express this way by default. Middleware
can be found in
node modules→connect→ lib→middleware →errorHandler.
Now let us create errorHandler
and deliver a request in an explicit view to it, as the previous code, unfortunately, won’t work:
app.use(function(err, req, res, next) { // NODE_ENV = 'production' if (app.get('env') == 'development') { var errorHandler = express.errorHandler(); errorHandler(err, req, res, next); } else { res.send(500); } });
Check it. We’ve got development
, so the respective branch if
has worked.
In our upcoming articles we will continue working with Express, analyze built-in Middlewares
and output a normal html
page.
The lesson code can be found here.
The materials for this article have been borrowed from the following screencast.
We are looking forward to meeting you on our website soshace.com