Меню

Middleware в Express (Функции промежуточного слоя)

03.07.2018 - Back-end, NodeJS

Middleware

Один из ключевых концепций, которые нужно освоить, это концепция middleware или middleware function. Middleware function по простому это функция, которая берет объект request и или возвращает ответ (response) клиенту, или передает управление другой middleware function. Мы уже видели примеры middleware функций. Один из них, это route handler функция. Т.е. каждая route handler функция (та, которая передается callbackами при получении запроса), также является middleware функцией. Потому что она берет объект request, и в нашем случаше, например, возвращает ответ клиенту. Она завершает цикл request-response.

app.get('/', (req, res) => { //Middleware
    res.send('Hello, world');
})

В общем, это один из примеров middleware функций.

Также у нас был другой пример.

app.use(express.json());

Когда мы вызываем метод express.json(), этот метод возвращает функцию — middleware функцию. Работа этой функции — прочитать запрос, и если есть json объект в теле запроса, она распарсит тело запроса в json объект и затем установит в req.body.

Когда мы получаем запрос на сервер, этот запрос проходит через pipeline, который мы называем request processing pipeline. В этот pipeline у нас есть одна или более middleware функций. Каждая middleware функция или завершает request-response цикл, возвращая объект response объект или передавая контроль другой middleware функции.

В нашей текущей имплементации наш request processing pipeline имеет две middleware функции. Первая — это middleware функция, которая парсит тело запроса в json объект, в данном случае она не завершает цикл response-request, а передает контроль другой middleware функции, которая в нашем случае — route handler. В route handler у нас есть request объект с заполненным свойством body. Здесь мы можем произвести некоторые операции и затем завершить цикл response-request, возвращая ответ клиенту.

req-res-middleware

Итак, express включает в себя несколько middleware функций. Но мы можем создать свои кастомный мидлвэар функции, и подставить их вперед processing pipeline. И каждый запрос, который мы получим на сервер будет проходить через нашу мидлвэар функцию (функцию промежуточного слоя). С этой кастомной middleware функцией мы можем обеспечить перекрестный вызов. Например мы можем делать логирование, аутентификацию, авторизацию и т.д. Итак, наше экспресс приложение, ни что иное как группа middleware функций.

Creating a custom middleware functions (Создание кастомных функций промежуточного слоя).

Сейчас мы посмотрим как создавать кастомный middleware функции. Итак, у нас есть json middleware функция.

app.use(express.json());

После этого мы вызовем app.use. Еще раз, мы вызываем этот метод, чтобы установить middleware функцию в request processing pipeline. Теперь передадим в него функцию, она принимает req, res и next аргументы. next ссылается на следующую middleware функцию в pipeline. В нашем случае наша функция будет просто выводить инфу в console.log. Представим что наша функция логирует запросы. Чтобы выполнить логирование. Затем вы вызовем next, чтобы передать контроль следующей функции в pipeline.

app.use((req, res, next) => {
    console.log('Logging...');
    next();
});

Если мы этого не сделаем, так как мы не завершаем цикл response-request, запрос конечном итоге повиснет. Мы можем также создать другую middleware функцию, чтобы выполнить авторизацию.

app.use((req, res, next) => {
    console.log('Authenticating...');
    next();
});

Если мы запустим сервер и сделаем запрос, то получим в консоли, сначала Logging..., затем Authenticating....

Обратите внимаение на то, что middleware функции вызываются последовательно.
Сначала logging, затем Authenticating, а затем route handler.

Для чистого код, когда Вам нужны кастомные middleware функции, то не нужно всё писать внутри файла, где они будут выполняться. Обычно их выделяют в отдельный модуль (файл). Создадим файл logger.js и запишем функцию туда.

function log (req, res, next) => {
    console.log('Logging...');
    next();
}

module.exports = log;

В файла index.js в самом верху, подгрузим наш модуль.

const logger = require('./logger');

А затем передадим нашу функцию в request processing pipeline.

app.use(logger);

Теперь вы понимаете, что значит строка: app.use(express.json());. Когда мы вызываем express.json(), она возвращает функцию, а точнее middleware функцию, у которой есть три параметра — req, res, next. Эта middleware функцию парсит тело запроса и возвращает json объект, который будет установлен в req.body и затем она передаст контроль в следующую middleware функцию.

Built-in middleware (Встроенные функции промежуточного слоя)

Как мы уже говорили ранее, в express уже встроено некоторое количество middleware функций. Одна из них это express.json(). У нас есть другая middleware аналогичная функция, которая называется express.urlencoded.

app.use(express.json());
app.use(express.urlencoded());

Она также возвращает middleware функцию. Эта middleware функция парсит входящий запрос с url кодированными адресами.
Это запрос с телом типа как key=value&key1=value и т.д. Есть более традиционные подходы, это не то чем ползуются часто в наши дни. По простому, если у вас есть какая то html форма с заполненными полями и она была отправлена на сервер, тело этого запроса будет выглядеть как только что показал: key=value&key1=value, вот почему есть url encoded payload в теле запроса. Этот middleware парсит это тело и заполняет req.body как json объект.

Давайте посмотрим как это работает. Запустим серевер и через postman отправим POST запрос. Выберем x-www-form-urlendoded. И напишем пару парк ключ-значение. Мы увидим в ответе json объект.

Middleware функция была в состоянии прочитать запрос с urlencoded содержимым. Если мы сейчас посмотрим на терминал, то увидим предупреждение — body-parser deprecated undefined extended: provide extended option. Это предупреждение говорит нам, что мы должны передать в middleware функцию объект и установить extended как true.

app.use(express.urlencoded({
    extended: true
}));

С этим мы сможем передавать массивы и сложные объекты, используя urlendoded формат.

Теперь рассмотрим еще одну middleware функцию встроенную в express — static. Мы используем её чтобы обслуживать статические файлы. Давайте посмотрим как она работает. express.static() — она принимает один аргумент — имя директории. В нашем примере будем использовать public. Итак мы собираемся складывать все наши статические файлы, такие как css, images и т.д. внутрь этой папки.

Давайте создадим эту папку и добавим туда простой текстовый файл.

Теперь если мы в браузере наберем localhost:3000/readme.txt, то увидим его содержимое.

Итак с этим middleware мы можем обслуживать статический контент.

Third-party middleware (Стороннии функции промежуточного слоя)

Посмотрим какие есть сторонние middleware.
На сайте expressjs.com в разделе resources -> Middleware мы увидим все сторонние middleware, которые мы можем использовать у себя в приложениях.

Один из важные middleware из этого списка — helmet. Он помогает защитить ваше приложения с помощью установки различных заголовков. Все что нам нужно сделать, это импортировать его и вклинить с помощью app.use.

Давайте установим helmet.

npm i helmet

Теперь зареквайрим его в нашем файле. И вызовем его.

const helmet = require('helmet');
app.use(helmet());

Другой полезный middleware — morgan. Он используется для логгирования HTTP запросов.

npm i morgan
const morgan = require('morgan');

Мы можем указать для него разные форматы. Мы в примере будем использовать самый просто — tiny. Чтобы увидеть все возможные вариант — изучайте документацию. По умолчанию морган пишет логи в консоль. Но мы можем сконфигурировать его, чтобы он писал отчеты в файл.

Единственное помните, что каждый middleware тормозит ответ. Поэтому убедитесь что они вам нужны. Опять же, на примере morgan — лучше не использовать его в продакшене, а если использовать, то только чтобы решить проблему и снова его отключить.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *