Express/Node introduction - Изучение веб-разработки

Express/Node introduction
=========================

* Обзор: Express Nodejs
* Далее

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

| Предварительные знания: | Базовая компьютерная грамотность. Общее понимание серверного программирования веб-сайтов и, в частности, механики клиент-серверного взаимодействия на веб-сайтах. |
| --- | --- |
| Цель: | Ознакомить вас с фреймворком Express и как он вписывается в среду Node, какие функции он предоставляет, и основные строительные блоки приложения Express. |

Что такое Express и Node?
-------------------------

Node (или более формально *Node.js*) - кросплатформенная среда исполнения с открытым исходным кодом, которая позволяет разработчикам создавать всевозможные серверные инструменты и приложения используя язык JavaScript. Среда исполнения предназначена для использования вне контекста браузера (т.е. выполняется непосредственно на компьютере или на серверной ОС). Таким образом, среда исключает API-интерфейсы JavaScript для браузера и добавляет поддержку более традиционных OS API-интерфейсов, включая библиотеки HTTP и файловых систем.

С точки зрения веб-серверной разработки Node имеет ряд преимуществ:

* Отличная производительность! Node был разработан для оптимизации пропускной способности и масштабируемости в веб-приложениях и очень хорошо справляется со многими распространёнными проблемами веб-разработки (например, веб-приложения реального времени).
* Код написан на «обычном старом JavaScript», а это означает, что затрачивается меньше времени при написании кода для браузера и веб-сервера связанное с «переключением технологий» между языками.
* JavaScript является относительно новым языком программирования и имеет преимущества от улучшения дизайна языка по сравнению с другими традиционными языками для веб-серверов (например, Python, PHP, и т.д.). Многие другие новые и популярные языки компилируются/конвертируются в JavaScript, поэтому вы можете также использовать CoffeeScript, ClosureScript, Scala, LiveScript, etc.
* Менеджер пакетов Node (NPM) обеспечивает доступ к сотням тысяч многоразовых пакетов. Он также имеет лучшее в своём классе разрешение зависимостей и может также использоваться для автоматизации большинства инструментов построения.
* Он портативен, имеет версии для Microsoft Windows, OS X, Linux, Solaris, FreeBSD, OpenBSD, WebOS, и NonStop OS. Кроме того, он имеет хорошую поддержку среди многих хостинг-провайдеров, которые часто предоставляют конкретную инфраструктуру и документацию для размещения сайтов, работающих на Node.
* Он имеет очень активную стороннюю экосистему и сообщество разработчиков, которые всегда готовы помочь.

Вы можете использовать Node.js для создания простого веб сервера используя пакет Node HTTP.

### Hello Node.js

Следующий пример создаёт веб сервер который обрабатывает любой HTTP запрос на URL `http://127.0.0.1:8000/` — когда запрос будет получен, скрипт ответит строкой "Hello World". Если вы уже установили node, можете, следуя шагам инструкции попробовать пример:

1. Откройте терминал (в Windows окно командной строки)
2. Создайте папку, куда вы хотите сохранить программу, к примеру `test-node` и перейдите в неё с помощью следующей команды:
```

cd test-node
```

3. Используя любимый текстовый редактор, создайте файл `hello.js` и вставьте в него код:

```js
// Загружаем HTTP модуль
const http = require("http");

const hostname = "127.0.0.1";
const port = 8000;

// Создаём HTTP-сервер
const server = http.createServer((req, res) => {
  // Устанавливаем HTTP-заголовок ответа с HTTP статусом и Content type
  res.writeHead(200, { "Content-Type": "text/plain" });

  // Отсылаем тело ответа "Hello World"
  res.end("Hello World\n");
});

// Выводим лог как только сервер будет запущен
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

4. Сохраните файл в папку, созданную выше.
5. Вернитесь в терминал и выполните следующую команду:
```

node hello.js
```

В итоге, перейдите по ссылке `http://localhost:8000` в вашем браузере; вы должны увидеть текст "**Hello World**" в верху слева на чистой странице.

Веб фреймворк
-------------

Другие общие для веб-программирования задачи не поддерживаются на прямую Node. Если вы хотите добавить специфичную поддержку различных HTTP методов (например `GET`, `POST`, `DELETE`, и т.д.) по разному для разных URL путей ("routes"), отдачу статических файлов, или использовать шаблоны для создания динамических ответов, вам нужно написать код самим, или можете отказаться от изобретения колеса и использовать фреймворк!

Введение в Express
------------------

Express - самый популярный веб-фреймворк для *Node*. Он является базовой библиотекой для ряда других популярных веб-фреймворков Node. Он предоставляет следующие механизмы:

* Написание обработчиков для запросов с различными HTTP-методами в разных URL-адресах (маршрутах).
* Интеграцию с механизмами рендеринга «view», для генерации ответов, вставляя данные в шаблоны.
* Установка общих параметров веб-приложения, такие как порт для подключения, и расположение шаблонов, которые используются для отображения ответа.
* «промежуточное ПО» для дополнительной обработки запроса в любой момент в конвейере обработки запросов.

В то время как сам express довольно минималистичный, разработчики создали совместимые пакеты промежуточного программного обеспечения для решения практически любой проблемы с веб-разработкой. Существуют библиотеки для работы с куки-файлами, сеансами, входами пользователей, параметрами URL, данными POST, заголовками безопасности и многими другими. Вы можете найти список пакетов промежуточного программного обеспечения, поддерживаемых командой Express в Express Middleware (наряду со списком некоторых популярных пакетов сторонних производителей) .

**Примечание:** Гибкость это палка о двух концах. Существуют пакеты промежуточного программного обеспечения (middleware) для решения практически любых проблем или для удовлетворения любых ваших требований, но правильный выбор подходящих пакетов иногда может быть проблемой. Также нет «правильного пути» для структурирования приложения, и многие примеры, которые вы можете найти в Интернете, не являются оптимальными или лишь показывают небольшую часть того, что вам нужно сделать для разработки веб-приложения.

Откуда это все взялось?
-----------------------

Node первоначально был выпущен только под Linux в 2009. Менеджер пакетов NPM был выпущен в 2010, а поддержка Windows была добавлена в 2012. Текущая LTS-версия Node v12.16.1 , в то время как последний выпуск Node версии 13.11.0. Это короткий экскурс в историю; обратитесь к Википедии, если вы хотите узнать больше).

Express первоначально был выпущен в ноябре 2010 и текущая версия API 4.17.1 вы можете отследить изменения и текущий релиз, и GitHub для более детальной информации о релизах.

Насколько популярен Node/Express?
---------------------------------

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

Не существует какого-либо доступного и точного измерения популярности серверных фреймворков (хотя сайты, такие как Hot Frameworks, пытаются оценить популярность, используя такие механизмы, как подсчёт количества проектов на GitHub и вопросов на StackOverflow для каждой платформы). Лучший вопрос заключается в том, достаточно ли популярны Node и Express, чтобы избежать проблем с непопулярными платформами. Они продолжают развиваться? Можете ли вы получить помощь, если вам это нужно? Есть ли у вас возможность получить оплачиваемую работу, если вы изучаете Express?

Как только мы посмотрим на список широкоизвестных компаний пользующихся Express, количество разработчиков участвующих в разработке Express, и громадному числу людей, которые занимаются поддержкой Express, то мы с уверенностью скажем - *Express* поистине популярный фреймворк!

Является ли Express ограничивающим?
-----------------------------------

Web-фреймворки часто принято делить на "ограничивающие" и "не ограничивающие".

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

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

Express не ограничивающий. Вы можете вставить в цепочку обработки (middleware) запросов практически любое совместимые промежуточные компоненты, которые вам нравятся. Вы можете структурировать приложение в одном файле или в нескольких, использую любую структуру каталогов. Иногда вы можете чувствовать, что у вас слишком много вариантов!

Как выглядит код Express?
-------------------------

В традиционных динамических веб-сайтах, веб-приложение ожидает HTTP-запроса от веб-браузера (или другого клиента). Когда запрос получен, приложение определяет, какое действие необходимо выполнить на основе URL шаблона и, возможно, связанной информации, содержащейся в данных `POST` или `GET`. В зависимости от того, что требуется, Express может затем читать или записывать данные из/в базы данных или выполнять другие задачи, в соответствии с полученным запросом. Затем приложение возвращает ответ в веб-браузер, зачастую динамически создавая HTML страницу для отображения браузером, вставляя извлечённые данные в заполнители HTML шаблона.

Express предоставляет методы позволяющие указать, какая функция вызывается для конкретного HTTP запроса (`GET`, `POST`, `SET`, etc.), и URL шаблон ("Route"), а также методы позволяющие указать, какой механизм шаблона ("view") используется, где находятся шаблоны файлов и какой шаблон использовать для вывода ответа. Вы можете использовать Express middleware для добавления поддержки файлов cookies, сеансов, и пользователей, получения `POST`/`GET` параметров, и т.д. Вы можете использовать любой механизм базы данных, поддерживаемый Node (Express не определяет поведение, связанное с базой данных).

В следующих разделах объясняются некоторые общие моменты, которые вы увидите при работе с кодом *Express* and *Node*.

### Helloworld Express

Сначала давайте рассмотрим стандартный пример Express Hello World (мы обсудим каждую часть этого ниже и в следующих разделах).

**Примечание:** Совет: Если у вас уже установлены Node и Express (или если вы устанавливаете их, как показано в следующей статье), вы можете сохранить этот код в файле с именем app.js и запустить его в командной строке, вызвав узел app.js. отражения).

