Основы Node JS & Express (III). Основы Node JS & Express (III) Express js быстрый сервер

  • Перевод

Начинающему программисту разработка для Node.js может показаться сущим кошмаром. Виной всему – гибкость этой платформы и отсутствие чётких руководств. Но, на самом деле, всё не так уж и страшно.


Вот, например, типичная задача: разработка REST API, серверной части некоего приложения. Обилие собственных возможностей Node и множество дополнительных модулей, которые способны помочь в решении этой задачи, способны завести новичка в тупик, вызванный богатством выбора. Основные вопросы здесь заключаются в подборе компонентов и в настройке их совместной работы.

Один из способов создания серверной части приложения заключается в применении связки из Node.js, фреймворка Express и СУБД MongoDB. Собственно говоря, сегодня я расскажу о том, как создать рабочий макет API, который может служить основой для практически любого приложения. Здесь мы реализуем основные маршруты REST, будем взаимодействовать с API по HTTP и использовать простые варианты работы с базой данных.

Для того, чтобы успешно освоить этот материал, вам надо понимать, что такое REST API, иметь представление об операциях CRUD и обладать базовыми знаниями в области JavaScript. Здесь я использую ES6, ничего особенно сложного, в основном – стрелочные функции.

Мы разработаем скелет серверной части приложения для создания заметок, похожего на Google Keep . При этом с заметками можно будет выполнять все четыре CRUD-действия, а именно – создание (create), чтение (read), обновление (update), и удаление (delete).

Предварительная подготовка

Если Node у вас пока нет, самое время его установить . После установки создайте папку и выполните в ней команду инициализации нового проекта:

Npm init
В ходе инициализации ответьте на вопросы, в частности, дайте приложению имя «notable» (или, если хотите, любое другое).

Теперь в папке должен появиться файл package.json . Это означает, что можно начать устанавливать дополнительные пакеты, от которых зависит проект.

В качестве фреймворка мы планируем использовать Express. Системой управления базами данных будет MongoDB. Кроме того, в качестве вспомогательного средства для работы с JSON, используем пакет body-parser. Установим всё это:

Npm install --save express mongodb body-parser
Ещё, я очень рекомендую установить Nodemon как dev-зависимость. Это простой маленький пакет, который, при изменении файлов, автоматически перезапускает сервер.

Для установки этого пакета выполните команду:

Npm install --save-dev nodemon
Затем можно добавить следующий скрипт в файл package.json :

// package.json "scripts": { "dev": "nodemon server.js" },
Готовый package.json будет выглядеть примерно так:

// package.json { "name": "notable", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "dev": "nodemon server.js" }, "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.15.2", "express": "^4.14.0", "mongodb": "^2.2.16" }, "devDependencies": { "nodemon": "^1.11.0" } }
Теперь создадим файл server.js и приступим к работе над API.

Сервер

Начнём с подключения зависимостей в файле server.js .

// server.js const express = require("express"); const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); const app = express();
MongoClient будем использовать для взаимодействия с базой данных. Кроме того, здесь мы инициализируем константу app , символизирующую наше приложение, экземпляром фреймворка Express. Чтобы сервер заработал, осталось лишь указать приложению на то, чтобы оно начало прослушивать HTTP-запросы.

Тут укажем порт и запустим прослушивание следующим образом:

// server.js const port = 8000; app.listen(port, () =>
Теперь, если выполнить команду npm run dev (или – node server.js , если вы не устанавливали Nodemon), в терминале должно появиться сообщение: «We are live on port 8000».

Итак, сервер работает. Но сейчас он не делает совершенно ничего полезного. Давайте с этим разберёмся.

Маршруты, ориентированные на CRUD-операции

Мы планируем создать 4 маршрута. А именно:
  • CREATE – создание заметок.
  • READ –чтение заметок.
  • UPDATE –обновление заметок.
  • DELETE –удаление заметок.
Освоив эту схему, вы сможете понять, как, с помощью Node, организовать практически любой необходимый REST-маршрут.

Для того, чтобы протестировать API, понадобится нечто, способное имитировать запросы клиентской части приложения. Решить эту задачу нам поможет отличная программа, которая называется Postman . Она позволяет выполнять простые HTTP-запросы с заданным телом и параметрами.

Установите Postman. Теперь всё готово к настройке маршрутов.

О структуре проекта

В большинстве руководств по Node.js (и во множестве реальных приложений) все маршруты размещают в одном большом файле route.js . Мне такой подход не очень нравится. Если разложить файлы по разным папкам, это улучшит читаемость кода, приложением будет легче управлять.

Наше приложение большим не назовёшь, но предлагаю сделать всё как надо, учитывая, всё же, его скромные масштабы. Создайте следующие папки: папку app , а внутри неё – routes . В папке routes создайте файлы index.js и note_routes.js . Другими словами, структура проекта будет выглядеть так: root > app > routes > index.js и note_routes.js .

Mkdir app cd app mkdir routes cd routes touch index.js touch note_routes.js
Такая структура, для маленького приложения, может показаться избыточной, но она окажется очень кстати в более крупной системе, построенной на базе нашего примера. К тому же, любые проекты лучше всего начинать, используя лучшие из существующих наработок.

Создание заметок: маршрут CREATE

Начнём с маршрута CREATE. Для этого ответим на вопрос: «Как создать заметку?».
Прежде чем приступить к созданию заметок, нам понадобится расширить инфраструктуру приложения. В Express маршруты оборачивают в функцию, которая принимает экземпляр Express и базу данных как аргументы.

Выглядеть это может так:

// routes/note_routes.js module.exports = function(app, db) { };
Теперь можно экспортировать эту функцию через index.js :

// routes/index.js const noteRoutes = require("./note_routes"); module.exports = function(app, db) { noteRoutes(app, db); // Тут, позже, будут и другие обработчики маршрутов };
Импортируем то, что получилось, в server.js :

// server.js const express = require("express"); const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); const app = express(); const port = 8000; require("./app/routes")(app, {}); app.listen(port, () => { console.log("We are live on " + port); });
Обратите внимание на то, что так как базу данных мы пока не настроили, вторым аргументом передаётся пустой объект.

Теперь создаём маршрут CREATE. Синтаксис здесь довольно простой:

