Express 3 вышел в 2012 году. Эта статья разбирает конкретный переход с 2.x на 3.x и полезна при чтении старого проекта; она не предлагает устанавливать Express 3 для нового приложения. Обе ветки не поддерживаются. Изменения сверяются с официальной миграцией 2.x → 3.x; для действующего приложения нужен дальнейший переход на поддерживаемую ветку.
Удалено
res.render()опция "status" (используйтеres.statusCode=илиres.status(code).render(...))res.render()опция "charset" (используйтеres.charset=)res.local(name, value)(используйтеres.locals.name = valueилиres.locals({ name: value }))app.dynamicHelpers()(используйте middleware +res.locals)app.helpers()(используйтеapp.locals)- концепция "layout" (теперь это задача шаблонизатора)
partial()(теперь это задача шаблонизатора)res.partial()- настройка "view options", используйте
app.locals - настройка "hints"
req.isXMLHttpRequest(используйтеreq.xhr)app.error()(используйте middleware с аргументами (err, req, res, next))req.flash()(просто используйте сессии:req.session.messages = ['foo']или как-то так)- connect-flash может использоваться вместо req.flash()
- настройка
jsonp callbackбыла удалена (используйтеres.jsonp())
Изменилось
Здесь перечислены изменения именно Express 3. В текущем Express сессии и парсер cookies подключаются отдельными пакетами, а значения секретов берутся из конфигурации окружения. Не копируйте старую строку express.cookieParser('secret') как рабочую конфигурацию.
req.header(field[, defaultValue])изменили наreq.get(field)(оставлено для обратной совместимости)res.header(field[, value])изменили наres.set(field, value)/res.get(field)(оставлено для обратной совместимости)- переименовали
app.register()наapp.engine() - шаблонизатор теперь должен соответствовать не
engine.compile(str, options) => Function, аengine.__express(filename, options, callback) express.createServer()теперь простоexpress()(но для обратной совместимости сохранено).- помните, что
express()теперь не возвращает экземплярhttp.Server. (См. раздел Фабрика приложения, там детальнее)
- помните, что
- Используйте
express.cookieParser('secret')вместо того, чтобы передавать "секрет" в middlewareexpress.session(). Опция 'secret' в middlewareexpress.session()была удалена.
View options
Настройка "view options" больше не нужна, app.locals - это локальные переменные, которые смешиваются с теми, что мы передаём в res.render(), поэтому app.locals.pretty = true эквивалентно res.render(view, { pretty: true }).
Фабрика приложения
express() возвращает функцию-обработчик HTTP-запросов, а не «единичный такт» приложения. HTTP-сервер вызывает её для входящего запроса. Это позволяет передать одно приложение в http.createServer() или https.createServer():
...
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
Для удобства и простоты метод app.listen() принимает те же аргументы,
оборачивая их в HTTP сервер. Следующие примеры эквивалентны:
var app = express();
app.listen(3000);
и
var app = express()
, http = require('http');
http.createServer(app).listen(3000);
Это означает, что методы, присутствующие в http.Server.prototype, более не присутствуют в app, например, app.address() теперь должен вызываться у экземпляра сервера, который возвращается из app.listen()
или того, который вы создадите с помощью http.createServer(app).
Совместимость с Socket.IO
Ниже сохранён исторический способ подключения Socket.IO. У современных версий API отличается: следуйте инструкции сервера Socket.IO, где используется new Server(httpServer). Не смешивайте версии в одном примере.
Метод .listen() Socket.IO принимает аргументом экземпляр http.Server. С версии 3.x, express() не возвращает экземпляр http.Server. (См. раздел Фабрика приложения выше.) Чтобы Socket.IO заработал с Express 3.x, создайте вручную и передайте экземпляр http.Server в метод .listen() Socket.IO.
Примечание переводчика. Я, например, передаю в Socket.IO то, что вернуло мне
app.listen(), это ведь тожеhttp.Server
var app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
server.listen(3000);
Интеграция шаблонизаторов
Для совместимости с Express 2.x шаблонизатор должен экспортировать следующее:
exports.compile = function(templateString, options) {
return a Function;
};
Совместимые с Express 3.x шаблонизаторы должны экспортировать следующее:
exports.__express = function(filename, options, callback) {
callback(err, string);
};
Если шаблонизатор не экспортирует этот метод, все равно есть способ - метод app.engine() позволяет сопоставить любую функцию определённому расширению файла.
Допустим, есть библиотека markdown, и нужно рендерить .md-файлы, но эта библиотека не поддерживает Express, тогда ваш вызов app.engine() будет выглядеть примерно так:
var markdown = require('some-markdown-library');
app.engine('md', function(path, options, fn){
fs.readFile(path, 'utf8', function(err, str){
if (err) return fn(err);
str = markdown.parse(str).toString();
fn(null, str);
});
});
Изменения системы шаблонов
После удаления концепции layouts и partials в Express 3.x шаблонизаторы теперь имеют больше контроля над операциями файлового ввода-вывода. Интеграция с шаблонизаторами теперь существенно упростилась, также упростились внутренние механизмы системы шаблонов.
Поскольку layouts и partials вышли из Express, наследование реализует шаблонизатор. В Pug для этого есть extends и block, а для повторяемых частей — include и mixin. Устаревшая статья на devthought больше не нужна как инструкция по настройке.
Возврат старого partial() отдельным расширением — исторический вариант совместимости, а не обязательный шаг миграции. Сначала определите, какие шаблоны действительно используют старое поведение.
В старых проектах встречаются пакеты express-partials и ejs-locals. Они названы здесь для распознавания существующей зависимости; это не рекомендация добавлять их в новый проект.
Middleware для обработки ошибок
В версии 2.x метод app.error(callback) был на практике эквивалентен следующему:
app.error = function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
});
};
Причина в том, что Connect различает "обычное" middleware,
и middleware для обработки ошибок через fn.length. У обычного fn.length <= 3, что соответствует (req, res, next), тогда как middleware для обработки ошибок должно иметь ровно 4 - (err, req, res, next).
В 2.x эта функциональность была обёрнута в синтаксический сахар, чтобы упростить API.
Проще говоря, всё, что нужно для перехвата ошибок, это определить другое middleware, но с четырьмя аргументами. Это middleware должно быть определено ниже всех остальных, чтобы они могли вызывать next(err) для передачи ошибки, как показано ниже:
app.use(express.bodyParser())
app.use(express.cookieParser())
app.use(express.session())
app.use(app.router) // the router itself (app.get(), app.put() etc)
app.use(function(err, req, res, next){
// if an error occurs Connect will pass it down
// through these "error-handling" middleware
// allowing you to respond however you like
res.send(500, { error: 'Sorry something bad happened!' });
})
Локальные переменные уровня запроса и приложения
app.locals содержит значения уровня приложения, например название сервиса. res.locals существует в рамках одного ответа и подходит для данных текущего пользователя. Не сохраняйте пользователя в app.locals: он станет общей переменной для запросов.
app.locals.siteName = 'JSman';
app.use(function (req, res, next) {
res.locals.currentUser = req.user || null;
next();
});
app.get('/profile', function (req, res) {
res.render('profile');
});
Проверьте два последовательных запроса с разными пользователями: второй не должен получить данные первого. Имена шаблонов выбирает код приложения, а не произвольный параметр посетителя.