```js
var express = require("express");
var app = express();

app.get("/", function (req, res) {
  res.send("Hello World!");
});

app.listen(3000, function () {
  console.log("Example app listening on port 3000!");
});
```

Первые две строки требуют () (импорт) модуля Express и создания приложения Express. Этот объект, который традиционно называется app, имеет методы для маршрутизации HTTP-запросов, настройки промежуточного программного обеспечения, рендеринга представлений HTML, регистрации механизма шаблонов и изменения параметров приложения, которые управляют поведением приложения (например, режим среды, чувствительны ли определения маршрута к регистру). , и т.д.)

Средняя часть кода (три строки, начинающиеся с app.get) показывает определение маршрута. Метод app.get () указывает колбэк-функцию, которая будет вызываться всякий раз, когда есть HTTP-запрос GET с путём ('/') относительно корня сайта. Колбэк-функция принимает запрос и объект ответа в качестве аргументов и просто вызывает send () для ответа, чтобы вернуть строку «Hello World!»

Последний блок запускает сервер через порт «3000» и печатает комментарий журнала в консоль. Когда сервер работает, вы можете перейти к localhost: 3000 в вашем браузере, чтобы увидеть возвращённый пример ответа.

### Импорт и создание модулей

Модуль - это библиотека / файл JavaScript, который вы можете импортировать в другой код с помощью функции require () Node. Express сам по себе является модулем, как и промежуточное программное обеспечение и библиотеки баз данных, которые мы используем в наших приложениях Express.

Приведённый ниже код показывает, как мы импортируем модуль по имени, используя в качестве примера платформу Express. Сначала мы вызываем функцию require (), определяя имя модуля в виде строки («express») и вызывая возвращённый объект для создания приложения Express. Затем мы можем получить доступ к свойствам и функциям объекта приложения.

```js
var express = require("express");
var app = express();
```

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

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

Чтобы сделать объекты доступными вне модуля, вам просто нужно назначить их объекту экспорта. Например, модуль square.js ниже представляет собой файл, который экспортирует методы area () и perimeter ():

```js
exports.area = function (width) {
  return width \* width;
};
exports.perimeter = function (width) {
  return 4 \* width;
};
```

Мы можем импортировать этот модуль, используя require (), а затем вызвать экспортированные методы, как показано:

```js
var square = require("./square"); // Here we require() the name of the file without the (optional) .js file extension
console.log("The area of a square with a width of 4 is " + square.area(4));
```

**Примечание:** Примечание. Вы также можете указать абсолютный путь к модулю (или имя, как мы делали изначально).

Если вы хотите экспортировать полный объект в одном назначении, а не создавать его по одному свойству за раз, назначьте его для module.exports, как показано ниже (вы также можете сделать это, чтобы сделать корень объекта экспорта конструктором или другой функцией) :

```js
module.exports = {
  area: function (width) {
    return width \* width;
  },

  perimeter: function (width) {
    return 4 \* width;
  },
};
```

Для получения дополнительной информации о модулях см. Modules (Node API docs).

### Использование асинхронных API

Код JavaScript часто использует асинхронные, а не синхронные API для операций, выполнение которых может занять некоторое время. Синхронный API - это тот, в котором каждая операция должна завершиться до начала следующей операции. Например, следующие функции журнала являются синхронными и выводят текст на консоль по порядку (первый, второй).

```js
console.log("First");
console.log("Second");
```

В отличие от этого, асинхронный API - это тот, в котором API начнёт операцию и сразу же вернётся (до завершения операции). После завершения операции API будет использовать некоторый механизм для выполнения дополнительных операций. Например, приведённый ниже код выведет «Second, First», потому что хотя метод setTimeout () вызывается первым и возвращается немедленно, операция не завершается в течение нескольких секунд.

```js
setTimeout(function () {
  console.log("First");
}, 3000);
console.log("Second");
```

Использование неблокирующих асинхронных API-интерфейсов ещё более важно в Node, чем в браузере, поскольку Node - это однопоточная среда выполнения, управляемая событиями. «Однопоточный» означает, что все запросы к серверу выполняются в одном потоке (а не порождаются в отдельных процессах). Эта модель чрезвычайно эффективна с точки зрения скорости и ресурсов сервера, но это означает, что если любая из ваших функций вызывает синхронные методы, выполнение которых занимает много времени, они будут блокировать не только текущий запрос, но и любой другой запрос, обрабатываемый ваше веб-приложение.

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

**Примечание:** Совет: Использование колбэков может быть довольно «грязным», если у вас есть последовательность зависимых асинхронных операций, которые должны выполняться по порядку, потому что это приводит к нескольким уровням вложенных колбэков. Эта проблема широко известна как «ад колбэков». Эту проблему можно решить с помощью хороших методов кодирования (см. https://callbackhell.com/), использования такого модуля, как async, или даже перехода к функциям ES6, таким как Promises.

**Примечание:** Примечание. Общим соглашением для Node и Express является использование колбэков с ошибками. В этом соглашении первое значение в ваших колбэк-функциях является значением ошибки, в то время как последующие аргументы содержат данные об успехе. В этом блоге есть хорошее объяснение того, почему этот подход полезен: путь Node.js - понимание колбэков с ошибками (fredkschott.com).

### Создание обработчиков маршрута

В нашем примере Hello World Express (см. Выше) мы определили функцию обработчика маршрута (колбэка) для HTTP-запросов GET к корню сайта ('/').

```js
app.get("/", function (req, res) {
  res.send("Hello World!");
});
```

Колбэк-функция принимает запрос и объект ответа в качестве аргументов. В этом случае метод просто вызывает send () в ответе, чтобы вернуть строку «Hello World!» Существует ряд других методов ответа для завершения цикла запрос / ответ, например, вы можете вызвать res.json () для отправки ответа JSON или res.sendFile () для отправки файла.

**Примечание:** Совет по JavaScript: вы можете использовать любые имена аргументов, которые вам нравятся, в колбэк-функциях; при вызове колбэка первый аргумент всегда будет запросом, а второй всегда будет ответом. Имеет смысл назвать их так, чтобы вы могли идентифицировать объект, с которым работаете, в теле колбэка.

Объект приложения Express также предоставляет методы для определения обработчиков маршрутов для всех других HTTP-глаголов, которые в основном используются одинаково: post (), put (), delete (), options (), trace (), copy ( ), lock (), mkcol (), move (), purge (), propfind (), proppatch (), unlock (), report (), mkactivity (), checkout (), merge ( ), m-search (), notify (), subscribe (), unsubscribe (), patch (), search () и connect ().

Существует специальный метод маршрутизации app.all (), который будет вызываться в ответ на любой метод HTTP. Это используется для загрузки функций промежуточного программного обеспечения по определённому пути для всех методов запроса. В следующем примере (из документации Express) показан обработчик, который будет выполняться для запросов к / secret независимо от используемого глагола HTTP (при условии, что он поддерживается модулем http).

```js
app.all("/secret", function (req, res, next) {
  console.log("Accessing the secret section ...");
  next(); // pass control to the next handler
});
```

Маршруты позволяют сопоставлять определённые шаблоны символов в URL-адресе, извлекать некоторые значения из URL-адреса и передавать их в качестве параметров обработчику маршрута (в качестве атрибутов объекта запроса, передаваемого в качестве параметра).

Часто полезно группировать обработчики маршрутов для определённой части сайта и получать к ним доступ с помощью общего префикса маршрута (например, сайт с вики может иметь все связанные с вики маршруты в одном файле и иметь к ним доступ с префиксом маршрута из / вики /). В Express это достигается с помощью объекта express.Router. Например, мы можем создать наш вики-маршрут в модуле с именем wiki.js, а затем экспортировать объект Router, как показано ниже:

```js
// wiki.js - Wiki route module

var express = require("express");
var router = express.Router();

// Home page route
router.get("/", function (req, res) {
  res.send("Wiki home page");
});

// About page route
router.get("/about", function (req, res) {
  res.send("About this wiki");
});

module.exports = router;
```

**Примечание:** Примечание. Добавление маршрутов к объекту Router аналогично добавлению маршрутов к объекту приложения (как показано ранее).

Чтобы использовать маршрутизатор в нашем главном файле приложения, нам потребуется () модуль route (wiki.js), а затем вызовите use () в приложении Express, чтобы добавить маршрутизатор в путь обработки промежуточного программного обеспечения. Эти два маршрута будут доступны из / wiki / и / wiki / about /.

```js
var wiki = require("./wiki.js");
// ...
app.use("/wiki", wiki);
```

Мы покажем вам намного больше о работе с маршрутами, и в частности об использовании маршрутизатора, позже в связанном разделе Routes and controllers .

### Использование промежуточного программного обеспечения

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

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

Большинство приложений используют стороннее промежуточное программное обеспечение для упрощения общих задач веб-разработки, таких как работа с файлами cookie, сессиями, аутентификацией пользователя, доступом к данным запросов POST и JSON, ведение журнала и т. д. Список пакетов промежуточного программного обеспечения, поддерживаемых командой Express, можно найти. (который также включает в себя другие популярные сторонние пакеты). Другие экспресс-пакеты доступны в диспетчере пакетов NPM.

Для использования стороннего промежуточного программного обеспечения сначала необходимо установить его в своё приложение с помощью NPM. Например, чтобы установить промежуточное программное обеспечение средства регистрации HTTP-запросов morgan, вы должны сделать следующее:

```bash
npm install morgan
```

Затем вы можете вызвать use () для объекта приложения Express, чтобы добавить промежуточное программное обеспечение в стек:

```js
var express = require('express');
var logger = require('morgan');
var app = express();
app.use(logger('dev'));
...
```

**Примечание:** Примечание. Промежуточное программное обеспечение и функции маршрутизации вызываются в том порядке, в котором они были объявлены. Для некоторого промежуточного программного обеспечения важен порядок (например, если промежуточное программное обеспечение сеанса зависит от промежуточного программного обеспечения cookie, то сначала должен быть добавлен обработчик cookie). Почти всегда случается так, что промежуточное ПО вызывается перед настройкой маршрутов, иначе ваши обработчики маршрутов не будут иметь доступа к функциям, добавленным вашим промежуточным ПО.

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

Вы можете добавить функцию промежуточного программного обеспечения в цепочку обработки с помощью app.use () или app.add (), в зависимости от того, хотите ли вы применить промежуточное программное обеспечение ко всем ответам или к ответам с определённым глаголом HTTP (GET, POST и т. д.). ). Маршруты задаются одинаково в обоих случаях, хотя маршрут необязателен при вызове app.use ().

