
Всем привет! Давайте продолжим наш урок об основах Express и Middleware.
Итог (добавим в app.js):
app.use(function(req, res, next) {
if (req.url == '/') {
res.end("Hello");
} else {
next();
}
});Функция next служить для того, чтобы передать управление дальше по цепочке к следующему Middleware, то есть, к следующей функции, которая объявлена через app.use. Второй Middleware тоже может что-то проверить и передать управление дальше:
// 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();
}
});Запустим вот таким образом и посмотрим, что у нас получилось.
http://localhost:3000/
http://localhost:3000/test
Все работает.
А что будет, если мы пойдем на страницу, которой не существует?
http://localhost:3000/nopage
Сработал встроенный обработчик Express. В том случае, если Middleware кончились, next вызван, а следующего Middleware нет, то Express выводит страничку «не найдено» по умолчанию. Для того, чтобы как-то влиять на это, сделаем еще один Middleware, который будет получать req и res, и который будет всегда замыкать цепочку:
app.use(function(req, res) {
res.send(404, "Page Not Found Sorry");
});Запустим это. Обратим внимание в console, статус совершенно правильный – 404. Здесь мы сейчас использовали метод res.send. Он отсутствует в обычных req и res, но он есть в Express, потому что он, перед тем как вызвать цепочку Middleware, расширяет объекты req и res при помощи наследования и добавляет им некоторые свои методы. Эти методы можно посмотреть на страничке expressjs.com. Там есть API Reference, где имеется много чего, в частности, нам нужно Response и метод send. Вот его простой вид: res.send(hello world); – отослать строку, а есть и более сложные и интересные варианты:
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 умеет посылать что угодно: Buffer, json, текст, и если первый аргумент – это число, то он ставит соответствующий статус тоже. Такой вот универсальный и удобный метод.
Что будет, если где-то произойдет ошибка? Давайте создадим специальный Middleware, назовем его error, и в нем будет ошибка:
app.use(function(req, res, next) {
if (req.url == '/error') {
BLABLA()
} else {
next();
}
});Зайдем на :
http://localhost:3000/error
Сработал встроенный обработчик ошибок Express. Он работает в том случае, если в Middleware есть throw.
app.use(function(req, res, next) {
if (req.url == '/error') {
throw new Error ('......')
} else {
next();
}
});Конечно, он не сработает, если этот throw завернут в setTimeout. Так уж устроен JavaScript.
Займемся более правильной обработкой ошибок вообще, тех, которые возникают в ходе нормального функционирования сайта. Например, человек зашел на url, на который заходить не имеет права. В этом случае мы можем либо сразу же ему ответить (res.send(401)), либо бывает гораздо удобнее передать ошибку дальше по цепочке. Выглядит это так (изменим нашу предыдущую функцию):
app.use(function(req, res, next) {
if (req.url == '/forbidden') {
next(new Error("wops, denied"));
} else {
next();
}
});Если в next есть какой-то аргумент, тогда Express знает, что это ошибка, и передает ее обработчику ошибок. По умолчанию такой обработчик, как мы видели, выводит стек, что совершенно неприемлемо в реальной жизни. Поэтому можно задать свой обработчик. Он задается совершенно так же, как и Middleware, а app.use – только функция – не 3 элемента имеет, а 4:
app.use(function(err, req, res, next) {
В JavaScript у каждой функции есть свойство length, которое содержит количество аргументов в ее объявлении, поэтому Express, если видит функцию с 4 аргументами, то оно может понять, что это обработчик ошибок. Соответственно, если произошла ошибка либо throw, либо next вызвался с аргументом, тогда управление передается сразу в
app.use(function(err, req, res, next) {
}Здесь мы уже можем вывести ошибку, если это разработка – то вывести stack, если это реальная жизнь – код ошибки, шаблон и т.д.
Как нам узнать в скрипте сейчас разработка или реальный боевой запуск? Для этого есть специальное значение, которое можно получить app.get('env'). Если не указана специальная переменная окружения NODE_ENV, то эта штука равна development. А если она указана, то остается равна значению этой переменной:
app.use(function(err, req, res, next) {
// NODE_ENV = 'production'
if (app.get('env') == 'development') {
В реальной жизни в боевом запуске она имеет значение production. Соответственно, если это development, то давайте выведем ошибку красиво. Для этого есть специальная встроенная Middleware – express.errorHandler. Возьмем ее из cгенерированного шаблона и поставим вот сюда:
app.use(function(err, req, res, next) {
// NODE_ENV = 'production'
if (app.get('env') == 'development') {
app.use (express.errorHandler());
}
});
Что это за зверь такой? Давайте посмотрим внимательнее в исходники Express. В том, что экспортирует Express нет errorHandler. Чтобы его найти нам надо немного глубже заглянуть в то, что здесь происходит, а именно, в цикл:
for (var key in connect.middleware) {
Object.defineProperty(
exports
, key
, Object.getOwnPropertyDescriptor(connect.middleware, key));
}
Еxpress – это фреймворк, который создан вокруг другого фреймворка, который называется connect. (c версии Express 4 это стало не так, но наш урок построен на версии Express 3). В нем есть различные Middleware, которые по умолчанию Express таким образом в себя получает. Middleware можно посмотреть в
node modules→connect→ lib→middleware →errorHandler.
Теперь создадим errorHandler и отдадим ему запрос в явном виде, так как предыдущий код, к сожалению, работать не будет:
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);
}
});
Проверим.У нас development, поэтому сработала соответствующая ветка if.
В следующих статьях мы продолжим работать с Express, рассмотрим встроенные Middleware и выведем нормальную html-страничку.
Код урока доступен по ссылке.

Материалы для статьи взяты из данного скринкаста.
We are looking forward to meeting you on our website blog.soshace.com