Module.exports = function(app, db) { app.post("/notes", (req, res) => { // Здесь будем создавать заметку. res.send("Hello") }); };
Когда приложение получает POST-запрос по пути ‘/notes’, оно исполнит код внутри функции обратного вызова, передав ей объект запроса (который содержит параметры запроса или JSON-данные) и объект ответа (который, понятно, используется для ответа).

То, что у нас получилось, уже можно протестировать. Отправим, с помощью Postman, POST-запрос по адресу localhost:8000/notes .


В ответ на запрос должно прийти «Hello»

Отлично. Первый маршрут создан. Следующий шаг – добавление к запросу параметров, обработка их в API, и, наконец – сохранение заметки в базе данных.

Параметры запроса

В Postman перейдите на вкладку Body и добавьте несколько пар ключ-значение, выбрав радиокнопку x-www-form-urlencoded . А именно, первым ключом будет title , его значение – My Note Title . Второй ключ – body , его значение – What a great note .

Это добавит к запросу закодированные данные, которые можно будет обработать в API.


Заголовок моей заметки, да и она сама – очень просты, а вы тут можете проявить фантазию

В файле note_route.js , просто выведем тело заметки в консоль.

// note_routes.js module.exports = function(app, db) { app.post("/notes", (req, res) => { console.log(req.body) res.send("Hello") }); };
Попробуйте отправить запрос с помощью Postman, и вы увидите… undefined .

К сожалению, Express не может самостоятельно обрабатывать формы в URL-кодировке. Тут нам на помощь придёт ранее установленный пакет body-parser.

// server.js const express = require("express"); const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); const app = express(); const port = 8000; app.use(bodyParser.urlencoded({ extended: true })); require("./app/routes")(app, {}); app.listen(port, () => { console.log("We are live on " + port); });
Теперь, после выполнения POST-запроса, его тело можно будет увидеть в терминале в виде объекта.

{ title: "My Note Title", body: "What a great note." }
Чтобы первый маршрут полностью заработал, осталось лишь настроить базу данных и добавить в неё заметку.

Для быстрого создания и настройки базы данных воспользуемся сервисом mLab . Работать с ним легко, для маленьких объёмов информации он бесплатен.

Создайте учётную запись на сайте mLab и разверните новую базу данных MongoDB. Для этого нажмите на кнопку Create New в разделе MongoDB Deployments , в появившемся окне, в разделе Plan , выберите Single-node . В списке Standard Line , выберите Sandbox и дайте базе данных имя. Далее, в окне управления базой, перейдите на вкладку Users и добавьте пользователя базы данных, задав имя и пароль.


Новый пользователь базы данных

Скопируйте с той же страницы второй URL – строку подключения к базе данных.


URL для подключения к базе данных

В корень проекта добавьте директорию config , создайте в ней файл db.js .

Mkdir config cd config touch db.js
В файл db.js добавьте следующее:

Module.exports = { url: здесь будет ваш URL };
Не забудьте добавить в URL имя пользователя и пароль (не те, что от учётной записи в mLab, а те, что создавали для базы данных). Если вы размещаете проект на Github, не забудьте включить в него файл .gitignore (вроде этого). Так вы не сделаете всеобщим достоянием имя и пароль для работы с базой.

Теперь, в server.js , можно использовать MongoClient для подключения к базе данных и обернуть в функцию, которая передаётся ему при создании, настройки приложения:

// server.js const express = require("express"); const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); const db = require("./config/db"); const app = express(); const port = 8000; app.use(bodyParser.urlencoded({ extended: true })); MongoClient.connect(db.url, (err, database) => { if (err) return console.log(err) require("./app/routes")(app, database); app.listen(port, () => { console.log("We are live on " + port); }); })
На этом подготовка инфраструктуры закончена. С этого момента будем заниматься исключительно путями.

Добавление записей в базу данных

MongoDB хранит данные в коллекциях (collections), которые полностью оправдывают своё название. В нашем случае заметки будут храниться в коллекции, которая, как несложно догадаться, будет называться notes .

В ходе настройки клиента, ему была передана строка подключения к базе данных, аргумент db . В коде маршрутов доступ к базе можно получить так:

Db.collection("notes")
Создание заметки в базе равносильно вызову команды insert для коллекции notes:

Const note = { text: req.body.body, title: req.body.title} db.collection("notes").insert(note, (err, results) => { }
После успешного завершения команды (или после того, как она, по какой-нибудь причине, не сможет выполниться), нужно либо отправить в ответ только что созданный объект заметки, либо – сообщение об ошибке. Вот код note_routes.js , дополненный с учётом этих рассуждений:

// note_routes.js module.exports = function(app, db) { app.post("/notes", (req, res) => { const note = { text: req.body.body, title: req.body.title }; db.collection("notes").insert(note, (err, result) =>
Испытайте то, что получилось. Отправьте из Postman POST-запрос (с флагом x-www-form-urlencoded ), задав на вкладке Body значения полей title и body .

Ответ должен выглядеть примерно так:


Успешное добавление записи в базу

Чтение заметок: маршрут READ

Инфраструктура, которую мы подготовили выше, подходит для всех маршрутов, поэтому теперь дело пойдёт быстрее.

Итак, мы собираемся запросить только что созданную заметку, пройдя по пути localhost:8000/notes/{id заметки} . В нашем случае путь будет выглядеть так: localhost:8000/notes/585182bd42ac5b07a9755ea3 .

Если ID одной из уже созданных заметок у вас нет, можете заглянуть в базу на mLab и найти его там, или создать новую заметку и скопировать её идентификатор.

Вот как это выглядит в note_route.js :

// note_routes.js module.exports = function(app, db) { app.get("/notes/:id", (req, res) => { }); app.post("/notes", (req, res) => { const note = { text: req.body.body, title: req.body.title }; db.collection("notes").insert(note, (err, result) => { if (err) { res.send({ "error": "An error has occurred" }); } else { res.send(result.ops); } }); }); };
Так же, как и раньше, мы собираемся вызвать некую команду для коллекции базы данных заметок. Применим для этого метод findOne .

// note_routes.js module.exports = function(app, db) { app.get("/notes/:id", (req, res) => { const details = { "_id": <ТУТ БУДЕТ ID> }; db.collection("notes").findOne(details, (err, item) => { if (err) { res.send({"error":"An error has occurred"}); } else { res.send(item); } }); }); app.post("/notes", (req, res) => { const note = { text: req.body.body, title: req.body.title }; db.collection("notes").insert(note, (err, result) => { if (err) { res.send({ "error": "An error has occurred" }); } else { res.send(result.ops); } }); }); };
Идентификатор из параметров URL можно вытащить с помощью конструкции req.params.id . Однако если просто вставить строку вместо <<>> из кода выше, работать это не будет.

MongoDB требуется ID не в виде строки, а в виде специального объекта. Он называется ObjectID .

Вот что, после небольших изменений, у нас получилось:

// note_routes.js var ObjectID = require("mongodb").ObjectID; module.exports = function(app, db) { app.get("/notes/:id", (req, res) => { const id = req.params.id; const details = { "_id": new ObjectID(id) }; db.collection("notes").findOne(details, (err, item) => { if (err) { res.send({"error":"An error has occurred"}); } else { res.send(item); } }); }); app.post("/notes", (req, res) => { const note = { text: req.body.body, title: req.body.title }; db.collection("notes").insert(note, (err, result) => { if (err) { res.send({ "error": "An error has occurred" }); } else { res.send(result.ops); } }); }); };
Испытайте это с одним из идентификаторов заметок, имеющихся в базе данных. Ответ в Postman должен выглядеть так:


Успешный запрос заметки из базы

Удаление заметок: маршрут DELETE

Удаление объектов – это практически то же самое, что их поиск в базе. Только вместо функции findOne используется функция remove . Вот полный код соответствующего пути. Здесь выделено то, что отличается от кода уже существующего метода, обрабатывающего запрос GET.

// note_routes.js // ... app.delete("/notes/:id", (req, res) => { const id = req.params.id; const details = { "_id": new ObjectID(id) }; db.collection("notes").remove(details, (err, item) => { if (err) { res.send({"error":"An error has occurred"}); } else { res.send("Note " + id + " deleted!"); } }); }); // ...

Обновление заметок: маршрут UPDATE

А вот и последний маршрут. Обработка запроса PUT – это, по сути, гибрид операций READ и CREATE. Сначала надо найти объект, потом – обновить его в соответствии с поступившими в запросе данными. Сейчас, если вы, испытывая предыдущий фрагмент кода, удалили свою единственную заметку, создайте ещё одну.

Вот код маршрута обновления заметок:

// note_routes.js // ... app.put ("/notes/:id", (req, res) => { const id = req.params.id; const details = { "_id": new ObjectID(id) }; const note = { text: req.body.body, title: req.body.title }; db.collection("notes").update(details, note, (err, result) => { if (err) { res.send({"error":"An error has occurred"}); } else { res.send(note); } }); }); // ...
Теперь любую заметку можно редактировать. Вот, как это выглядит:


Успешное обновление заметки

Обратите внимание на недостаток нашего примера. Если в PUT-запросе не будет тела или заголовка заметки, соответствующие поля в базе будут просто очищены.

Я не нагружал пример дополнительными проверками. Если хотите, можете сами доработать операцию обновления заметок, внося в базу новые данные только в том случае, если запрос сформирован верно.

Итоги

Теперь у вас есть рабочее Node API, которое поддерживает четыре основные операции CRUD. Серверная часть приложения умеет, реагируя на HTTP-запросы клиента, создавать в базе данных заметки, находить их, удалять и редактировать.

Основная цель моего рассказа – познакомить всех желающих со связкой Node + Express + MongoDB и с методикой разработки серверных приложений. Конечно, если сегодня состоялось ваше первое знакомство с этими инструментами, для того, чтобы во всё лучше вникнуть, вам понадобится почитать документацию. Однако, понимание происходящего позволит вам быстро восполнить пробелы в знаниях и приступить к работе над собственными проектами, использовав, в качестве отправной точки, приложение, которым мы с вами тут занимались.

Если у вас есть опыт работы с Node.js, Express и MongoDB в реальных проектах, может быть, вы посоветуете что-нибудь полезное новичкам? А если вы только что всё это впервые попробовали – ждём ваших впечатлений.

Основы Node JS & Express (III).

Разбираемся, что такое npm и для чего он нужен. Устанавливаем Express и шаблонизатор EJS. Делаем полную подготовительную работу и начинаем создавать свой собственный сайт на NodeJS.

Теперь с параметрами, которые постоянно будут меняться.

Если нам нужно создать ссылку на некое значение, после /mews/значение . Оно будет меняться. Например: 23, part или любое др значение.

App.get("/news/:id", function(req, res){ res.send("ID is - " + req.params.id); });

В зависимости от этого параметра мы можем брать данные из БД (базы данных) и выводить конкретную статью.

Нам нужен некий html файл куда мы будем передавать данные нашего id и в зависимости от этих данных выводить ту или иную информацию.

Нам нужен некий шаблонизатор .

Благодаря Express мы можем использовать несколько шаблонизаторов.

Поскольку EJS является дополнительным пакетом, то нам нужно его установить.

Нажать Enter

После этого он установится в наш проект.

Он позволяет передавать данные в различные шаблоны, причем эти шаблоны будут иметь расширение.ejs .

В этих шаблонах мы сможем выводить наш html код вместе с вставленным в него js кодом (переменными, выводить циклы и многое другое).

Будет такой шаблон страницы, который будет изменяться в зависимости от переданных в него данных.

Первое, что нам нужно сделать это указать какой view engine мы будем использовать.

View engine - по сути шаблонизатор.

Поскольку их существует огромное количество, а мы выбрали именно EJS, то мы должны его указать в нашем index.js файле.

Сразу после инициализации переменной app .

App.set("view-engine", "ejs");

Все файлы, которые мы будем отображать, по умолчанию будут искаться в папке views .

На том же уровне, где index.js создадим папку views .

В ней создадим новый файл news.ejs . Это будет некий шаблон, который мы будем наполнять.

В эти шаблоны мы можем помещать самый обычный html-код.

Новости

Новостная страница.

Для этого мне не нужно использовать метод.send или.sendFile , а мне потребуется метод render() .

Метод render() берет нужный файл (шаблон) в папке views и может отобразить его в браузере. Плюс в этот шаблон он может передать некие параметры.

Расширение в методе render() можно не указывать. Далее можно передать некие параметры в сам шаблон. Поэтому мы передаем вторым параметром - объект. В нем может быть большое количество свойств и значений.

Допустим, что мы решили передать некий параметр newsId со значением req.params.id - то есть значение будет как раз сам вызываемый id .

App.get("/news/:id", function(req, res){ render("news", {newsId: req.params.id}); });

Таким образом в шаблон news будет передано значение, которое будет называться newsId со значением id .

В файл news.ejs мы можем все это принять и отобразить.

Немного изменим наш файл news.ejs . В заголовок страницы будем выводить ID.

Все можно посмотреть в документации к шаблонизатору EJS (ссылка выше).

Новостная страница c ID = <%= newsId %>

Файл /views/news.ejs

Новости

Новостная страница c ID = <%= newsId %>

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eaque numquam libero, veniam ipsum similique odit molestiae esse quia blanditiis magni debitis aliquam, pariatur nam quaerat quas nemo, facilis temporibus laboriosam.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maiores enim vitae dolore nemo quas aliquam quia corrupti rerum ipsam ad nesciunt, architecto, pariatur officiis. Maxime iste ullam quibusdam, nobis voluptas!

Файл index.js

Let express = require("express"); let app = express(); app.set("view engine", "ejs"); app.get("/", function(req, res){ res.sendFile(__dirname + "/index.html"); }); app.get("/about", function(req, res){ res.sendFile(__dirname + "/about.html"); }); app.get("/news/:id", function(req, res){ res.render("news", {newsId: req.params.id}); }); app.listen(8080);

Мы можем передавать несколько параметров. Например:

App.get("/news/:id", function(req, res){ res.render("news", {newsId: req.params.id, newParam: 535 }); });

А в файле news.ejs выведем его на страницу, например так:

<%= newParam %>

Помимо этого мы можем передавать и собственные объекты. Например, создадим объект:

App.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4}; res.render("news", {newsId: req.params.id, newParam: 535}); });

И этот объект мы тоже можем передавать. Вначале определяем название того, что будем передавать, а потом указываем что передаем.

Например:

App.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4}; res.render("news", {newsId: req.params.id, newParam: 535, obj: obj }); });