В приведённом ниже примере показано, как можно добавить функцию промежуточного программного обеспечения, используя оба метода, а также с / без маршрута.

```js
var express = require("express");
var app = express();

// An example middleware function
var a\_middleware\_function = function (req, res, next) {
  // ... perform some operations
  next(); // Call next() so Express will call the next middleware function in the chain.
};

// Function added with use() for all routes and verbs
app.use(a_middleware_function);

// Function added with use() for a specific route
app.use("/someroute", a_middleware_function);

// A middleware function added for a specific HTTP verb and route
app.get("/", a_middleware_function);

app.listen(3000);
```

**Примечание:** Совет по JavaScript: выше мы объявляем функцию промежуточного программного обеспечения отдельно, а затем устанавливаем её в качестве колбэка. В нашей предыдущей функции обработчика маршрута мы объявили колбэк-функцию, когда она использовалась. В JavaScript любой подход является допустимым.

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

### Обслуживание статических файлов

Вы можете использовать промежуточное программное обеспечение express.static для обслуживания статических файлов, включая ваши изображения, CSS и JavaScript (static () - единственная функция промежуточного программного обеспечения, которая фактически является частью Express). Например, вы должны использовать строку ниже для обслуживания изображений, файлов CSS и файлов JavaScript из каталога с именем public на том же уровне, где вы вызываете узел:

```js
app.use(express.static("public"));
```

Любые файлы в публичном каталоге обслуживаются путём добавления их имени файла (относительно базового «публичного» каталога) к базовому URL. Так, например:
```

http://localhost:3000/images/dog.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/about.html
```

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

```js
app.use(express.static("public"));
app.use(express.static("media"));
```

Вы также можете создать виртуальный префикс для ваших статических URL-адресов, вместо добавления файлов к базовому URL-адресу. Например, здесь мы указываем путь монтирования, чтобы файлы загружались с префиксом "/ media":

```js
app.use("/media", express.static("public"));
```

Теперь вы можете загружать файлы, находящиеся в публичном каталоге, из префикса / media path.
```

http://localhost:3000/media/images/dog.jpg
http://localhost:3000/media/video/cat.mp4
http://localhost:3000/media/cry.mp3
```

Для получения дополнительной информации см. Serving static files in Express.

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

Ошибки обрабатываются одной или несколькими специальными функциями промежуточного программного обеспечения, которые имеют четыре аргумента вместо обычных трёх: (err, req, res, next). Например:

```js
app.use(function (err, req, res, next) {
  console.error(err.stack);
  res.status(500).send("Something broke!");
});
```

Они могут возвращать любой требуемый контент, но должны вызываться после всех других app.use () и маршрутизировать вызовы, чтобы они были последним промежуточным ПО в процессе обработки запросов!

Express поставляется со встроенным обработчиком ошибок, который заботится обо всех оставшихся ошибках, которые могут возникнуть в приложении. Эта промежуточная функция обработки ошибок по умолчанию добавляется в конец стека функций промежуточного программного обеспечения. Если вы передаёте ошибку в next () и не обрабатываете её в обработчике ошибок, она будет обработана встроенным обработчиком ошибок; ошибка будет записана клиенту с трассировкой стека.

**Примечание:** Примечание. Трассировка стека не включена в производственную среду. Чтобы запустить его в производственном режиме, необходимо установить переменную среды NODE\_ENV в «производство».

**Примечание:** Примечание. HTTP404 и другие коды состояния «ошибка» не считаются ошибками. Если вы хотите справиться с этим, вы можете добавить функцию промежуточного программного обеспечения для этого. Для получения дополнительной информации см. FAQ.

Для получения дополнительной информации см. Error handling (Express docs).

### Использование баз данных

Приложения Express могут использовать любой механизм базы данных, поддерживаемый Node (сам по себе Express не определяет каких-либо дополнительных действий / требований для управления базой данных). Есть много вариантов, включая PostgreSQL, MySQL, Redis, SQLite, MongoDB и т. Д.

Чтобы использовать их, вы должны сначала установить драйвер базы данных, используя NPM. Например, чтобы установить драйвер для популярной NoSQL MongoDB, вы должны использовать команду:

```bash
npm install mongodb
```

Сама база данных может быть установлена локально или на облачном сервере. В вашем экспресс-коде вам требуется драйвер, подключиться к базе данных, а затем выполнить операции создания, чтения, обновления и удаления (CRUD). Пример ниже (из документации Express) показывает, как вы можете найти записи «млекопитающих», используя MongoDB.

```js
var MongoClient = require("mongodb").MongoClient;

MongoClient.connect("mongodb://localhost:27017/animals", function (err, db) {
  if (err) throw err;

  db.collection("mammals")
    .find()
    .toArray(function (err, result) {
      if (err) throw err;

      console.log(result);
    });
});
```

Другим популярным подходом является косвенный доступ к вашей базе данных с помощью Object Relational Mapper («ORM»). При таком подходе вы определяете свои данные как «объекты» или «модели», и ORM отображает их в базовый формат базы данных. Этот подход имеет то преимущество, что как разработчик вы можете продолжать думать с точки зрения объектов JavaScript, а не семантики базы данных, и что есть очевидное место для выполнения проверки и проверки входящих данных. Подробнее о базах данных мы поговорим в следующей статье.

Для получения дополнительной информации см. Database integration (Express docs).

### Рендеринг данных (просмотров)

Механизмы шаблонов (в Express называемые «механизмами просмотра») позволяют указывать структуру выходного документа в шаблоне, используя заполнители для данных, которые будут заполняться при создании страницы. Шаблоны часто используются для создания HTML, но могут также создавать другие типы документов. В Express есть поддержка ряда шаблонных движков, и здесь есть полезное сравнение более популярных движков: Сравнение шаблонизаторов JavaScript: Jade, Mustache, Dust и More.

В своём коде настроек приложения вы задаёте механизм шаблонов для использования и место, где Express должен искать шаблоны, используя настройки «views» и «engine», как показано ниже (вам также нужно будет установить пакет, содержащий вашу библиотеку шаблонов). !)

```js
var express = require("express");
var app = express();

// Set directory to contain the templates ('views')
app.set("views", path.join(__dirname, "views"));

// Set view engine to use, in this case 'some\_template\_engine\_name'
app.set("view engine", "some\_template\_engine\_name");
```

Внешний вид шаблона будет зависеть от того, какой движок вы используете. Предполагая, что у вас есть файл шаблона с именем «index. <Template\_extension>», который содержит заполнители для переменных данных с именами «title» и «message», вы должны вызвать Response.render () в функции обработчика маршрута для создания и отправки ответа HTML. :

```js
app.get("/", function (req, res) {
  res.render("index", { title: "About dogs", message: "Dogs rock!" });
});
```

Для получения дополнительной информации см. Using template engines with Express (Express docs).

### Файловая структура

Express не делает никаких предположений относительно структуры или компонентов, которые вы используете. Маршруты, представления, статические файлы и другая логика конкретного приложения могут находиться в любом количестве файлов с любой структурой каталогов. Хотя вполне возможно иметь все приложения Express в одном файле, обычно имеет смысл разделить ваше приложение на файлы на основе функций (например, управление учётными записями, блоги, доски обсуждений) и проблемной области архитектуры (например, модель, представление или контроллер, если вы случайно используете MVC architecture).

В более поздней теме мы будем использовать Express Application Generator, который создаёт модульный каркас приложения, который мы можем легко расширить для создания веб-приложений.

Резюме
------

Поздравляем, вы завершили первый шаг в своём путешествии Express / Node! Теперь вы должны понимать основные преимущества Express и Node, а также примерно то, как могут выглядеть основные части приложения Express (маршруты, промежуточное ПО, обработка ошибок и код шаблона). Вы также должны понимать, что с Express, который является непонятным фреймворком, то, как вы собираете эти части вместе, и библиотеки, которые вы используете, в значительной степени зависит от вас!

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

Смотрите также
--------------

* Modules (Node API docs)
* Express (home page)
* Basic routing (Express docs)
* Routing guide (Express docs)
* Using template engines with Express (Express docs)
* Using middleware (Express docs)
* Writing middleware for use in Express apps (Express docs)
* Database integration (Express docs)
* Serving static files in Express (Express docs)
* Error handling (Express docs)
* Обзор: Express Nodejs
* Далее


Учебник Express часть 4: Маршруты и контроллеры - Изучение веб-разработки

Учебник Express часть 4: Маршруты и контроллеры
===============================================

* Назад
* Обзор: Express Nodejs
* Далее

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

| Предварительные знания: | Прочесть введение в Express/Node. Завершить предыдущие уроки (включая Express Tutorial Part 3: Using a Database (with Mongoose)). |
| --- | --- |
| Цель: | Понять, как создать простые маршруты. Настроить конечные точки URL. |

Обзор
-----

В последней статье мы определили модели *Mongoose* для взаимодействия с базой данных, и использовали (автономный) скрипт, который создал некоторые исходные записи библиотеки. Теперь можно написать код, чтобы представить эту информацию пользователям. Первое, что нужно сделать, это решить, какие возможности для отображения информации мы хотим иметь на наших страницах, а затем определить соответствующие URL-адреса для получения этих ресурсов. Затем нужно будет создать маршруты (обработчики URL-адресов) и представления (шаблоны) для отображения этих страниц.

Приведённая ниже диаграмма напоминает об основном потоке данных и об элементах, которые необходимо реализовать при обработке HTTP-запроса/ответа. Кроме представлений и маршрутов на диаграмме показаны "контроллеры" - функции, которые отделяют код для маршрутизации запросов от кода, который фактически обрабатывает запросы.

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

* "Маршруты" для перенаправления поддерживаемых запросов (и любой закодированной информации в URL-запросах) соответствующим функциям контроллера.
* Контроллеры -функции для получения запрашиваемых данных из моделей, создание HTML страницы, отображающей данные, и возращение их пользователю для просмотра в браузере.
* Представления (шаблоны), используемые контроллерами для отрисовки данных.

![](/ru/docs/Learn/Server-side/Express_Nodejs/routes/mvc_express.png)

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

В первом разделе ниже приведён краткие основы того, как использовать промежуточное средство (middleware) Express Router. Эти знания будут использованы в следующих разделах при настройке маршрутов для LocalLibrary.

Маршруты - основы
-----------------

Маршруты - это часть кода Express, связывающая HTTP действия (`GET`, `POST`, `PUT`, `DELETE`, etc.), URL пути (шаблона), и функцию, которая обрабатывает этот шаблон.

Есть несколько способов создания маршрутов. В этом уроке мы используем промежуточные запросы `express.Router,` так как они позволяют группировать обработчики маршрутов для определённой части сайта и получать к ним доступ через общий префикс маршрута. Все маршруты, связанные с библиотекой, будут сохранены в модуле "catalog", и если мы добавим маршруты для обработки учётных записей пользователей или других функций, мы сможем сгруппировать их отдельно.

**Примечание:** Маршруты приложения Express уже кратко рассматривались в Express Introduction > Creating route handlers (Введение -> Создание обработчиков маршрутов). Применение *Router* обеспечивает лучшую поддержку модульности (как обсуждается в первой подсекции ниже), а в остальном очень похоже на определение маршрутов непосредственно в объекте приложения *Express*.

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

### Определение и использование отдельных модулей маршрутов

Код ниже является реальным примером того, как можно создать модуль маршрута, а затем использовать его в приложении *Express*.

Первым делом в модуле **wiki.js** создадим маршруты для wiki . Код сначала импортирует объект приложения Express, использует его для получения объекта `Router` и затем, применяя метод `get(),` добавляет к объекту пару маршрутов. В завершение модуль экспортирует объект `Router` .

```js
// wiki.js - Wiki route module.

var express = require("express");
var router = express.Router();

// Home page route.
router.get("/", function (req, res) {
  res.send("Wiki home page");
});

// About page route.
router.get("/about", function (req, res) {
  res.send("About this wiki");
});

module.exports = router;
```

**Примечание:** В примере колбэк-функции обработчиков маршрутов определены непосредственно в функциях роутеров. А в LocalLibrary мы определим эти колбэк-функции в отдельном модуле контроллера.

Чтобы использовать модуль роутера в главном приложении, прежде всего следует выполнить `require()` модуля маршрута (**wiki.js**). Потом вызовем `use()` для приложения Express с аргументом, в котором указан URL-путь 'wiki', что добавит Router к пути обработки промежуточного слоя.

```js
var wiki = require("./wiki.js");
// ...
app.use("/wiki", wiki);
```

После этого два маршрута, определённые в нашем модуле маршрутов wiki, станут доступны из `/wiki/` и `/wiki/about/`.

### Функции Route

В модуле выше определена пара типовых функций маршрута. Маршрут "about" (ещё раз показан ниже) определён при помощи метода `Router.get()`, который отвечает только на HTTP GET-запросы. Первый аргумент метода - URL-путь, а второй - колбэк-функция, которая будет вызвана, если получен HTTP GET-запрос с указанным путём.

```js
router.get("/about", function (req, res) {
  res.send("About this wiki");
});
```

 Эта колбэк-функция имеет три аргумента takes three arguments (обычно именуемых как указано: `req`, `res`, `next`), которые соответствуют объекту HTTP запроса, ответу HTTP, и *следующей*
 функции в цепочке промежуточных элементов.