Title = <%= obj.title %>

ID = <%= obj.id %>

<%= newParam %>

Передача в шаблон массива.

Создадим массив данных и выведем его с помощью цикла.

App.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4, paragraphs:["Параграф", "Обычный текст", "Числа: 3, 7, 24", 476] }; res.render("news", {newsId: req.params.id, newParam: 535, obj: obj}); });

Теперь в самом шаблоне мы просто выведем этот массив циклом:

    <% obj.paragraphs.forEach(function(item) { %>
  • <%= item %>
  • <% }); %>

Статические файлы и промежуточное ПО.

Файлы, которые можно включить в другие файлы.

Сейчас у нас есть один шаблон - news.ejs , а представьте. что их много. Десятки. И вам потребуется внести изменения в какую-то часть кода, который встречается во всех этих файлах. Придется вносить множество изменений.

Чтобы этого избежать, можно использовать файлы, которые можно включать в другие файлы. Например. Есть файл с шапкой сайта. И если нужно что-то изменить, то достаточно внести изменения только в один файл, так как к другим он просто подключается.

В папке шаблонов views создадим папку blocks , а в ней файл hrader.ejs .

Файл hrader.ejs

  • На главную
  • Про нас
  • Новости

Теперь нам нужно этот файл подключить во всех шаблонах. Идем в файл news и сразу после открывающего тега body записываем:

<% include blocks/header.ejs %>

Путь указывается от папки views , поскольку шаблонизатор всегда начинает искать там.

Статические файлы.

Создадим новую папку на уровне index.js с названием public . В ней будут находиться все статические файлы. Это css-файлы, картинки, документы и пр. Все те файлы. которые будут вызываться с различных страниц нашего сайта.

В этой папке создадим еще одну папку - css и уже в ней создадим файл style.css .

В него перенесем весь код стилей из файла index.ejs

В файлах.ejs подключаем стили:

Если теперь проверить, то ничего не произойдет. Стили не подключатся.

Для подключения статических файлов нам нужно использовать промежуточное программное обеспечение:

В файле index.js вверху, сразу за app.set , мы должны написать:

App.use("/public",);

И теперь, если мы где то будем использовать ссылку начинающуюся с /public сам NodeJS и Express будет понимать, что мы используем статические файлы и будет подключать все верно.

Вторым - где мы их ищем express.static("public") т. есть в папке /public .

Если обобщить, то в коде app.use("/public", express.static("public")); мы отслеживаем ту ссылку, которую прописываем в

Если бы у на было бы вот так:

То и в этом коде было бы:

App.use("/assets", express.static("public"));

В данном случае public указывает на папку!

Если так и оставить, то никаких изменений не произойдет. Файл подключится, потому что мы будем отслеживать ссылку assets .

App.use("/assets ", express.static("public "));

Для того, чтобы не путаться, обычно, делают ссылку и папку одного имени. Чаще всего это именно public .

Промежуточное ПО - это то, что мы делаем до того пока отправим какие-то данные на страницу (сервер).

В данном случае это и есть наше промежуточное ПО.

Создание HTML-формы и получение данных

Первое, что мы сделаем это добавим саму форму на наш сайт.

Открываем файл about.ejs и сюда мы будем добавлять форму используя технологию bootstrap.

В окно поиска вводим Forms и на найденной странице копируем первую форму сверху.

Сохраним и запустим.

POST -запрос.

Так так мы будем выполнять POST -запрос, то нам необходимо добавить в форму несколько атрибутов.

Method="post" - т.к POST -запрос

И action="" - это то куда нужно перенаправить пользователя после того,как он нажмет "Отправить". В нашем случае это:

Все остальное нам нужно сделать в файле index.js

Первым делом нам необходимо скачать пакет, который называется body-parser .

Он нам позволяет брать POST -запрос, который идет от формы и обрабатывать его. Проще говоря - получать все данные из формы.

Для установки, в папке проекта в КС пишем.

npm install body-parser

Нажимаем - Enter .

Пакет устанавливается.

После установки стоит следовать простой инструкции.

Перейдем на сайте в раздел Examples и найдем там раздел Express route-specific

  1. Подключаем нужные для нас модули.
  2. Var bodyParser = require("body-parser")

    Var urlencodedParser = bodyParser.urlencoded({ extended: false })

    То есть тот парсер, который позволит нам брать данные из POST -запроса и работать с ними как нам будет нужно.

  3. Далее, на основе документации, мы видим, что нам нужно отслеживать именно POST -запрос и передавать в него некое промежуточное ПО (urlencodedParser). Ранее мы уже отслеживали GET- запросы.

Данные полученные из формы мы выведем в консоль.

Console.log(req.body);

Можно сразу добавить проверку. Если не передано никаких данных из формы, мы выдадим просто ошибку.

If (!req.body) return res.sendStatus(400)

В самой форму нужно указать для полей атрибут name . Это будут названия свойств, а значение - это то, что введет пользователь.

О нас. <% include blocks/header.ejs %>

Заголовок второго уровня.

На главную

третьего уровня заглавие.

Значимость этих проблем настолько очевидна, что консультация с широким активом позволяет выполнять важные задания по разработке систем массового участия. Идейные соображения высшего порядка, а также рамки и место обучения кадров позволяет оценить значение системы обучения кадров, соответствует насущным потребностям. Не следует, однако забывать, что консультация с широким активом способствует подготовки и реализации системы обучения кадров, соответствует насущным потребностям.

Не следует, однако забывать, что консультация с широким активом способствует подготовки и реализации системы обучения кадров, соответствует насущным потребностям.

We"ll never share your email with anyone else.

Let express = require("express"); var bodyParser = require("body-parser"); let app = express(); var urlencodedParser = bodyParser.urlencoded({ extended: false }); app.set("view engine", "ejs"); app.use("/public", express.static("public")); app.get("/", function(req, res){ res.render("index"); }); app.get("/about", function(req, res){ res.render("about"); }); app.post("/about", urlencodedParser, function(req, res){ if (!req.body) return res.sendStatus(400); console.log(req.body); res.render("about"); }); app.get("/news", function(req, res) { res.render("news-common",{newParam1:"Param-1"}); }); app.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4, paragraphs:["Параграф", "Обычный текст", "Числа: 3, 7, 24", 476]}; res.render("news", {newsId: req.params.id, newParam: 535, obj: obj}); }); app.listen(8080);

Введем данные и нажмем отправить. В консоли мы увидим вывод этих данных (свойство - значение).

Страница перезагрузится после отправки формы и в консоли мы увидим данные, которые отправили.

Теперь мы можем сделать так, чтобы после отправки мы показывали другие данные.

Изменим немного код в файле index.js

App.post("/about", urlencodedParser, function(req, res){ if (!req.body) return res.sendStatus(400); console.log(req.body); res.render("about-success", {data: req.body} ); });

Таким образом мы будем выводить страницу about-success.ejs и мы ее сейчас создадим в папке views . Вторым параметром мы передадим данные формы в виде объекта. - {data: req.body}

О нас. <% include blocks/header.ejs %>

Hello, It is my first page on Node.js

Заголовок второго уровня.

На главную

Равным образом укрепление и развитие структуры способствует подготовки и реализации системы обучения кадров, соответствует насущным потребностям. Равным образом начало повседневной работы по формированию позиции требуют от нас анализа существенных финансовых и административных условий.

Спасибо

Email: <%= data.email %>
Pass: <%= data.pass %>
isCheck: <%= data.check %>

Таким образом вы можете отслеживать данные, которые поступают из форм, проверять их на соответствие и если пользователь что-то не заполнил, то выдавать ошибку и т.д.

Кроме этого было бы удобно отправлять эти данные на почту или сохранять в БД (базу данных).

Если вы хотите отправить их на почту. то в npm есть еще один пакет - Nodemailer . Этот пакет позволяет отправлять данные непосредственно на почту. Использовать его просто. И с помощью него вы можете получать по почте все данные формы, заполненной пользователем.

NodeJS предоставляет нам множество дополнительных пакетов. Например Express мы использовали для того, чтобы проще отслеживать ссылки и использовать шаблонизаторы. Body-parseer для того, чтобы принимать данные полученные из формы. Nodemailer - для отправки данных на почту.

Как получать данные из URL-строки.

Иногда нужно получить такой тип данных из адресной строки:

http://localhost:8080/news/12?filter-id&city=london

Разберем, как получить эти данные на примере вот этого кода из файла index.js:

App.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4, paragraphs:["Параграф", "Обычный текст", "Числа: 3, 7, 24", 476]}; res.render("news", {newsId: req.params.id, newParam: 535, obj: obj}); });

Просто выведем эти данные в консоль:

App.get("/news/:id", function(req, res){ let obj = { title:"Новость", id: 4, paragraphs:["Параграф", "Обычный текст", "Числа: 3, 7, 24", 476]}; console.log(req.query); res.render("news", {newsId: req.params.id, newParam: 535, obj: obj}); });

В консоли мы увидим

{ filter: "id", city: "london" }

Это может быть иногда полезно.

Начало работы с Express

Последнее обновление: 18.11.2018

В этой главе мы рассмотрим создание сервера с помощью фреймворка Express . Казалось бы, зачем нам нужен дополнительный фреймворк, если мы можем воспользоваться готовым модулем http, который есть в Node.js API. Однако Express сам использует модуль http, но вместе с тем предоставляет ряд готовых абстракций, которые упрощают создание сервера и серверной логики, в частности, обработка отправленных форм, работа с куками, CORS и т.д.

Создадим для проекта новый каталог, который назовем, к примеру, expressapp . Для хранения информации обо всех зависимостях проекта определим в этом каталоге новый файл package.json :

{ "name": "expressapp", "version": "1.0.0", "dependencies": { "express": "^4.16.4" } }

Создадим в каталоге проекта новый файл app.js :

// подключение express const express = require("express"); // создаем объект приложения const app = express(); // определяем обработчик для маршрута "/" app.get("/", function(request, response){ // отправляем ответ response.send("

Привет Express!

"); }); // начинаем прослушивать подключения на 3000 порту app.listen(3000);

Для использования Express в начале надо создать объект, который будет представлять приложение:

Const app = express();