**Примечание:** Функции в Router - это промежуточный слой (middleware) are Express, что означает, что они должны или завершить (ответить на) запрос reqили вызвать следующую (`next)` функцию в цепочке. В нашем случае запрос завершается вызовом `send()`, поэтому аргумент `next` не нужен (и поэтому не указан).

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

Здесь, когда приходит GET-запрос с путём ('`/about'`) колбэк-функция при ответе вызывает `send()` , возвращая строку "About this wiki". Существует ряд других методов ответа , завершающих цикл запрос-ответ. Например, можно вызвать `res.json()` , чтобы послать ответ JSON, или `res.sendFile(),` чтобы послать файл. Метод ответа, который будет использован чаще всего при построении нашей библиотеки - это render(), создающий, на основе шаблонов и данных, и возвращающий HTML-файлы —мы поговорим об этом подробнее в следующей статье!

### HTTP глаголы (действия)

Рассмотренный пример использует метод `Router.get()` для ответа на HTTP GET- запросы с указанным путём.

Кроме того, `Router` обеспечивает также методы маршрутизации для других HTTP глаголов, которые обычно используются точно таким же способом: `post()`, `put()`, `delete()`, `options()`, `trace()`, `copy()`, `lock()`, `mkcol()`, `move()`, `purge()`, `propfind()`, `proppatch()`, `unlock()`, `report()`, `mkactivity()`, `checkout()`, `merge()`, `m-search()`, `notify()`, `subscribe()`, `unsubscribe()`, `patch()`, `search()`, и `connect()`.

Например, код ниже делает то же, что и предыдущий, с путём `/about,` но отвечает на HTTP POST-запросы.

```js
router.post("/about", function (req, res) {
  res.send("About this wiki");
});
```

### Маршруты путей

Маршруты путей определяют конечные точки, в которых могут быть сделаны запросы. В уже рассмотренных примерах это были просто строки, которые использовались точно так, как были записаны: '/', '/about', '/book', '/any-random.path'.

Маршруты путей могут быть также образцами строк. Образцы строк используют подмножество синтаксиса регулярных выражений для определения *образцов* конечных точек, для которых должно проверяться соответствие. Подмножество приведено ниже (отметим, что перенос (`-`) и точка (`.`) в путях на основе строк понимаются буквально):

* ? : Конечная точка должна иметь 0 или 1 предыдущий символ. Так, путь маршрута `'/ab?cd'` соответствует конечным точкам `acd` или `abcd` (т.е. b может присутствовать или нет).
* + : Конечная точка должна иметь 1 или более предыдущих символов. Так, путь маршрута `'/ab+cd'` будет соответствовать конечным точкам `abcd`, `abbcd`, `abbbcd`, и так далее.
* \* : Конечная точка может содержать произвольную строку там, где находится символ '\*'. Так, путь маршрута `'ab\*cd'` будет соответствовать конечным точкам `abcd`, `abXcd`, `abSOMErandomTEXTcd`, и так далее.
* () : Группировка символов для выполнения другой операции. Так, `'/ab(cd)?e'` выполнит ?-проверку (0 или 1 появление) для группы (cd) — соответствия таковы: `abe` и `abcde`.

Пути маршрутов могут быть также регулярными выражениями JavaScript. Например, путь маршрутов (ниже) будет соответствовать `catfish и` `dogfish`, но не `catflap`, `catfishhead`, и так далее. Отметим, регулярное выражение как путь использует синтаксис регулярных выражений (это не строка в кавычках, как в предыдущих случаях).

```js
app.get(/.\*fish$/, function (req, res) {
  ...
})
```

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

### Параметры маршрутов

Параметры маршрутов - это *именованные сегменты URL* , которые используются для выбора значений из указанной позиции URL. Именованные сегменты начинаются двоеточием, после которого следует имя (например, `/:your_parameter_name/`. Выбранные значения сохраняются в объекте `req.params,` причём имя параметра используется как ключ (т.е. `req.params.your_parameter_name`).

Предположим, например, что URL содержит информацию о пользователях и книгах: `http://localhost:3000/users/34/books/8989`. Можно извлечь эту информацию (см. ниже) в параметры `userId` и `bookId` пути:
```

app.get('/users/:userId/books/:bookId', function (req, res) {
  // доступ к userId через: req.params.userId
  // доступ к bookId через: req.params.bookId
  res.send(req.params);
})
```

Имена параметров пути должны состоять из "символов слова" (A-Z, a-z, 0-9, и \_).

**Примечание:** URL */book/create* будет соответствовать маршрутам вида `/book/:bookId` (и '`create`' станет значением "bookId"). Будет использован первый маршрут, соответствующий введённому URL, поэтому, если необходимо обрабатывать URL вида `/book/create` отдельно, обработчик этого маршрута должен быть расположен до маршрута `/book/:bookId` .

Для начала этих сведений достаточно - если потребуется, можно найти дополнительную информацию в документации Express: Basic routing (основы маршрутизации) и Routing guide (руководство по маршрутизации). В следующем разделе показано, как задать маршруты и контроллеры для нашей библиотеки LocalLibrary.

Маршруты, необходимые для библиотеки LocalLibrary
-------------------------------------------------

Те URL, которые в итоге будут нужны для наших страниц, показаны ниже. Слово *object* должно быть заменено на имя каждой из наших моделей (book, bookinstance, genre, author), слово *objects* - множественное число для *object,* а *id* - уникальное значение для поля(`_id`), которое Mongoose создаёт по умолчанию для каждого экземпляра модели.

* `catalog/` — Домашняя страница home/index.
* `catalog/<objects>/` — Список всех книг, экземпляров книг, жанров и авторов (т.е. /`catalog/books/`, /`catalog/genres/`, и т.д.)
* `catalog/<object>/<id>` — Страница с подробностями для отдельной книги, экземпляра книги, жанра или автора с заданным полем идентификатора `_id` (т.е. `/catalog/book/584493c1f4887f06c0e67d37)`.
* `catalog/<object>/create` — Форма для создания новой книги, экземпляра книги, жанра или автора (т.е. `/catalog/book/create)`.
* `catalog/<object>/<id>/update` — Форма для обновления отдельной книги, экземпляра книги, жанра или автора с заданным идентификатором `_id` (т.е. `/catalog/book/584493c1f4887f06c0e67d37/update)`.
* `catalog/<object>/<id>/delete` — Форма для удаления отдельной книги, экземпляра книги, жанра или автора по заданному идентификатору `_id` (т.е. `/catalog/book/584493c1f4887f06c0e67d37/delete)`.

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

В противоположность этому, другие URL используются для работы с определёнными экземплярами документов и моделей — индивидуальность элементов кодируется в URL (как `<id>` выше). Параметры путей используются для извлечения информации и передачи её в обработчик пути (в следующей статье мы применим этот приём для того, чтобы динамически определять, какую информацию следует получить из БД). Кодирование информации в URL-адресе позволяет обойтись одним маршрутом для каждого ресурса определенного типа (например, можно обойтись одним маршрутом для обработки отображения любой отдельной записи о книге).

**Примечание:** Express позволяет строить URL любым способом, который вам нравится — можно кодировать информацию в теле URL как показано выше или использовать URL `GET` -запрос с параметрами (например, `/book/?id=6`). Какой бы подход вы не применяли, URL должны быть ясными, логичными и читаемыми (ознакомьтесь с советами W3C).

Далее мы создадим колбэк-функции обработчиков маршрутов и код маршрутов для всех указанных выше URL.

Создаём колбэк-функции обработчиков маршрутов
---------------------------------------------

Перед определением маршрутов сначала создадим фиктивные (каркасные) колбэк-функции, которые они будут вызывать. Эти функции будут храниться в отдельных модулях -"контроллерах" для моделей Book, BookInstance, Genre, и Author (можно использовать любую структуру моделей и файлов, но кажется, что выбранная обеспечивает приемлемую модульность нашего проекта).

Начнём с создания каталога для контроллеров в корне проекта (**/controllers**), а затем создадим отдельные файлы (модули) контроллеров для работы с моделями:
```

/express-locallibrary-tutorial  //корень проекта
  /controllers
    authorController.js
    bookController.js
    bookinstanceController.js
    genreController.js
```

### Контроллер автора

Скопируем следующий код в файл **/controllers/authorController.js**:

```js
var Author = require("../models/author");

// Показать список всех авторов.
exports.author\_list = function (req, res) {
  res.send("NOT IMPLEMENTED: Author list");
};

// Показать подробную страницу для данного автора.
exports.author\_detail = function (req, res) {
  res.send("NOT IMPLEMENTED: Author detail: " + req.params.id);
};

// Показать форму создания автора по запросу GET.
exports.author\_create\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Author create GET");
};

// Создать автора по запросу POST.
exports.author\_create\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Author create POST");
};

// Показать форму удаления автора по запросу GET.
exports.author\_delete\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Author delete GET");
};

// Удалить автора по запросу POST.
exports.author\_delete\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Author delete POST");
};

// Показать форму обновления автора по запросу GET.
exports.author\_update\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Author update GET");
};

// Обновить автора по запросу POST.
exports.author\_update\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Author update POST");
};
```

В модуле сначала подключается (requires) модель, которая далее будет использована для получения данных и их обновления. Далее экспортируются функции для каждого URL, который мы хотим обрабатывать (операции create-создать, update-обновить и delete-удалить используют формы, следовательно, должны быть дополнительные методы для обработки post-запросов от форм - эти методы обсуждаются далее, в статье "forms article" ("формы")).

Все функции имеют стандартную форму функций среднего слоя *Express* , с аргументами для запроса, ответа и следующей `(next)` функции, которая должна быть вызвана, если метод не завершил цикл запроса (во всех приведённых в коде случаях - завершает!). Методы просто возвращают строку, информирующую о том, что соответствующая страница ещё не создана. Если функция контроллера должна получить параметры маршрута, эти параметры будут выведены в строке сообщения (смотри выше `req.params.id` ).

#### BookInstance controller

Скопируйте следующий код в файл **/controllers/bookinstanceController.js** (он построен по образцу модуля контроллера для автора `Author` ):

```js
var BookInstance = require("../models/bookinstance");

// Display list of all BookInstances.
exports.bookinstance\_list = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance list");
};

// Display detail page for a specific BookInstance.
exports.bookinstance\_detail = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance detail: " + req.params.id);
};

// Display BookInstance create form on GET.
exports.bookinstance\_create\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance create GET");
};

// Handle BookInstance create on POST.
exports.bookinstance\_create\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance create POST");
};

// Display BookInstance delete form on GET.
exports.bookinstance\_delete\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance delete GET");
};

// Handle BookInstance delete on POST.
exports.bookinstance\_delete\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance delete POST");
};

// Display BookInstance update form on GET.
exports.bookinstance\_update\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance update GET");
};

// Handle bookinstance update on POST.
exports.bookinstance\_update\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: BookInstance update POST");
};
```

#### Контроллер жанра

Скопируйте следующий код в файл **/controllers/genreController.js** (он построен по образцу модулей контроллеров для автора `Author` и экземпляра книги `BookInstance`):

```js
var Genre = require("../models/genre");

// Display list of all Genre.
exports.genre\_list = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre list");
};

// Display detail page for a specific Genre.
exports.genre\_detail = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre detail: " + req.params.id);
};

// Display Genre create form on GET.
exports.genre\_create\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre create GET");
};

// Handle Genre create on POST.
exports.genre\_create\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre create POST");
};

// Display Genre delete form on GET.
exports.genre\_delete\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre delete GET");
};

// Handle Genre delete on POST.
exports.genre\_delete\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre delete POST");
};

// Display Genre update form on GET.
exports.genre\_update\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre update GET");
};

// Handle Genre update on POST.
exports.genre\_update\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Genre update POST");
};
```

#### Контроллер книги

Скопируйте следующий код в файл **/controllers/bookController.js**. Он построен по образцу других модулей контроллеров, но ещё содержит функцию `index()` для вывода странички с приветствием:

```js
var Book = require("../models/book");

exports.index = function (req, res) {
  res.send("NOT IMPLEMENTED: Site Home Page");
};

// Display list of all books.
exports.book\_list = function (req, res) {
  res.send("NOT IMPLEMENTED: Book list");
};

// Display detail page for a specific book.
exports.book\_detail = function (req, res) {
  res.send("NOT IMPLEMENTED: Book detail: " + req.params.id);
};

// Display book create form on GET.
exports.book\_create\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Book create GET");
};

// Handle book create on POST.
exports.book\_create\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Book create POST");
};

// Display book delete form on GET.
exports.book\_delete\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Book delete GET");
};

// Handle book delete on POST.
exports.book\_delete\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Book delete POST");
};

// Display book update form on GET.
exports.book\_update\_get = function (req, res) {
  res.send("NOT IMPLEMENTED: Book update GET");
};

// Handle book update on POST.
exports.book\_update\_post = function (req, res) {
  res.send("NOT IMPLEMENTED: Book update POST");
};
```

Создание модуля для маршрута catalog
------------------------------------

Далее мы создадим маршруты для всех URL, необходимых веб-сайту LocalLibrary, которые будут вызывать функции контроллеров, определённые в предыдущем разделе.

Каркас приложения уже содержит каталог **./routes**, в котором есть маршруты для *index* и *users*. Внутри этого каталога создадим ещё один файл маршрутов — **catalog.js** ( см. ниже).
```

/express-locallibrary-tutorial //the project root
  /routes
    index.js
    users.js
    catalog.js
```

Скопируйте приведённый ниже код в файл \*\*/routes/\*\*catalog.js :

```js
var express = require("express");
var router = express.Router();

// Требующиеся модули контроллеров.
var book_controller = require("../controllers/bookController");
var author_controller = require("../controllers/authorController");
var genre_controller = require("../controllers/genreController");
var book_instance_controller = require("../controllers/bookinstanceController");

/// BOOK ROUTES МАРШРУТЫ КНИГ///

// GET catalog home page.
router.get("/", book_controller.index);

// GET request for creating a Book. NOTE This must come before routes that display Book (uses id).
// GET запрос для создания книги. Должен появиться до маршрута, показывающего книгу(использует id)
router.get("/book/create", book_controller.book_create_get);

// POST request for creating Book.
router.post("/book/create", book_controller.book_create_post);

// GET request to delete Book.
router.get("/book/:id/delete", book_controller.book_delete_get);

// POST request to delete Book.
router.post("/book/:id/delete", book_controller.book_delete_post);

// GET request to update Book.
router.get("/book/:id/update", book_controller.book_update_get);

// POST request to update Book.
router.post("/book/:id/update", book_controller.book_update_post);

// GET request for one Book.
router.get("/book/:id", book_controller.book_detail);

// GET request for list of all Book items.
router.get("/books", book_controller.book_list);

/// AUTHOR ROUTES ///

// GET request for creating Author. NOTE This must come before route for id (i.e. display author).
// GET-запрос для создания автора. Должен появиться до маршрута для id (для вывода автора)
router.get("/author/create", author_controller.author_create_get);

// POST request for creating Author.
router.post("/author/create", author_controller.author_create_post);

// GET request to delete Author.
router.get("/author/:id/delete", author_controller.author_delete_get);

// POST request to delete Author.
router.post("/author/:id/delete", author_controller.author_delete_post);

// GET request to update Author.
router.get("/author/:id/update", author_controller.author_update_get);

// POST request to update Author.
router.post("/author/:id/update", author_controller.author_update_post);

// GET request for one Author.
router.get("/author/:id", author_controller.author_detail);

// GET request for list of all Authors.
router.get("/authors", author_controller.author_list);

/// GENRE ROUTES ///

// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
// GET-запрос для создания жанра. Должен появиться до маршрута, выводящего жанр (( с использованием id)
router.get("/genre/create", genre_controller.genre_create_get);

//POST request for creating Genre.
router.post("/genre/create", genre_controller.genre_create_post);

// GET request to delete Genre.
router.get("/genre/:id/delete", genre_controller.genre_delete_get);

// POST request to delete Genre.
router.post("/genre/:id/delete", genre_controller.genre_delete_post);

// GET request to update Genre.
router.get("/genre/:id/update", genre_controller.genre_update_get);

// POST request to update Genre.
router.post("/genre/:id/update", genre_controller.genre_update_post);

// GET request for one Genre.
router.get("/genre/:id", genre_controller.genre_detail);

// GET request for list of all Genre.
router.get("/genres", genre_controller.genre_list);

/// BOOKINSTANCE ROUTES ///

// GET request for creating a BookInstance. NOTE This must come before route that displays BookInstance (uses id).
// GET-запрос для создания экземпляра книги. Должен появиться до маршрута, выводящего BookInstance с использованием id
router.get(
  "/bookinstance/create",
  book_instance_controller.bookinstance_create_get,
);

// POST request for creating BookInstance.
router.post(
  "/bookinstance/create",
  book_instance_controller.bookinstance_create_post,
);

// GET request to delete BookInstance.
router.get(
  "/bookinstance/:id/delete",
  book_instance_controller.bookinstance_delete_get,
);

// POST request to delete BookInstance.
router.post(
  "/bookinstance/:id/delete",
  book_instance_controller.bookinstance_delete_post,
);

// GET request to update BookInstance.
router.get(
  "/bookinstance/:id/update",
  book_instance_controller.bookinstance_update_get,
);

// POST request to update BookInstance.
router.post(
  "/bookinstance/:id/update",
  book_instance_controller.bookinstance_update_post,
);

// GET request for one BookInstance.
router.get("/bookinstance/:id", book_instance_controller.bookinstance_detail);

// GET request for list of all BookInstance.
router.get("/bookinstances", book_instance_controller.bookinstance_list);

module.exports = router;
```

Модуль загружает Express и использует его для создания объекта `Router` . В маршрутизаторе задаются маршруты и производится их экспорт.

Маршруты определяют в объекте маршрутизатора или `.get()` или `.post()` методы. Все пути заданы как строки (образцы строк и регулярные выражения не использовались). Маршруты, которые взаимодействуют с конкретным ресурсом (скажем, с книгой), для получения из URL идентификатора объекта используют параметры путей.

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

### Обновление модуля маршрута index

Все новые маршруты заданы, а маршрут на начальную страницу остался без изменения. Давайте перенаправим его на новую страницу "index", которая создана в каталоге '/catalog'.

Откройте **/routes/index.js** и замените существующий маршрут приведённую ниже.

```js
// GET home page.
router.get("/", function (req, res) {
  res.redirect("/catalog");
});
```

**Примечание:** Это первое использование метода ответа redirect() . Он делает перенаправление на указанную страницу, и по умолчанию устанавливает код возврата HTTP в "302 Found" (найдено). Если требуется, можно изменить код возврата. Путь можно задавать как абсолютный или как относительный.

### Обновление app.js

Завершающий шаг - добавление маршрутов в цепочку промежуточного слоя. Это будет сделано в `app.js`.

Откройте файл **app.js** и поместите require для маршрута каталог ниже других маршрутов (добавьте третью строку. показанную ниже, после имеющихся двух строк):

```js
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var catalogRouter = require("./routes/catalog"); //Import routes for "catalog" area of site
```

Далее, добавьте маршрут каталога в стек промежуточного слоя после других маршрутов (добавить третью строку после имеющихся двух):

```js
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/catalog", catalogRouter); // Add catalog routes to middleware chain.
```

**Примечание:** Мы добавили модуль каталога в путь`'/catalog'`. Этот путь будет предшествовать всем путям, определённым в модуле каталога. Например, для доступа к списку книг URL будет таким: `/catalog/books/`.

Вот так. Теперь у нас есть пути и фиктивные функции, подготовленные для всех URL, которые мы собираемся поддерживать на веб-сайте LocalLibrary.

### Проверка маршрутов

Чтобы проверить маршруты, сначала запустим веб-сайт обычным способом

* Обычный способ

```bash
// Windows
SET DEBUG=express-locallibrary-tutorial:* & npm start

// macOS or Linux
DEBUG=express-locallibrary-tutorial:* npm start
```

* Если предварительно установлен nodemon, для запуска можно использовать:
```

// Windows
SET DEBUG=express-locallibrary-tutorial:* & npm run devstart

// macOS or Linux
 DEBUG=express-locallibrary-tutorial:* npm run devstart
```

После запуска перейдите к совокупности URL нашей LocalLibrary, и проверьте, что не появляется страница ошибки (HTTP 404). Небольшая часть наших URL для удобства приводится ниже:

* `http://localhost:3000/`
* `http://localhost:3000/catalog`
* `http://localhost:3000/catalog/books`
* `http://localhost:3000/catalog/bookinstances/`
* `http://localhost:3000/catalog/authors/`
* `http://localhost:3000/catalog/genres/`
* `http://localhost:3000/catalog/book/5846437593935e2f8c2aa226`
* `http://localhost:3000/catalog/book/create`
Итог
----

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

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

Смотрите также
--------------

* Basic routing Основы маршрутизации (документация Express)
* Routing guide Руководство по маршрутизации (документация Express)
* Назад
* Обзор: Express Nodejs
* Далее
In this module
--------------

* Express/Node introduction
* Setting up a Node (Express) development environment
* Express Tutorial: The Local Library website
* Express Tutorial Part 2: Creating a skeleton website
* Express Tutorial Part 3: Using a Database (with Mongoose)
* Express Tutorial Part 4: Routes and controllers
* Express Tutorial Part 5: Displaying library data
* Express Tutorial Part 6: Working with forms
* Express Tutorial Part 7: Deploying to production (en-US)


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

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

* Назад
* Обзор: Express Nodejs
* Далее

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

| Необходимые знания: | Установить среду разработки Node. Просмотреть учебник Express. |
| --- | --- |
| Задача: | Научиться запускать свои проекты используя *Express Application Generator*. |

Обзор
-----

В этой статье показано, как создать каркас сайта с помощью средства Express Application Generator. Каркас затем можно будет заполнить с помощью путей сайта, шаблонов/представлений и обращений к базе данных. Мы используем это средство для создания основы нашего сайта Local Library. К основе будет добавлен код, необходимый сайту. Создание каркаса чрезвычайно просто — требуется только вызвать генератор в командной строке, указав имя нового проекта, дополнительно можно указать также движок шаблона сайта и генератор CSS.

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

**Примечание:** *Express Application Generator* — не единственный генератор Express-приложений, и созданный проект --не единственный жизнеспособный способ организации ваших файлов и каталогов. Однако созданный сайт имеет модульную структуру, которую легко понять и расширить. О *минимальном* Express приложении смотрите Hello world example в документации Express.

Применение генератора приложений
--------------------------------

Вы уже должны были установить `express-generator`, читая статью установка среды разработки Node. Напомним, что генератор установлен с помощью менеджера пакетов NPM, при выполнении команды:

```bash
npm install express-generator -g
```

`express-generator` имеет ряд параметров, которые можно увидеть, выполнив команду express --help (или express -h):

```bash
> express --help

  Usage: express [options] [dir]

  Options:

    -h, --help           output usage information (информация по применению)
        --version        output the version number (номер версии express)
    -e, --ejs            add ejs engine support (добавить поддержку движка ejs)
        --pug            add pug engine support (добавить поддержку движка pug)
        --hbs            add handlebars engine support (добавить поддержку движка handlebar)
    -H, --hogan          add hogan.js engine support (добавить поддержку движка hogan.js)
    -v, --view <engine>  add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
                         (добавить поддержку движков представлений. По умолчанию - jade)
    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
                         (добавить поддержку движков стилей, по умолчанию - простой CSS)
        --git            add .gitignore (добавить поддержку .gitignore)
    -f, --force          force on non-empty directory (работать в каталоге с файлами)
```

Команда `express` создаст проект в *текущем* каталоге с использованием (устаревшего) движка представления *Jade* и обычного CSS. Если указать express name, проект будет создан в подкаталоге name текущего каталога.

```bash
express
```

Можно выбрать движок представления (шаблон), используя `--view` параметр `--css` позволяет выбрать движок для создания CSS.

**Примечание:** Другие опции (`--hogan`, `--ejs`, `--hbs` и пр.) для выбора шаблонизатора устарели. Используйте `--view` (или `-v`)!

### Какой движок представлений следует использовать?

`Express-generator` даёт возможность сконфигурировать несколько популярных движков, включая EJS, Hbs, Pug (Jade), Twig, и Vash, но по умолчанию выбран Jade. Экспресс сразу после установки может поддерживать большое количество и других шаблонизаторов.

**Примечание:** При желании использовать шаблонизатор, который не поддерживается генератором, просмотрите документацию Using template engines with Express и документацию для нужного шаблонизатора.

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

* Время до получения результата — если ваша команда уже имела дело с шаблонизатором, то, скорее всего, продуктивнее будет использовать этот шаблонизатор. Если нет, тогда следует учесть все относительные сложности изучения кандидатов в шаблонизаторы.
* Популярность и активность — проверьте популярность движка, возможно, у него есть активное сообщество. Очень важно иметь поддержку для движка, если у вас возникнут проблемы в течении жизни веб-сайта.
* Стиль — некоторые шаблонизаторы используют особую разметку для отображения вставленного контента внутри "обычного" HTML, а другие строят HTML, используя специальный синтаксис (например, используя отступы или блочные имена).
* Производительность и время интерпретации.
* Особенности — следует выбирать движок с учётом таких особенностей:
    + Наследование макета: позволяет определить базовый шаблон и затем наследовать только те части, которые отличаются для конкретной страницы. Это, как правило, лучший подход, чем создание шаблонов путём включения нескольких необходимых компонентов или создания шаблона с нуля каждый раз.
    + Поддержка «Include»: позволяет создавать шаблоны, включая другие шаблоны.
    + Краткий синтаксис управления переменными и циклами.
    + Возможность фильтровать значения переменных на уровне шаблона (например, делать переменные в верхнем регистре или форматировать значение даты).
    + Возможность создавать выходные форматы, отличные от HTML (например, JSON или XML).
    + Поддержка асинхронных операций и потоковой передачи.
    + Возможность использования как на клиенте, так и на сервере. Возможность применения движка шаблона на клиенте позволяет обслуживать данные и выполнять все действия или их большую часть на стороне клиента.

**Примечание:** В интернете множество ресурсов, которые помогут сравнить различные варианты!

Для этого проекта мы используем шаблонизатор Pug (в прошлом назывался Jade) — один из популярнейших Express/JavaScript шаблонизаторов, который поддерживается в Express-generator "из коробки".

### Какие шаблонизаторы CSS следует использовать?

*Express Application Generator* позволяет создавать проекты, настроенные для применения шаблонизаторов CSS: LESS, SASS, Compass, Stylus.

**Примечание:** простой CSS имеет некоторые ограничения, затрудняющие выполнение задач. Шаблонизаторы CSS позволяют использовать более эффективный подход для создании таблиц стилей CSS, но требуют компиляции файлов таблиц стилей в стандартный CSS для применения в браузере.

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

### Какую базу данных следует использовать?

Сгенерированный код не использует и не содержит в себе какой-либо базы данных. *Express* может использовать любой движок базы данных, который поддерживается *Node* (*Express* не предъявляет каких-либо особых требований к базе данных).

Мы обсудим взаимодействие с базой данных в следующей статье.

Создание проекта
----------------

Разрабатывая пример - приложение *Local Library,* мы построим проект с именем *express-locallibrary-tutorial.* Используем библиотеку шаблонов Pug, а движок CSS применять не будем.

Выберем место для нового проекта — каталог express-locallibrary-tutorial - и выполним команду:

```bash
express express-locallibrary-tutorial --view=pug
```

Будет создан каталог express-locallibrary-tutorial и выведен список созданных внутри каталога проектных файлов.

```bash
   create : express-locallibrary-tutorial
   create : express-locallibrary-tutorial/package.json
   create : express-locallibrary-tutorial/app.js
   create : express-locallibrary-tutorial/public/images
   create : express-locallibrary-tutorial/public
   create : express-locallibrary-tutorial/public/stylesheets
   create : express-locallibrary-tutorial/public/stylesheets/style.css
   create : express-locallibrary-tutorial/public/javascripts
   create : express-locallibrary-tutorial/routes
   create : express-locallibrary-tutorial/routes/index.js
   create : express-locallibrary-tutorial/routes/users.js
   create : express-locallibrary-tutorial/views
   create : express-locallibrary-tutorial/views/index.pug
   create : express-locallibrary-tutorial/views/layout.pug
   create : express-locallibrary-tutorial/views/error.pug
   create : express-locallibrary-tutorial/bin
   create : express-locallibrary-tutorial/bin/www

   install dependencies:
     > cd express-locallibrary-tutorial && npm install

   run the app:
     > SET DEBUG=express-locallibrary-tutorial:* & npm start
```

После списка файлов генератор выведет инструкции для установки зависимостей (указанных в файле **package.json**) и запуска приложения (инструкции предназначены для Windows; для Linux/Mac OS X они могут слегка отличаться).

Запускаем каркас сайта
----------------------

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

1. Прежде всего установим зависимости (команда `install` запросит все пакеты зависимостей, указанные в файле **package.json**).

```bash
cd express-locallibrary-tutorial
npm install
```

2. Затем запустим приложение.
    * В Windows используйте команду:
     bash
    ```bash
    SET DEBUG=express-locallibrary-tutorial:* & npm start

    ```
    * В Mac OS X или Linux используйте команду:
     bash
    ```bash
    DEBUG=express-locallibrary-tutorial:* npm start

    ```
3. Откроем http://localhost:3000/ в браузере. Мы должны увидеть такую страницу:

![Browser for default Express app generator website](/ru/docs/Learn/Server-side/Express_Nodejs/skeleton_website/expressgeneratorskeletonwebsite.png)

У нас получилось веб-приложение на базе Express, работающее по адресу *localhost:3000*.

**Примечание:** Можно также запустить приложение командой `npm start`. Переменная DEBUG, указанная в примере, включает логирование в консоль для дальнейшей отладки. Так, при посещении страницы веб-приложения, вы увидите похожий вывод в консоль:

```bash
>SET DEBUG=express-locallibrary-tutorial:* & npm start

> express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial
> node ./bin/www

  express-locallibrary-tutorial:server Listening on port 3000 +0ms
GET / 200 288.474 ms - 170
GET /stylesheets/style.css 200 5.799 ms - 111
GET /favicon.ico 404 34.134 ms - 1335
```

Обеспечиваемперезапуск сервера при изменении файлов
---------------------------------------------------

Любые изменения, внесённые на веб-сайт Express, не будут отображаться до перезапуска сервера. Остановка (Ctrl-C) и перезапуск сервера каждый раз после внесения изменений быстро становится раздражающей, поэтому стоит автоматизировать перезапуск.

 Одно из самых простых средств для этого --
 nodemon. Его обычно устанавливают глобально (так как это "инструмент"), но сейчас мы установим его и будем применять локально как зависимость разработки, так что любые разработчики проекта получат его автоматически при установке приложения. Выполним следующую команду (предполагаем, что мы находимся в корневом каталоге):

```bash
npm install --save-dev nodemon
```

Если вы предпочитаете установить nodemon глобально, не только для этого проекта, надо выполнить команду
```

npm install -g nodemon
```

В файле **package.json** проекта появится новый раздел с этой зависимостью (на вашей машине номер версии nodemon может быть другим) :

```json
  "devDependencies": {
    "nodemon": "^1.11.0"
  }
```

Поскольку nodemon не установлен глобально, его нельзя запустить из командной строки (пока мы не добавим его в путь), но его можно вызвать из сценария NPM, так как NPM знает все об установленных пакетах. Раздел `scripts` в файле package.json исходно будет содержать одну строку, которая начинается с `"start"`. Обновите его, поставив запятую в конце строки, и добавьте строку `"devstart",` показанную ниже:

```json
  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },
```

Теперь можно запустить сервер почти так же, как и ранее, но командой npm run devstart:

* В Windows:

```bash
SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
```

* Для macOS или Linux:
```

DEBUG=express-locallibrary-tutorial:* npm run devstart
```

**Примечание:** Сейчас после изменения любого файла проекта сервер будет перезапускаться (или можно самостоятельно перезапустить его, введя `rs` в командной строке). Вам всё равно придётся обновить страницу в браузере .

Теперь мы должны выполнять команду "`npm run` *<scriptname>*" а не просто `npm start`, поскольку "start", это, по сути, команда NPM, сопоставленная сценарию в файле package.json. Можно заменить команду в сценарии "start", но, так как мы хотим использовать nodemon только во время разработки, разумно создать новую команду сценария.

Созданный проект
----------------

Давайте посмотрим на созданный проект.

### Структура каталогов

После установки зависимостей проект имеет такую структуру файлов (файлы - это элементы **без** префикса"/"). Файл **package.json** определяет имя файла с приложением, сценарии запуска, зависимости и др. Сценарий запуска задаёт точку входа приложения, у нас — файл JavaScript **/bin/www**. Этот файл настраивает некоторые обработчики ошибок приложения, а затем загружает **app.js** для выполнения остальной работы. Пути приложения хранятся в отдельных модулях каталога **routes/**. Шаблоны хранятся в каталоге /**views**.
```

/express-locallibrary-tutorial
    app.js
    /bin
        www
    package.json
    /node_modules
        [about 4,500 subdirectories and files]
    /public
        /images
        /javascripts
        /stylesheets
            style.css
    /routes
        index.js
        users.js
    /views
        error.pug
        index.pug
        layout.pug
```

Далее файлы описаны более подробно.

### package.json

Файл **package.json** указывает зависимости приложения и содержит другие данные:

```json
{
  "name": "express-locallibrary-tutorial",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.15.2",
    "cookie-parser": "~1.4.3",
    "debug": "~2.2.0",
    "express": "~4.14.0",
    "morgan": "~1.7.0",
    "pug": "~2.0.0-beta6",
    "serve-favicon": "~2.3.0"
  },
  "devDependencies": {
    "nodemon": "^1.11.0"
  }
}
```

Зависимости включают пакет express и пакет для выбранного движка представления (pug). Кроме того, указаны пакеты, полезные во многих веб-приложениях:

* body-parser: — анализирует часть тела входящего запроса HTTP и облегчает извлечение из него различных частей. Например, мы можно читать `POST-`параметры.
* cookie-parser: разбирает заголовок и заполняет `req.cookies` (по сути, даёт удобный метод для доступа к информации cookie).
* debug: небольшой отладчик, работающий по образцу методики отладки ядра node.
* morgan: средство логирования запросов HTTP для node.
* serve-favicon: средство обработки favicon (значка, используемого для представления сайта на вкладках браузера, закладках и т. д).

Раздел "scripts" определяет скрипт" start", выполняемый при запуске сервера командой `npm start`. Можно видеть, что самом деле выполняется команда node **./bin/www**. Кроме того, определяется script "*devstart*", который вызывается командой `npm run devstart`. Запускается тот же файл **./bin/www** ,но командой *nodemon* вместо *node*.

```json
"scripts": {
    "start": "node ./bin/www",
    "devstart": "nodemon ./bin/www"
  },
```

### Файл www

Файл **/bin/www** – это входная точка приложения. Сначала в файле создаётся объект основного приложения, расположенного в app.js — выполняется `app= require(./app)`.

```js
#!/usr/bin/env node

/\*\*
 \* Module dependencies.
 \*/

var app = require("../app");
```

**Примечание:** `require()` — это глобальная функция node для импорта модулей в текущий файл. Для модуля **app.js** указан относительный путь, а расширение файла по умолчанию (.js) опущено.

Оставшаяся часть кода настраивает порт сервера node для HTTP (определён в переменной среды или 3000, если не определён), и начинает обработку и протоколирование соединений и ошибок сервера. Сейчас вам не требуется дополнительных сведений о коде (все в этом файле шаблонно), но, при желании, его можно посмотреть.

### Файл app.js

Этот файл создаёт объект приложения `express` (с именем `app`, по соглашению), настраивает приложение и промежуточное ПО, а затем экспортирует приложение из модуля. В приведённом ниже коде показаны только те части файла, которые создают и экспортируют объект приложения:

```js
var express = require('express');
var app = express();
...
module.exports = app;
```

Именно этот экспортированный объект использован в рассмотренном ранее файле www.

Рассмотрим детали файла app.js. Сначала при помощи require(...) выполняется импорт некоторых полезных библиотек node: *express,* s*erve-favicon*, *morgan*, *cookie-parse, body-parser* (они ранее были загружены для нашего приложения командой npm install), а также path из основной библиотеки node (применяется для разбора путей каталогов и файлов).

```js
var express = require("express");
var path = require("path");
var favicon = require("serve-favicon");
var logger = require("morgan");
var cookieParser = require("cookie-parser");
var bodyParser = require("body-parser");
```

Затем require запрашивает модули из каталога путей route. Эти модули и файлы содержат код для обработки конкретного набора соответствующих путей (URL маршрутов). Если мы расширим каркас приложения, например, чтобы получить список книг библиотеки, нам следует добавить новый файл для обработки пути, связанного с книгами.

```js
var index = require("./routes/index");
var users = require("./routes/users");
```

**Примечание:** Здесь мы только импортируем модули. В действительности эти пути ещё не используются — это произойдёт в файле несколько позже.

Далее, импортированные модули express применяются для создания объекта app, который потом устанавливает движки-шаблоны представления. Установка движков состоит их двух частей. В первой мы задаём значение 'view', указывая папку, в которой будут размещаться шаблоны (у нас это /views). Во второй мы задаём значение движка 'view engine', указывая на библиотеку шаблона (у нас — "pug").

```js
var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");
```

Следующие строки вызывают app.use(...), чтобы добавить промежуточные (middleware) библиотеки в цепочку обработки запросов. Кроме сторонних библиотек, импортированных ранее, используем библиотеку Express.static, что позволит обрабатывать статические файлы из папки **/public** корня проекта.

```js
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(\_\_dirname, 'public', 'favicon.ico')));
app.use(logger("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
```

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

```js
app.use("/", index);
app.use("/users", users);
```

**Примечание:** . пути, указанные выше ('/' и '`/users'`) рассматриваются как префиксы путей, определённых в импортированных файлах. Так, например, если импортированный модуль users определяет путь для /profile, для доступа следует указать /users/profile. Мы поговорим подробнее о путях в последующей статье.

Последняя в файле промежуточная библиотека добавляет методы обработки ошибок и ответов 404 от HTTP.

```js
// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error("Not Found");
  err.status = 404;
  next(err);
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});
```

Объект app приложения Express теперь полностью настроен. Остался последний шаг - добавить его к экспортируемым элементам модуля (это позволит импортировать его в файле **/bin/www**).

```js
module.exports = app;
```

### Пути (Routes)

Файл путей /routes/users.js приведён ниже (файлы путей имеют сходную структуру, поэтому нет необходимости приводить также index.js). Сначала загружается модуль Express, затем он используется для получения объекта express.Router. После этого для этого объекта задаётся путь, и, наконец, объект-роутер экспортируется из модуля (именно это позволяет импортировать файл в app.js):.

```js
var express = require("express");
var router = express.Router();

/\* GET users listing. \*/
router.get("/", function (req, res, next) {
  res.send("respond with a resource");
});

module.exports = router;
```

Путь определяет колбэк-функцию, которая будет вызвана, когда обнаружится HTTP GET-запрос корректного вида. Образец для сопоставления пути задаётся при импорте модуля — ('`/users`') плюс что-то, определяемое в этом файле ('`/`'). Иными словами, этот путь будет использован, когда получен URL-запрос `/users/`.

**Примечание:** запустите сервер и задайте в браузере URL `http://localhost:3000/users/`. Вы должны увидеть сообщение: 'respond with a resource'.

Стоит отметить, что колбэк-функция имеет третий аргумент - '`next`', т. е. является не простой колбэк-функцией, а колбэк-функцией промежуточного модуля. Пока третий аргумент не используется, но будет полезен в дальнейшем, если мы захотим создать несколько обработчиков пути `'/'`.

### Представления (шаблоны)

Файлы преставлений (шаблонов) хранятся в каталоге **/views** (это указано в **app.js**) и имеют расширение **.pug**. Метод `Response.render()` выполняет указанный шаблон, передавая объекту значение именованной переменной, и затем посылает результат как ответ. В коде из **/routes/index.js** (приводится ниже) можно увидеть, что роут отвечает, используя шаблон "index" с переданным значением переменной "title" из шаблона.

```js
/\* GET home page. \*/
router.get("/", function (req, res) {
  res.render("index", { title: "Express" });
});
```

Шаблон для пути '/' приведён ниже (файл **index.pug**). О синтаксисе мы поговорим позже. Сейчас важно знать, что переменная title со значением 'Express' помещена в определённое место шаблона.
```

extends layout

block content
  h1= title
  p Welcome to #{title}
```

Мини-тест
---------

Создайте новый путь в **/routes/users.js**, чтобы выводить текст **"You're so cool"** или **"Ну, вы крутой!"** по URL *`/users/cool/`*. Проверьте его, запустив сервер и посетив в браузере `http://localhost:3000/users/cool/`.

Итоги
-----

Сейчас создан каркас проекта Local Library. Мы проверили, что он запускается с использованием Node. Но главное, что вы поняли структуру проекта, и знаете, где и как добавить пути и представления для нашей локальной библиотеки.

Далее мы изменим каркас, чтобы он работал как библиотечный веб-сайт

Смотрите также
--------------

* Express application generator (документация Express)
* Using template engi nes with Express (документация Express)
* Назад
* Обзор: Express Nodejs
* Далее