Для обработки запросов в Express определено ряд встроенных функций, и одной из таких является функция app.get() . Она обрабатывает GET-запросы протокола HTTP и позволяет связать маршруты с определенными обработчиками. Для этого первым параметром передается маршрут, а вторым - обработчик, который будет вызываться, если запрос к серверу соответствует данному маршруту:

App.get("/", function(request, response){ // отправляем ответ response.send("

Привет Express!

"); });

Маршрут "/" представляет корневой маршрут.

Для запуска сервера вызывается метод app.listen() , в который передается номер порта.

Запустим проект и обратимся в браузере по адресу http://localhost:3000/ :

И что важно, Express опирается на систему маршрутов, поэтому все другие запросы, которые не соответствуют корневому маршруту "/", не будут обрабатываться:

Теперь изменим файл app.js :

Const express = require("express"); const app = express(); app.get("/", function(request, response){ response.send("

Главная страница

"); }); app.get("/about", function(request, response){ response.send("

О сайте

"); }); app.get("/contact", function(request, response){ response.send("

Контакты

"); }); app.listen(3000);

Теперь в приложении определено три маршрута, которые будут обрабатываться сервером.

$ npm install express

или, чтобы иметь доступ к команде express, установите глобально:

$ npm install -g express

Быстрый старт

Проще всего начать работу с Express можно выполнив команду express , которая сгенерирует приложение:

Создание приложения:

$ npm install -g express $ express /tmp/foo && cd /tmp/foo

Установка зависимостей:

$ npm install -d

Запуск сервера:

Создание сервера

Чтобы создать экземпляр express.HTTPServer , просто вызовите метод createServer() . С помощью нашего экземпляра приложения мы можем задавать маршруты, основанные на HTTP-методах, в данном примере app.get() .

var app = require("express").createServer(); app.get("/", function(req, res){ res.send("hello world"); }); app.listen(3000);

Создание HTTPS-сервера

Чтобы инициализировать express.HTTPSServer , мы совершаем те же действия, что и выше, но к тому де передаем объект опций, содержащий ключ, сертификат и другие параметры, о которых написано в документации модуля https NodeJS.

var app = require("express").createServer({ key: ... });

Конфигурирование

Express поддерживает произвольные окружения (environments), как например, production и development . Разработчики могут использовать метод configure() , чтобы добавить нужные для данного окружения функции. Когда configure() вызывается без имени окружения, он будет срабатывать в любом окружении прежде чем сработает любой configure , в котором окружение задано.

В приведенном ниже примере мы просто используем опцию dumpExceptions и в режиме разработки выдаем клиенту в ответ стек-трейс исключения. В обоих же режимах мы используем прослойку methodOverride и bodyParser . Обратите внимание на использование app.router , который сам по себе позволяет монтировать маршруты - в противном случае они монтируются при первом вызове app.get() , app.post() и т.д.

app.configure(function(){ app.use(express.methodOverride()); app.use(express.bodyParser()); app.use(app.router); }); app.configure("development", function(){ app.use(express.static(__dirname + "/public")); app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure("production", function(){ var oneYear = 31557600000; app.use(express.static(__dirname + "/public", { maxAge: oneYear })); app.use(express.errorHandler()); });

Для окруженией со схожими настройками можно передавать несколько имен окружений:

app.configure("stage", "prod", function(){ // config });

Для внутренних и произвольных настроек в Express есть методы set(key[, val]) , enable(key) , disable(key) :

app.configure(function () { app.set("views", __dirname + "/views"); app.set("views"); // => "/absolute/path/to/views" app.enable("some feature"); // все равно что app.set("some feature", true); app.disable("some feature"); // все равно что app.set("some feature", false); app.enabled("some feature") // => false });

Чтобы задать окружение мы можем установить переменную окружения NODE_ENV . Например:

$ NODE_ENV=production node app.js

Это очень важно, потому что множество механизмов кэширования включаются только в окружении production .

Настройки

Из коробки Express поддерживает следующие настройки:

  • home - базовый путь приложения, который используется для res.redirect(), а также для прозрачной поддержки примонтированных приложений.
  • views корневая директория представлений. По умолчанию текущая_папка/views
  • view engine - шаблонизатор по умолчанию для представлений, вызываемых без расширения файла.
  • view options - объект, отражающий глобальные опции представлений
  • view cache - включить кэширование представлений (включается в окружении production)
  • case sensitive routes - включить маршруты, чувствительные к регистру
  • strict routing - если включено, то завершающие слэши больше не игннорируются
  • jsonp callback - разрешить методу res.send() прозрачную поддержку JSONP

Маршрутизация

Express использует HTTP-методы для обеспечения осмысленного, выразительного API маршрутизации. Например, мы хотим, чтобы по запросу /user/12 отображался профиль пользователя с id=12 . Для этого мы определяем привелденный ниже маршрут. Значения, связанные с именованными полями, доступны в объекте res.params .

app.get("/user/:id", function(req, res){ res.send("user " + req.params.id); });

Маршрут это просто строка, которая внутри движка компилируется в регулярное выражение. Например, когда компилируется /user/:id , то получается регулярное выражение вроде такого:

\/user\/([^\/]+)\/?

Также можно сразу передавать регулярное выражение. Но поскольку группы в регулярных выражениях не именуются, к ним можно добраться в req.params по номерам. Так первая группа попадает в req.params , вторая в req.params и т.д.

app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){ res.send(req.params); });

Теперь возьмем curl и пошлем запрос на вышеупомянутый маршрут:

$ curl http://dev:3000/user $ curl http://dev:3000/users $ curl http://dev:3000/users/1 ["1",null] $ curl http://dev:3000/users/1..15 ["1","15"]

Ниже приведены несколько примеров маршрутов и пути, которые могут им соответствовать:

"/user/:id" /user/12 "/users/:id?" /users/5 /users "/files/*" /files/jquery.js /files/javascripts/jquery.js "/file/*.*" /files/jquery.js /files/javascripts/jquery.js "/user/:id/:operation?" /user/1 /user/1/edit "/products.:format" /products.json /products.xml "/products.:format?" /products.json /products.xml /products "/user/:id.:format?" /user/12 /user/12.json

Например, мы можем послать POST-ом некоторый JSON и ответить тем же JSON-ом, используя прослойку bodyParser , который умеет парсить JSON запрос (как впрочем и другие запросы) и помещать ответ в req.body:

var express = require("express"), app = express.createServer(); app.use(express.bodyParser()); app.post("/", function(req, res) { res.send(req.body); }); app.listen(3000);

Как правило мы используем “глупое” поле (например, /user/:id), у которого нет ограничений. Но если мы, к примеру, хотим ограничить ID пользователя только числовыми символами, можно использовать /user/:id(+) . Такая конструкция не будет срабатывать, если значение поля содержит нечисловые символы.

Передача управления на другой маршрут

Вызвав третий аргумент - next() , можно передать управление на следующий маршрут. Если соответствие не найдено, управление передается назад в Connect, и прослойки продолжают вызываться в порядке, в котором они были включены с помощью use() . Так же работают несколько маршрутов, имеющих один и тот же путь. Они просто вызываются по очереди, до того момента, когда один их них ответит вместо того, чтобы вызвать next() .

app.get("/users/:id?", function(req, res, next) { var id = req.params.id; if (id) { // делаем что-то } else { next(); } }); app.get("/users", function(req, res) { // делаем что-то другое });

Метод app.all() полезен, если нужно выполнить одну и ту же логику для всех HTTP-методов. Ниже мы используем этот метод для извлечения юзера из базы данных и назначения его в req.user .

var express = require("express"), app = express.createServer(); var users = [{ name: "tj" }]; app.all("/user/:id/:op?", function(req, res, next) { req.user = users; if (req.user) { next(); } else { next(new Error("cannot find user " + req.params.id)); } }); app.get("/user/:id", function(req, res) { res.send("viewing " + req.user.name); }); app.get("/user/:id/edit", function(req, res) { res.send("editing " + req.user.name); }); app.put("/user/:id", function(req, res) { res.send("updating " + req.user.name); }); app.get("*", function(req, res) { res.send("what???", 404); }); app.listen(3000);

Прослойки

Прослойки фреймворка Connect можно передавать в express.createServer() точно так же, как если бы использовался обычный Connect-сервер. Например:

var express = require("express"); var app = express.createServer(express.logger(), express.bodyParser());

Так же можно использовать use() . Так удобнее добавлять прослойки внутри блоков configure() , что более прогрессивно.

app.use(express.logger({ format: ":method:url" }));

Обычно с прослойками Connect мы можем подключить Connect следующим образом:

var connect = require("connect"); app.use(connect.logger()); app.use(connect.bodyParser());

Это не совсем удобно, поэтому Express повторно экспортирует Connect-овские прослойки:

app.use(express.logger()); app.use(express.bodyParser());

Порядок прослоек имеет значение. Так, когда Connect получает запрос, выполняется первая прослойка, добавленная через createServer() или use() . Она вызывается с тремя параметрами: request , response и callback-функция, обычно называемая next . когда вызывается next() , управление передается на вторую прослойку и т.д. Это важно учитывать, так так множество прослоек зависят друг от друга. Например methodOverride() обращается к req.body.method для перегрузки HTTP-метода, а bodyParser() парсит тело запроса, чтобы заполнить req.body . Другой пример - парсинг cookies и поддержка сессий - вначале необходимо вызывать use() для cookieParser() , затем для session() .

Множество Express-приложений может иметь строчку app.use(app.router) . Это может показаться странным, но это нужно просто для того, чтобы явным образом указать прослойку, которая включает в себя все созданные нами маршруты. Эту прослойку можно включать в любом порядке, хотя по умолчанию она включается в конце. Изменяя ее позицию, можно управлять очередностью ее выполнения. Например, нам нужен обработчик ошибок, который будет срабатывать после всех других прослоек и отображать любое исключение, переданное в него с помощью next() . Или же может понадобиться понизить очередность выполнения прослойки, обслуживающей статические файлы, чтобы позволить другим маршрутам перехватывать запросы к таким файлам и, например, считать количество скачиваний и т.д. Вот как это может выглядеть:

app.use(express.logger(...)); app.use(express.bodyParser(...)); app.use(express.cookieParser(...)); app.use(express.session(...)); app.use(app.router); app.use(express.static(...)); app.use(express.errorHandler(...));

Сначала мы добавляет logger() - он будет оборачивать метод req.end() , чтобы предоставлять нам данные о скорости ответа. Потом мы парсим тело запроса (если таковое имеется), затем куки, далее сессию, чтобы req.session был уже определен, когда мы доберемся до маршрутов в app.router . Если, например, запрос GET /javascripts/jquery.js будет обрабатываться маршрутами, и мы не вызовем next() , то прослойа static() никогда не получит этот запрос. Однако, если мы определим маршрут, как показано ниже, можно будет записывать статистику, отклонять загрузки, списывать оплату за загрузки, и т.д.

var downloads = {}; app.use(app.router); app.use(express.static(__dirname + "/public")); app.get("/*", function(req, res, next) { var file = req.params; downloads = downloads || 0; downloads++; next(); });

Маршруты-прослойки

Маршруты могут использовать маршрутные прослойки путем передачи методу дополнительных коллбэков (или массивов). Это полезно, если нужно ограничить доступ либо подгружать какие-либо данные перед использованием маршрута, и т.д.

Обычно асинхронное получение данных может выглядеть примерно как показано ниже (тут мы берем параметр:id и грузим данные юзера).

app.get("/user/:id", function(req, res, next) { loadUser(req.params.id, function(err, user) { if (err) return next(err); res.send("Viewing user " + user.name); }); });

Чтобы придерживаться принципа DRY и повысить читабельность кода, можно организовать такую логику с помощью прослоек. Как можно заметить, абстрагируя логику с помощью прослоек, можно как добиться повторного использования прослоек, так и сделать код маршрута более красивым.

function loadUser(req, res, next) { // тут мы грузим юзера из базы данных var user = users; if (user) { req.user = user; next(); } else { next(new Error("Failed to load user " + req.params.id)); } } app.get("/user/:id", loadUser, function(req, res) { res.send("Viewing user " + req.user.name); });

Можно добавлять несколько маршрутных прослоек, и они будут выполняться последовательно, чтобы обеспечить различную логику, как, например, ограничение доступа к пользовательскому аккаунту. В нижеприведенном примере только авторизованный юзер может редактировать свой аккаунт.

function andRestrictToSelf(req, res, next) { req.authenticatedUser.id == req.user.id ? next() : next(new Error("Unauthorized")); } app.get("/user/:id/edit", loadUser, andRestrictToSelf, function(req, res) { res.send("Editing user " + req.user.name); });

Принимая во внимание тот факт, что прослойки - это просто функции, можно написать функцию, которая бы возвращала прослойку (чтобы обеспечить еще более выразительное и гибкое решение), как показано ниже.

function andRestrictTo(role) { return function(req, res, next) { req.authenticatedUser.role == role ? next() : next(new Error("Unauthorized")); } } app.del("/user/:id", loadUser, andRestrictTo("admin"), function(req, res) { res.send("Deleted user " + req.user.name); });

Часто используемые “стеки” прослоек можно передавать как массивы произвольной глубины и древовидности (они будут применяться рекурсивно):

var a = , b = , all = ; app.get("/foo", a, function() {}); app.get("/bar", a, function() {}); app.get("/", a, middleware3, middleware4, function() {}); app.get("/", a, b, function() {}); app.get("/", all, function() {});

Полный пример можно посмотреть в репозитории .

Бывают случаи, когда надо пропустить остальные прослойки маршрута в стеке, но продолжить выполнение следующих маршрутов. Для этого надо вызывать next() с аргументом route: next("route") . Если не осталось маршрутов для выполнения, Express ответит ошибкой 404 Not Found .

HTTP-методы

Мы уже неоднократно пользовались app.get() , однако Express также предоставляет прочие HTTP-методы - app.post() , app.del() и т.д.

Самый распространенный пример использования POST - это отправка формы. В примере ниже мы просто делаем HTML-форму. А потом управление будет передаваться маршруту, который мы определим в следующем примере.

По умолчанию Express не знает, что ему делать с телом запроса, поэтому мы должны добавить прослойку bodyParser() , которая будет парсить тело запроса, закодированное в application/x-www-form-urlencoded или application/json , и помещать результаты парсинга в req.body . Для этого мы должны сказать use() , как показано ниже:

app.use(express.bodyParser());

Теперь нижеприведенный маршрут будет иметь доступ к объекту req.body.user , у которого будут свойства name и email:

app.post("/", function(req, res) { console.log(req.body.user); res.redirect("back"); });

В случае использования формой таких методов как PUT, можно использовать скрытый инпут по имени _method , который позволяет изменить HTTP-метод. Чтобы этого добиться, нужно сначала задействовать прослойку methodOverride() , которая будет помещена после bodyParser() , что позволит ей использовать req.body , содержащий поля переданной формы.

app.use(express.bodyParser()); app.use(express.methodOverride());

Эти прослойки не задействованы по умолчанию, потому что Express не обязательно должен сразу обладать полным функционалом. В зависимости от нужд приложения, можно и не использовать их. И тогда методы PUT и DELETE все так же будут доступны, но уже напрямую. В то же вреям methodOverride - это отличное решение для HTML-форм. Ниже показан пример использования метода PUT:

app.put("/", function() { console.log(req.body.user); res.redirect("back"); });

Обработка ошибок

У Express есть метод app.error() , который принимает все исключения, брошенные маршрутами, или переданные в виде next(err) . Ниже пример, как обслуживать несколько страниц с использованием самодельного исключения NotFound:

function NotFound(msg) { this.name = "NotFound"; Error.call(this, msg); Error.captureStackTrace(this, arguments.callee); } NotFound.prototype.__proto__ = Error.prototype; app.get("/404", function(req, res) { throw new NotFound; }); app.get("/500", function(req, res) { throw new Error("keyboard cat!"); });

Можно вызывать app.error() несколько раз, как показано ниже. Тут мы проверяем instanceof NotFound и показываем страницу 404 , или же передаем управление следующему обработчику ошибок.

Заметьте, что эти обработчики могут быть определены где угодно, поскольку они все равно будут помещены ниже обработчиков маршрутов в listen() . Это позволяет их определять внутри блоков configure() , так что можно обрабатывать исключения по-разному в зависимости от текущего окружения.

app.error(function(err, req, res, next) { if (err instanceof NotFound) { res.render("404.jade"); } else { next(err); } });

Для просто ты мы принимаем здесь, что все ошибки имеют код 500, но вы можете это изменить как угодно. Например когда Node делает операции с файловой системой, можно получить объект ошибки с полем error.code = ENOENT , что означает “не найден файл или директория”, мы можем использовать это в обработчике ошибок и показывать соответствующую страницу.

app.error(function(err, req, res) { res.render("500.jade", { error: err }); });

Также приложения могут использовать для обработки исключений Connect-овскую прослойку errorHander . Например, если нужно в окружении development показывать исключения в stderr , можно сделать так:

app.use(express.errorHandler({ dumpExceptions: true }));

Также в ходе разработки нам могут понадобиться клевые HTML-странички, показывающие переданные или брошенные исключения. В таком случае нужно установить showStack в true:

app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));

Прослойка errorHandler также отвечает в JSON, если клиентом передан заголовок Accept: application/json , что полезно для разработки AJAX-приложений.

Пред-обработки параметров маршрута

Пред-обработки параметров маршрута могут существенно улучшить читабельность приложения, через явную загрузку данных и валидацию URL запроса. Например, если вы постоянно извлекаете какие-то данные для определенных запросов (например грузите пользовательские данные для /user/:id), можно сделать что-то вроде этого:

app.get("/user/:userId", function(req, res, next) { User.get(req.params.userId, function(err, user) { if (err) return next(err); res.send("user " + user.name); }); });

С пред-условиями на наши параметры запроса можно навесить callback-функции, которые бы выполняли валидацию, ограничене доступа, или даже загрузку данных из базы данных. В примере ниже мы вызываем app.param() с именем параметра, на который хотим навесить callback. Как можно заметить мы получаем аргумент id , который содержит имя поля. Таким образом мы загружаем объект пользователя и выполняем обычную обработку ошибок и простой вызов next() , чтобы передать управление на следующее пред-условие либо уже на обработчик маршрута.

app.param("userId", function(req, res, next, id) { User.get(id, function(err, user) { if (err) return next(err); if (!user) return next(new Error("failed to find user")); req.user = user; next(); }); });

Вышеуказанные действия, как уже говорилось, значительно улучшают читабельность кода и позволяют легко использовать одну логику в разных местах приложения:

app.get("/user/:userId", function(req, res) { res.send("user " + req.user.name); });

Рендеринг представлений

Имена файлов представлений образуются по схеме { имя } . { движок } , где { движок } - это название модуля шаблонизатора, который должен быть подключен. Например представление layout.ejs говорит системе представлений, что надо сделать require("ejs") . Чтобы интегрироваться в Express, загружаемый модуль должен экспортировать метод exports.compile(str, options) , и возвращать функцию. Чтобы изменить это поведение, можно пользоваться методом app.register() - он позволяет проассоциировать расширения файлов с определенными движками. Например можно сделать, чтобы foo.html рендерился движком ejs .

Ниже - пример, использующий Jade для рендеринга index.html . И поскольку мы не используем layout:false , отрендеренный контент представления index.jade будет передан как локальная переменная body в представление layout.jade.

app.get("/", function(req, res) { res.render("index.jade", { title: "My Site" }); });

Настройка view engine позволяет указать шаблонизатор по умолчанию. Так например при использовании Jade можно сделать так:

app.set("view engine", "jade");

что позволит нам рендерить так:

res.render("index");

А не так:

res.render("index.jade");

Когда шаблонизатор установлен через view engine , расширения файлов не нужны. Однако мы по-прежнему можем использовать сразу несколько шаблонизаторов:

res.render("another-page.ejs");

В Express также есть настройка view options , которая будет накладываться при каждом рендеринге представления. Например если вы не так часто используете лэйауты, можно написать так:

app.set("view options", { layout: false });

Что можно при необходимости потом перегрузить в вызове res.render() :

res.render("myview.ejs", { layout: true });

Когда же нужен другой лэйаут, можно также указать путь. Например, если у нас view engine установлен в jade , и файл лэйаута называется./views/mylayout.jade , можно просто передать:

res.render("page", { layout: "mylayout" });

В противном случае можно передать расширение файла:

res.render("page", { layout: "mylayout.jade" });

Пути могут быть также абсолютными:

res.render("page", { layout: __dirname + "/../../mylayout.jade" });

Хороший пример - это указание нестандартных открывающих и закрывающих тегов движка ejs :

app.set("view options", { open: "{{", close: "}}" });

Фрагменты представлений

Система представлений Express имеет встроенную поддержку фрагментов и коллекций, своего рода мини-представлений. Например, вместо того, чтобы итерировать в представлении циклом для отображения списка комментариев, можно просто использовать фрагмент collection:

partial("comment", { collection: comments });

Если другие опции или локальные переменные не нужны, то можно пропустить объект и просто передать массив данных. Пример ниже равносилен предыдущему:

partial("comment", comments);

В случае использовании коллекций мы имеем несколько “волшебных” локальных переменных:

  • firstInCollection - true , если это первый объект
  • indexInCollection - индекс объекта в коллекции
  • lastInCollection - true , если это последний объект
  • collectionLength - длина коллекции

Переданные или сгенерированные локальные переменные имеет более высокий приоритет, однако локальные переменные, переданные в родительское представление, также доступны и в дочернем. Так например, если мы рендерим представление с помощью partial("blog/post", post) и оно породит локальную переменную post , а представление, вызвавшее эту функцию, имело локальную переменную user , то user также будет виден в представлении blog/post .

Дополнительную документацию см в разделе res.partial() .

Примечание: используйте коллекции осторожно, так как рендеринг массива в 100 элементов означает рендеринг 100 представлений. Для простых коллекций лучше итерировать циклом внутри представления, а не использовать коллекции. Так нагрузка будет меньше.

Поиск представлений

Поиск представлений производится относительно родительского преставления. Например если у нас есть представление views/user/list.jade и внутри него мы вызываем фрагмент partial("edit") , система попытается загрузить представление views/user/edit.jade , тогда как partial("../messages") приведет к загрузке views/messages.jade

Система представлений также позволяет делать index-файлы. Например, мы можем вызвать res.render("users") , и это может загрузить как views/users.jade , так и views/users/index.jade .

Использовать index-файлы можно также из представления в той же директории. Так вызовом partial("users") можно обратиться к представлению../users/index вместо того чтобы вызывать partial("index") .

Шаблонизаторы

Ниже представлены несколько шаблонизаторов, часто используемых с Express:

  • EJS - встроенный JavaScript
  • CoffeeKup - шаблонизация на основе CoffeeScript
  • jQuery Templates для Node

Поддержка сессий

Поддержку сессий можно включить используя Connect-овскую прослойку session . Также для этого нам нужна вышележащая прослойка cookieParser , которая будет парсить куки и помещать их в req.cookies .

app.use(express.cookieParser()); app.use(express.session({ secret: "keyboard cat" }));

По умолчанию прослойка session использует Connect-овское хранилище в памяти, однако существует множество других решений. Например connect-redis поддерживает хранилище сессий в Redis . Вот как им пользоваться:

var RedisStore = require("connect-redis")(express); app.use(express.cookieParser()); app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));

Теперь свойства req.session и req.sessionStore будут доступны из всех маршрутов и последующих прослоек. Свойства req.session автоматически сохраняются при ответе. Вот как можно организовать корзину:

var RedisStore = require("connect-redis")(express); app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(express.session({ secret: "keyboard cat", store: new RedisStore })); app.post("/add-to-cart", function(req, res) { // допустим мы передали из формы несколько объектов // используем для этого bodyParser() var items = req.body.items; req.session.items = items; res.redirect("back"); }); app.get("/add-to-cart", function(req, res) { // Когда мы редиректим назат на GET /add-to-cart // мы можем проверить req.session.items && req.session.items.length // чтобы распечатать наше сообщение if (req.session.items && req.session.items.length) { req.flash("info", "You have %s items in your cart", req.session.items.length); } res.render("shopping-cart"); });

Объект req.session также имеет методы Session.touch() , Session.destroy() , Session.regenerate() для манипуляции сессиями. Для более полной информации см. документацию Connect Session .

Руководство по миграции

Разработчики, работавшие с Express 1.x могут обращаться к руководству по миграции , чтобы научить свои приложения работать с Express 2.x, Connect 1.x, и Node 0.4.x.

Request

req.header(key[, defaultValue])

Получить заголовок запроса key (нечувствительно к регистру) с необязательным значением по умолчанию DefaultValue:

req.header("Host"); req.header("host"); req.header("Accept", "*/*");

Заголовки Referrer и Referer - особый случай, обе конструкции будут работать:

// послан заголовок "Referrer: http://google.com" req.header("Referer"); // => "http://google.com" req.header("Referrer"); // => "http://google.com"

req.accepts(type)

Проверяет передан ли заголовок Accept , и подходит ли он под данный тип.

Когда заголовок Accept отсутствует, возвращается true . В противном случае проверяется соответствие типа, а потом подтипов. Можно передавать "html" которое внутренне конвертируется в "text/html" , используя таблицу соответствия MIME.

// Accept: text/html req.accepts("html"); // => true // Accept: text/*; application/json req.accepts("html"); req.accepts("text/html"); req.accepts("text/plain"); req.accepts("application/json"); // => true req.accepts("image/png"); req.accepts("png"); // => false

req.is(type)

Проверяет входящий запрос на наличие заголовка Content-Type и соответствие заданному MIME-типу.

// Пусть Content-Type: text/html; charset=utf-8 req.is("html"); req.is("text/html"); // => true // Пусть Content-Type теперь application/json req.is("json"); req.is("application/json"); // => true req.is("html"); // => false

В Express можно регистрировать собственные коллбэки для различных проверок запроса. Например, пусть нам нужно сделать красивую проверку является ли входящий запрос изображением. Для этого можно зарегистрировать коллбэк "an image" :

app.is("an image", function(req) { return 0 == req.headers["content-type"].indexOf("image"); });

Теперь внутри обработчиков маршрутов можно использовать его, чтобы проверять Content-Type вида "image/jpeg" , "image/png" и т.д.

app.post("/image/upload", function(req, res, next) { if (req.is("an image")) { // выполняем определенные действия } else { next(); } });

Не забывайте, что этот метод распространяется не только на Content-Type - вы можете делать любые проверки.

Также можно использовать подстановочные символы. Это упростит наш пример с изображением. Тут мы будем проверять только тип:

req.is("image/*");

Мы также можем также проверять подтип, как показано ниже. Тут проверка вернет true в случаях "application/json" , и "text/json" .

req.is("*/json");

req.param(name[, default])

Возвращает значение параметра name или - если оно не существует - default .

Проверяет параметры маршрута (req.params), например, /user/:id

Проверяет параметры строки запроса (req.query), например, ?id=12

Проверяет urlencoded-параметры тела запроса (req.body), например, id=12

Чтобы получать urlencoded-параметры тела запроса, должен существовать объект req.body . Для этого включите прослойку bodyParser() .

req.get(field, param)

Получает параметр поля заголовка. По умолчанию - пустая строка.

req.get("content-disposition", "filename"); // => "something.png" req.get("Content-Type", "boundary"); // => "--foo-bar-baz"

req.flash(type[, msg])

Помещает всплывающее сообщение в очередь.

req.flash("info", "email sent"); req.flash("error", "email delivery failed"); req.flash("info", "email re-sent"); // => 2 req.flash("info"); // => ["email sent", "email re-sent"] req.flash("info"); // => req.flash(); // => { error: ["email delivery failed"], info: }

Всплывающие сообщения также могут использовать форматные строки. По умолчанию доступна строка "%s" :

req.flash("info", "email delivery to _%s_ from _%s_ failed.", toUser, fromUser);

req.isXMLHttpRequest

Также имеет сокращение req.xhr . Проверяет заголовок X-Requested-With на предмет того, что запрос сделан с помощью XMLHttpRequest:

req.xhr req.isXMLHttpRequest

Response

res.header(key[, val])

Получает или устанавливает заголовок ответа.

res.header("Content-Length"); // => undefined res.header("Content-Length", 123); // => 123 res.header("Content-Length"); // => 123

res.charset

Устанавливает кодировку следующих заголовков Content-Type . Например, res.send() и res.render() по умолчанию будут "utf8" , а мы можем явно задать кодировку перед тем как рендерить шаблон:

res.charset = "ISO-8859-1"; res.render("users");

или перед тем, как отвечать с помощью res.send() :

res.charset = "ISO-8859-1"; res.send(str);

или с помощью встроенного в Node res.end() :

res.charset = "ISO-8859-1"; res.header("Content-Type", "text/plain"); res.end(str);

res.contentType(type)

Устанавливает заголовок ответа Content-Type .

var filename = "path/to/image.png"; res.contentType(filename); // Content-Type теперь "image/png"

Можно задавать Content-Type и строкой:

res.contentType("application/json");

Или просто расширением файла (без ведущей точки):

res.contentType("json");

res.attachment()

Устанавливает заголовок ответа Content-Disposition в "attachment" . Опционально может быть передано имя файла.

res.attachment("path/to/my/image.png");

res.sendfile(path[, options[, callback]])

Используется в res.download() для передачи произвольного файла.

res.sendfile("path/to/my.file");

Этод метод принимает необязательный параметр callback , который вызывается в случае ошибки или успеха передачи файла. По умолчанию вызывается next(err) , однако если передан callback , то надо это делать явно, или обрабатывать ошибку.

res.sendfile(path, function(err) { if (err) { next(err); } else { console.log("transferred %s", path); } });

Также можно передавать опции вызову fs.createReadStream() . Например, чтобы изменить размер буфера:

res.sendfile(path, { bufferSize: 1024 }, function(err) { // обработка... });

res.download(file[, filename[, callback[, callback2]]])

Передать данный файл как вложение (можно задать необязательное альтернативное имя файла).

res.download(‘path/to/image.png’);

res.download(‘path/to/image.png’, ‘foo.png’);

Это эквивалентно следующему:

res.attachment(file); res.sendfile(file);

Опционально можно задать callback вторым или третьим аргументом res.sendfile() . Внутри него вы можете отвечать, как если бы заголовок еще не был передан.

res.download(path, "expenses.doc", function(err) { // обработка... });

Также можно опционально передать второй коллбэк - callback2 . В нем обрабатываются ошибки, связанные с соединением. Однако в нем не следует пытаться посылать ответ.

res.download(path, function(err) { // ошибка или завершение }, function(err) { // ошибка соединения });

res.send(body|status[, headers|status[, status]])

Метод res.send() - высокоуровневое средство ответа, позволяющее передавать объекты (для JSON-ответа), строки (для HTML-ответа), экземпляры Buffer , или целые числа, определяющие статус-код (404 , 500 и т.д.). Вот как это используется:

res.send(); // 204 res.send(new Buffer("wahoo")); res.send({ some: "json" }); res.send(""); res.send("Sorry, cant find that", 404); res.send("text", { "Content-Type": "text/plain" }, 201); res.send(404);

По умолчанию заголовок Content-Type устанавливается автоматически. Однако если он был вручную, явным образом задан в res.send() или перед этим с помощью res.header() , или с помощью res.contentType() , то автоматической установки не произойдет.

Заметьте, что в этом методе происходит завершение ответа (аналогично res.end()), поэтому, если нужно выдать множественный ответ, или поток, то нужнопользоваться res.write() .

res.json(obj[, headers|status[, status]])

Посылает JSON-ответ с необязательными заголовками и статус-кодом. Этот метод идеален для организации JSON-API, однако JSON можно посылать также с помощью res.send(obj) (что впрочем не идеально, если нужно послать только строку, закодированную в JSON, так как res.send(string) отправит HTML)

res.json(null); res.json({ user: "tj" }); res.json("караул!", 500); res.json("Ничего не найдено", 404);

res.redirect(url[, status])

Перенаправляет на заданный URL. Статус-код по умолчанию - 302 .

res.redirect("/", 301); res.redirect("/account"); res.redirect("http://google.com"); res.redirect("home"); res.redirect("back");

Express поддерживает сокращения для редиректов - по умолчанию это "back" и "home" . При этом "back" перенаправляет на URL, заданный в заголовке Referrer (или Referer), а "home" использует настройку "home" (по умолчанию "/").

res.cookie(name, val[, options])

Устанавливает значение cookie с именем name в val . Опции: httpOnly , secure , expires , и т.д. Опция path по умолчанию принимает значение, установленное в настройке "home" , обычно это "/" .

// "Запомнить меня" на 15 минут res.cookie("rememberme", "yes", { expires: new Date(Date.now() + 900000), httpOnly: true });

Свойством maxAge можно задавать expire относительно Date.now() в миллисекундах. Таким образом наш вышеупомянутый пример теперь можно переписать так:

res.cookie("rememberme", "yes", { maxAge: 900000 });

Чтобы парсить входящие куки, использйте прослойку cookieParser , которая формирует объект req.cookies:

app.use(express.cookieParser()); app.get("/", function(req, res) { // используем req.cookies.rememberme });

res.clearCookie(name[, options])

Очищаем cookie по имени name , присваивая параметру expires дату в далеком прошлом. Опции те же, что у res.cookie() , path точно так же по умолчанию равен настройке "home" .

res.clearCookie("rememberme");

res.render(view[, options[, fn]])

Рендерит представление view с заданными опциями options и необязательным коллбеком fn . Когда задана fn , ответ клиенту не происходит автоматически, в противном же случае делается ответ text/html с кодом 200 .

Передаваемые опции являются по совместительству локальными переменными представления. Например, если мы хотим передать переменую user и запретить лэйаут, мы делаем это в одном объекте:

var user = { name: "tj" }; res.render("index", { layout: false, user: user });

Также объект options служит для передачи опций. Например, если вы передаете свойство status , то оно не только становится доступно представлению, а также устанавливает статус-код ответа. Это также полезно, если шаблонизатор принимает определенные опции, например debug или compress . Ниже - пример того, как можно отрендерить страницу ошибки - тут передается status как для его отображения, так и для установки статус-кода res.statusCode .

res.render("error", { status: 500, message: "Internal Server Error" });

res.partial(view[, options])

Рендерит фрагмент с заданными опциями. Этот метод всегда доступен из представления как локальная переменная.

  • object - объект, передаваемый в представление
  • as - имя переменной, которая будет представлять объект object или каждый элемент коллекции collection , переданных в представление. По умолчанию - имя представления.
    • as: "something" - добавит локальную переменную something
    • as: this - будет использовать элемент коллекции как контекст представления (this)
    • as: global - сольёт воедино свойства элемента колекции и локальные переменные представления
    • collection - массив объектов. Имя его происходит из имени представления. Например video.html будет имметь внутри объект video.

Следующие конструкции эквивалентны друг другу и имя коллекции, переданное фрагменту, везде будет "movie" .

partial("theatre/movie.jade", { collection: movies }); partial("theatre/movie.jade", movies); partial("movie.jade", { collection: movies }); partial("movie.jade", movies); partial("movie", movies); // Внутри представления: moovie.director

Чтобы сменить имя локальной переменной с "movie" на "video" , можно использовать опцию as:

partial("movie", { collection: movies, as: "video" }); // Внутри представления: video.director

Также мы можем сделать movie значением this внутри нашего представления, чтобы вместо movie.director можно было обращаться this.director .

partial("movie", { collection: movies, as: this }); // Внутри представления: this.director

Альтернативное решение - это развернуть свойства элемента коллекции в псевдо-глобальные (на самом деле локальные) переменные, используя as: global , такой вот “синтаксический сахар”:

partial("movie", { collection: movies, as: global }); // Внутри представления: director

Такая же логика применима не только к коллекциям, но и к объекту внутри фрагментного представления:

partial("movie", { object: movie, as: this }); // Внутри представления: this.director partial("movie", { object: movie, as: global }); // Внутри представления: director partial("movie", { object: movie, as: "video" }); // Внутри представления: video.director partial("movie", { object: movie }); // movie.director

Когда второй аргумент - не-коллекция (про признаку отсутствия.length), он считается объектом. При этом имя локальной переменной для этого объекта образуется из имени представления.

var movie = new Movie("Nightmare Before Christmas", "Tim Burton") partial("movie", movie) // => Внутри представления: movie.director

Исключение из этого правила - это когда передается простой объект ("{}" или "new Object"), тогда он считается объектом с локальными переменными (прим перев.: и недоступен по имени внутри фрагментного представления). Например в следующем примере можно ожидать, что будет локальная переменная "movie" , однако поскольку это простой объект, локальные переменные уже "director" и "title" , то есть его свойства:

var movie = { title: "Nightmare Before Christmas", director: "Tim Burton" }; partial("movie", movie)

Для таких случаев, когда нужно передавать именно простой объект, просто присвойте его какому-нибудь свойству, или используйте свойства object , которое унаследует имя объекта из имени файла. Перечисленные ниже примеры эквивалентны:

partial("movie", { locals: { movie: movie } }) partial("movie", { movie: movie }) partial("movie", { object: movie })

Такой же самый API может быть использован из маршрута, чтобы можно было ответить HTML-фрагментом через AJAX или WebSockets, например можно отрендерить коллекцию пользователей напрямую из маршрута:

app.get("/users", function(req, res) { if (req.xhr) { // передаем в ответ каждого юзера из коллекции // переданной в представление "user" res.partial("user", users); } else { // отвечаем полным лэйаутом со страницей списка пользователей // шаблон которой внутри себя делает partial("user", users) // ну и добавляет какой-то интерфейс res.render("users", { users: users }); } });

res.local(name[, val])

Получить или установить заданную локальную переменную. Под локальными переменными в данном случае имеются в виду переменные, передаваемые в методы рендеринга представления, например в res.render() .

app.all("/movie/:id", function(req, res, next) { Movie.get(req.params.id, function(err, movie) { // Делает присваивание res.locals.movie = movie res.local("movie", movie); }); }); app.get("/movie/:id", function(req, res) { // локальная переменная movie уже есть // , но мы можем ее дополнить, если нужно res.render("movie", { displayReviews: true }); });

res.locals(obj)

Присвоить несколько локальных переменных с помощью данного объекта obj . Следующее эквивалентно:

res.local("foo", bar); res.local("bar", baz); res.locals({ foo: bar, bar, baz });

Server

app.set(name[, val])

Установить настройку приложение name в значение val , или получить значение настройки name , если val отсутствует:

app.set("views", __dirname + "/views"); app.set("views"); // => ...path...

Также можно добраться до настроек через appsettings:

app.settings.views // => ...path...

app.enable(name)

Устанавливает настройку name в true:

app.enable("some arbitrary setting"); app.set("some arbitrary setting"); // => true app.enabled("some arbitrary setting"); // => true

app.enabled(name)

Проверяет, равна ли true настройка name:

app.enabled("view cache"); // => false app.enable("view cache"); app.enabled("view cache"); // => true

app.disable(name)

Установить настройку name в false:

app.disable("some setting"); app.set("some setting"); // => false app.disabled("some setting"); // => false

app.disabled(name)

Проверяет, равна ли false настройка name:

app.enable("view cache"); app.disabled("view cache"); // => false app.disable("view cache"); app.disabled("view cache"); // => true

app.configure(env|function[, function])

Задает коллбэк-функцию callback для окружения env (или для всех окружений):

app.configure(function() { // выполняется для всех окружений }); app.configure("development", function() { // выполняется только для окружения "development" });

app.redirect(name, val)

Для res.redirect() мы можем определить сокращения (в области видимости приложения), как показано ниже:

app.redirect("google", "http://google.com");

Теперь в маршруте мы можем вызвать:

res.redirect("google");

Также можно делать динамические сокращения:

app.redirect("comments", function(req, res) { return "/post/" + req.params.id + "/comments"; });

Теперь можно сделать следующее и редирект динамически построится в соответствие с контекстом запроса. Если мы вызвали маршрут с помощью GET /post/12 , наш редирект будет /post/12/comments .

app.get("/post/:id", function(req, res) { res.redirect("comments"); });

В случае монтированного приложения res.redirect() будет учитывать точку монтирования приложения. Например, если блог-приложение смонтировано в /blog , следующий пример сделает редирект в /blog/posts:

res.redirect("/posts");

app.error(function)

Добавляет функцию-обработчик ошибок, которая первым параметром будет принимать все исключения, как показано ниже. Заметьте, что можно устанавливать несколько обработчиков ошибок, путем нескольких вызовов этого метода, однако метод должен вызывать next() , если он не хочет сам обрабатывать исключение:

app.error(function(err, req, res, next) { res.send(err.message, 500); });

app.helpers(obj)

Регистрирует статические помощники представлений.

app.helpers({ name: function(first, last) { return first + ", " + last }, firstName: "tj", lastName: "holowaychuk" });

Наше представление может теперь пользоваться переменными firstName и lastName и функцией name() .

<%= name(firstName, lastName) %>

Также Express предоставляет по умолчанию несколько локальных переменных:

  • settings - объект настроек приложения
  • layout(path) указать лэйаут прямо изнутри представления

Этот метод имеет псевдоним app.locals() .

app.dynamicHelpers(obj) {#app.dynamic-helpers}

Регистрирует динамические помощники представлений. Динамические помощники представлений - это просто функции, принимающие res , req и выполняемые в контексте экземпляра Server перед тем, как отрендерить любое представление. Возвращаемое значение такой функции становится локальной переменной, с которой функция ассоциирована.

app.dynamicHelpers({ session: function(req, res) { return req.session; } });

Теперь все наши представления будут иметь доступ к сессии - данные сессии будут доступны на манер session.name и т.д.:

<%= session.name %>

app.lookup

Возвращает обработчики маршрута, связанные с заданным путем path .

Допустим, есть такие маршруты:

Можно использовать функционал lookup для проверки того, какие мрашруты заданы. Это может пригодиться для фреймворков более высокого уровня, построенных на Express.

app.lookup.get("/user/:id"); // => app.lookup.get("/user/:id/:op?"); // => app.lookup.put("/user/:id"); // => app.lookup.all("/user/:id"); // => app.lookup.all("/hey"); // =>

Псевдонимом для app.lookup.HTTP_МЕТОД() является просто app.HTTP_МЕТОД() - без аргумента callback . Такое вот сокращение. Например следующее эквивалентно:

app.lookup.get("/user"); app.get("/user");

Каждая возвращенная функция дополняется полезными свойствами:

var fn = app.get("/user/:id/:op?"); fn.regexp // => /^\/user\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i fn.keys // => ["id", "op"] fn.path // => "/user/:id/:op?" fn.method // => "GET"

app.match

Возвращает массив коллбэк-функций, срабатывающих на заданный URL, который может содержатьстроку запроса, и т.д. Это может пригодиться, чтобы понять какие маршруты имеют возможность ответить.

Допустим, есть следующие маршруты:

app.get("/user/:id", function() {}); app.put("/user/:id", function() {}); app.get("/user/:id/:op?", function() {});

Вызов match для GET вернет две функции, поскольку:op в последнем маршруте необязательный параметр.

app.match.get("/user/1"); // =>

А следующий вызов вернет только один коллбэк для /user/:id/:op? .

app.match.get("/user/23/edit"); // =>

Можно использовать и all() , если нам не важен HTTP-метод

app.match.all("/user/20"); // =>

Каждая функция снабжается следующими свойствами:

var fn = app.match.get("/user/23/edit"); fn.keys // => ["id", "op"] fn.params // => { id: "23", op: "edit" } fn.method // => "GET"

app.mounted(fn)

Назначить коллбэк fn , который вызывается, когда этот Server передается в Server.use() .

var app = express.createServer(), blog = express.createServer(); blog.mounted(function(parent) { //parent - это app // this - это blog }); app.use(blog);

app.register(ext, exports)

Ассоциирует заданные экспортируемые свойства (exports) шаблонизатора с расширением ext файла шаблона.

app.register(".html", require("jade"));

Также это может пригодиться в случае с библиотеками, имя которых не совпадает в точности с расширением файла шаблона. Живой пример - Haml.js , который устанавливается npm -ом как "hamljs" , а мы можем зарегистрировать его на шаблоны ".haml" , а не ".hamljs" , как было бы по умолчанию:

app.register(".haml", require("haml-js"));

Кроме того app.register очень помогает в случае с шаблонизаторами, API которых не соответствует спецификациям Express. В примере ниже мы ассоциируем расширение.md с рендерером markdown -файлов. Рендерить в HTML будем только первый раз - для большей производительности - и будем поддерживать подстановку переменных вида "{name}" .

app.register(".md", { compile: function(str, options) { var html = md.toHTML(str); return function(locals) { return html.replace(/\{([^}]+)\}/g, function(_, name) { return locals; }); }; } });

app.listen(])

Биндим сокет сервера app к адресу host:port . Порт по умолчанию 3000 , хост - INADDR_ANY .

app.listen(); app.listen(3000); app.listen(3000, "n.n.n.n");

Аргумент port может быть также строкой, представляющей собой путь к unix domain socket :

app.listen("/tmp/express.sock");

Теперь попробуем:

$ telnet /tmp/express.sock GET / HTTP/1.1 HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 11 Hello World

Участники проекта

Основной вклад в проект внесли следующие лица:

  • TJ Holowaychuk (visionmedia)
  • Ciaran Jessup (ciaranj)
  • Aaron Heckmann (aheckmann)
  • Guillermo Rauch (guille)

Сторонние модули

Следующие модули работают с Express или построены на его основе:

  • обеспечивает ресурсную маршрутизацию
  • express-messages рендеринг всплывающих уведомлений
  • express-configure поддержка асинхронной конфигурации (загрузка данных из Redis, и т.д.)
  • express-namespace - пространства имен в маршрутах
  • express-expose простая публикация JS-кода в клиентскую часть приложения
  • express-params - расширения app.param()
  • express-mongoose - плагин для простого рендеринга результатов запросов Mongoose (ORM для MongoDB)

Express представляет собой популярный веб-фреймворк, написанный на JavaScript и работающий внутри среды исполнения node.js. Этот модуль освещает некоторые ключевые преимущества этого фреймворка, установку среды разработки и выполнение основных задач веб-разработки и развертывания.

Предварительные требования

Перед началом этого модуля вам необходимо представлять, что из себя представляет серверное программирование и веб-фреймворки, желательно из прочтения статей другого модуля Server-side website programming first steps . Знакомство с основными концепциями программирования и языком программирования JavaScript будет очень полезным, но оно не является обязательным для понимания базовых понятий этого модуля.

Заметка : Этот веб-сайт содержит множество источников для изучения JavaScript в контексте разработки на стороне клиента : JavaScript , JavaScript Guide , JavaScript Basics , JavaScript (изучение). Ключевые особенности и коцепции языка JavaScript остаются сходными и для серверной разработки на Node.js и используемый материал достаточно релевантен. Node.js предоставляет additional APIs для обеспечения функционала, который полезен для "безбраузерной" разработки, т.е. для создания HTTP-сервера и доступа к файловой системе, но не поддерживает JavaScript APIs для работы с браузером и DOM.

Это руководство обеспечит вас некоторой информацией о работе с Node.js и Express, но также существуют и другие многочисленные отличные ресурсы в Интернете и книгах - некоторые из них доступны из тем How do I get started with Node.js (StackOverflow) и (Quora).

Руководства

Введение в Express/Node В первой статье об Express мы ответим на вопросы "Что такое Node?" и "Что такое Express?" и дадим вам представление о том, что делает веб-фреймворк Express особенным. Мы расскажем об основных функциях и покажем вам некоторые из основных строительных блоков приложений Express (хотя на данный момент у вас еще нет среды разработки, в которой можно ее протестировать) . Настройка среды разработки Node (Express)

Теперь, когда вы знаете, что такое Express, мы покажем вам, как настроить и протестировать среду разработки Node/Express в Windows, Linux (Ubuntu) и Mac OS X. Независимо от того, какую популярную операционную систему вы используете, эта статья даст вам то, что вам нужно, чтобы начать разработку приложений Express.

Учебник Express: сайт LocalLibrary

Первая статья в нашей серии практических уроков объясняет, что вы будете изучать, и предоставит обзор веб-сайта «локальной библиотеки», над которым мы будем работать и развивать в последующих статьях.

Учебник Express часть 2: Создание скелета веб-сайта

В этой статье показано, как вы можете создать «скелет» веб-сайта, который затем можно будет заполнить с помощью маршрутов сайта, шаблонов/представлений и баз данных.

Учебник Express часть 3: Использование базы данных (с помощью Mongoose) В этой статье кратко представлены базы данных для Node/Express. Затем показывается, как мы можем использовать Mongoose для обеспечения доступа к баз данных для сайта LocalLibrary . В уроке объясняется, как объявляются объектная схема и модели, основные типы полей и базовая валидация. Также кратко показаны некоторые из основных способов доступа к данным модели. Учебник Express часть 4: Маршруты и контроллеры В этом уроке мы создадим маршруты (код обработки URL) с "фиктивным" обработчиком функций для всех конечных точек ресурсов, которые нам в конечном итоге понадобятся для сайта LocalLibrary . По завершении мы будем иметь модульную структуру нашего кода обработки маршрута, который мы можем расширить с помощью функций реального обработчика в следующих статьях. Мы также будем очень хорошо понимать, как создавать модульные маршруты, используя Express. Учебник Express часть 5: Отображение данных библиотеки Теперь мы готовы добавить страницы, на которых будут отображаться книги веб-сайта LocalLibrary и другие данные. Страницы будут включать главную страницу, которая показывает сколько записей определенного типа мы имеем и отдельную страницу для детального просмотра записи. По пути мы получим практический опыт в получении записей из баз данных и использовании шаблонов. Учебник Express часть 6: Работы с формами В этой части мы покажем вам, как работать с HTML формами в Express, используя Pug, и в частности, как создавать, обновлять и удалять документы из базы данных. Учебник Express часть 7: Выкладка в production Теперь когда вы создали восхитительный сайт LocalLibrary , вы захотите установить его на общедоступном сервере, чтобы он мог дать доступ персоналу библиотеки и пользователям в Интернет. В этой статье представлен обзор того, как вы можете найти хост для развертывания вашего сайта и что вам нужно сделать, чтобы подготовить ваш сайт к публикации. Установка LocalLibrary на PWS/Cloud Foundry В этой статье представлена практическая демонстрация того, как установить LocalLibrary на облаке Pivotal Web Services PaaS - это полнофункциональная альтернатива с открытым исходным кодом для Heroku, облачного сервиса PaaS используемого в части 7 этого учебника, представленного выше. PWS/Cloud Foundry опредленно стоит попробовать, если вы ищете альтернативу Heroku (или другому PaaS облачному сервису), или просто хотите попробовать что-то другое.