Мидълуер (Express, Nodejs):

Преглед:

Мидълуерът е често неразбрана тема, тъй като звучи и изглежда много сложно, но в действителност мидълуерът всъщност е наистина ясен.

Цялата идея на междинния софтуер е да изпълни някакъв код преди действието на контролера, което изпраща отговора, и след като сървърът получи заявката от клиента. По съществотова е код, който се изпълнява в средата на вашата заявка, оттук и името мидълуер.

Преди да навляза твърде задълбочено в детайлите на мидълуера обаче, искам да настроя основен Express сървър с два маршрута.

Настройване на експресен сървър (Nodejs):

За да започнете да работите с Node.js проект, ще трябва да изпълните няколко прости команди, можете да следвате прости стъпки, споменати по-долу:

Стъпки:

  1. Изпълнете npm init -y, за да създадете основен файл package.json.
  2. Инсталирайте Express, като стартирате, npm i express.
  3. Създайте файл server.js със следния код.
const express = require('express')
const app = express()
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
app.listen(3000, () => console.log('Server Started'))

Този файл server.js просто настройва сървър на порт 3000, който има два маршрута, маршрут на начална страница и маршрут на потребителска страница.

4. Стартирайте node server.js, вижте съобщение в конзолата, което казва Сървърът стартира.

5. Стартирайте node server.js, вижте съобщение в конзолата, което гласи Сървърът стартира.

6. Отворете всеки браузър до localhost:3000, вижте съобщението Начална страница.

7. отидете на localhost:3000/users, тогава трябва да видите съобщението Страница за потребители.

Това е цялата основна настройка, от която ще се нуждаем до края на тази статия. Докато правим промени, ще трябва да рестартирате сървъра си в конзолата, за да видите промените да влязат в сила.

Какво е Middleware?

Говорих накратко за междинния софтуер като функции, които се изпълняват, след като сървърът получи заявката и преди действието на контролера да изпрати отговор, но има още няколко неща, които са специфични за междинния софтуер.

Най-важното е, че функциите на междинния софтуер имат достъп до променливите за отговор (res) и заявка (req) и могат да ги променят или използват според нуждите. Мидълуерните функции също имат трети параметър, който е next функция. Тази функция е важна, тъй като трябва да бъде извикана от междинен софтуер, за да бъде изпълнен следващият междинен софтуер. Ако тази функция не бъде извикана, няма да бъде извикан друг междинен софтуер, включително действието на контролера.

Всичко това е малко трудно за разбиране само от текст, така че в следващия раздел ще създадем междинен софтуер за регистриране, който ще регистрира URL адреса на заявката, която потребителят прави.

Как да създадете междинен софтуер за регистриране

Както споменах в предишния раздел, междинният софтуер приема три параметъра, req, res и next, така че за да създадем междинен софтуер, трябва да създадем функция, която има тези три входа.

const express = require('express')
const app = express()
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {  console.log('Inside Middleware')}
app.listen(3000, () => console.log('Server Started'))

Сега имаме обвивката на основна функция на мидълуера, дефинирана с някакво съдържание на контейнер, но приложението не го използва. Express има няколко различни начина, по които можете да дефинирате междинния софтуер, който да се използва, но за този пример ще направим този междинен софтуер да се изпълнява преди всяко отделно действие на контролера, като го добавим към нивото на приложението. Това може да стане с помощта на функцията use на променливата app по този начин.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log('Inside Middleware')
}
app.listen(3000, () => console.log('Server Started'))

Приложението вече използва мидълуера, който дефинирахме, и ако рестартираме нашия сървър и отидем до някоя от страниците в нашето приложение, ще забележите, че в конзолата се появява съобщението Inside Middleware. Това е страхотно, но има малък проблем. Приложението сега се зарежда завинаги и никога не завършва заявката. Това е така, защото в нашия междинен софтуер ние не извикваме функцията next, така че действието на контролера никога не се извиква. Можем да поправим това, като извикаме next след нашето регистриране.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log('Inside Middleware')
  next()}
app.listen(3000, () => console.log('Server Started'))

Сега, ако рестартирате сървъра, ще забележите, че всичко се регистрира правилно и уеб страницата се зарежда правилно. Следващото нещо, което трябва да направите, е действително да излезете от URL адреса, до който потребителят има достъп в междинния софтуер. Това е мястото, където променливата req ще бъде полезна.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log(`${new Date().toISOString()}: ${req.originalUrl}`)  next()
}
app.listen(3000, () => console.log('Server Started'))

Мидълуерът за регистриране вече работи 100% правилно по всички маршрути в приложението, но ние само надраскахме повърхността на полезността на мидълуера. В следващия пример ще разгледаме създаването на прост междинен софтуер за оторизация за потребителската страница.

Пример за разширен междинен софтуер

За да започнем, трябва да създадем друга функция, която да използваме като междинен софтуер.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log(`${new Date().toISOString()}: ${req.originalUrl}`)
  next()
}
function authorizeUsersAccess(req, res, next) {  console.log('authorizeUsersAccess Middleware')  next()}
app.listen(3000, () => console.log('Server Started'))

Това е само обвивка на функция, която да се използва като мидълуер, но можем да я добавим към маршрута на нашата потребителска страница сега, за да гарантираме, че нашият мидълуер се изпълнява само по маршрута на потребителската страница. Това може да стане чрез добавяне на функцията като параметър към функцията app.get за потребителската страница.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', authorizeUsersAccess, (req, res) => {  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log(`${new Date().toISOString()}: ${req.originalUrl}`)
  next()
}
function authorizeUsersAccess(req, res, next) {
  console.log('authorizeUsersAccess Middleware')
  next()
}
app.listen(3000, () => console.log('Server Started'))

Сега, ако рестартирате сървъра и отидете на потребителската страница, трябва да видите съобщението authorizeUsersAccess Middleware, но ако отидете на началната страница, това съобщение няма да се покаже. Сега имаме междинен софтуер, който се изпълнява само по един маршрут в приложението. Следващото нещо, което трябва да направите, е да попълните логиката на тази функция, така че ако потребителят няма достъп до страницата, вместо това да получи съобщение за грешка.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', authorizeUsersAccess, (req, res) => {
  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log(`${new Date().toISOString()}: ${req.originalUrl}`)
  next()
}
function authorizeUsersAccess(req, res, next) {
  if (req.query.admin === 'true') {    next()  } else {    res.send('ERROR: You must be an admin')  }}
app.listen(3000, () => console.log('Server Started'))

Този междинен софтуер сега проверява дали параметърът на заявката admin=true е в URL адреса и ако не е, на потребителя се показва съобщение за грешка. Можете да тествате това, като отидете на http://localhost:3000/users и ще видите съобщение за грешка, обясняващо, че не сте администратор. Ако вместо това отидете на http://localhost:3000/users?admin=true, ще ви бъде изпратена страницата за нормални потребители, тъй като сте задали параметъра на заявката на admin на true.

Едно друго нещо, което е наистина полезно с междинния софтуер, е възможността за изпращане на данни между междинния софтуер. Няма начин да направите това със следващата функция, но можете да промените променливите req или res, за да зададете свои собствени персонализирани данни. Например в предишния пример, ако искахме да зададем променлива на true, ако потребителят е администратор, лесно бихме могли да направим това.

const express = require('express')
const app = express()
app.use(loggingMiddleware)
app.get('/', (req, res) => {
  res.send('Home Page')
})
app.get('/users', authorizeUsersAccess, (req, res) => {
  console.log(req.admin)  res.send('Users Page')
})
function loggingMiddleware(req, res, next) {
  console.log(`${new Date().toISOString()}: ${req.originalUrl}`)
  next()
}
function authorizeUsersAccess(req, res, next) {
  if (req.query.admin === 'true') {
    req.admin = true    next()
  } else {
    res.send('ERROR: You must be an admin')
  }
}
app.listen(3000, () => console.log('Server Started'))

Този код задава администраторска променлива на обекта req, който след това е достъпен в действието на контролера за потребителската страница.

Допълнителна информация за междинен софтуер

Това е по-голямата част от всичко, което трябва да знаете за функциите на мидълуера, но има няколко допълнителни неща, които е важно да знаете.

1. Действията на контролера са точно като мидълуер

Едно нещо, което може би сте забелязали е, че действията на контролера, които имат променливи req и res, са много подобни на мидълуера. Това е така, защото те по същество са междинен софтуер, но без друг междинен софтуер, който идва след тях. Те са краят на веригата, поради което никога няма следващи повиквания в действието на контролера.

2. Обаждането следващо не е същото като обаждането за връщане

Най-голямата грешка, която виждам, че разработчиците правят, когато работят с мидълуер, е, че третират функцията next така, сякаш е излязла от мидълуера. Вземете например този междинен софтуер.

function middleware(req, res, next) {
  if (req.valid) {
    next()
  }
  res.send('Invalid Request')
}

На номинална стойност този код изглежда правилен. Ако заявката е валидна, тогава се извиква функцията next и ако не е валидна, тогава изпраща съобщение за грешка. Проблемът е, че функцията next всъщност не се връща от функцията на междинния софтуер. Това означава, че когато се извика next, следващият мидълуер ще се изпълни и това ще продължи, докато не остане повече мидълуер за изпълнение. Тогава, след като целият междинен софтуер, след като този междинен софтуер приключи, изпълнението на кода ще се върне обратно веднага след извикването next във всеки от междинния софтуер. Това означава, че в този междинен софтуер съобщението за грешка винаги ще се изпраща на потребителя, което очевидно не е това, което искате. Лесен начин да предотвратите това е като просто се върнете, когато се обадите на next

function middleware(req, res, next) {
  if (req.valid) {
    return next()  }
  res.send('Invalid Request')
}

Сега кодът вече няма да се изпълнява след извикване на next, тъй като ще се върне извън функцията. Лесен начин да видите този проблем в действие е със следния код.

const express = require('express')
const app = express()
app.get('/', middleware, (req, res) => {
  console.log('Inside Home Page')
  res.send('Home Page')
})
function middleware(req, res, next) {
  console.log('Before Next')
  next()
  console.log('After Next')
}
app.listen(3000, () => console.log('Server Started'))

Когато стартирате този код и отидете на началната страница, конзолата ще отпечата следните съобщения в ред.

Before Next
Inside Home Page
After Next

По същество това, което се случва, е, че мидълуерът се извиква и той излиза от оператора преди. След това се извиква next, така че се извиква следващият набор от междинен софтуер, който е действието на контролера, където се регистрира съобщението на началната страница. Накрая действието на контролера завършва изпълнението, така че междинният софтуер след това изпълнява кода след next, който излиза от оператора след.

3. Мидълуерът ще се изпълнява по ред

Това може да изглежда очевидно, но когато дефинирате междинен софтуер, той ще се изпълнява в реда, в който се използва. Вземете за пример следния код.

const express = require('express')
const app = express()
app.use(middlewareThree)
app.use(middlewareOne)
app.get('/', middlewareTwo, middlewareFour, (req, res) => {
  console.log('Inside Home Page')
  res.send('Home Page')
})
function middlewareOne(req, res, next) {
  console.log('Middleware One')
  next()
}
function middlewareTwo(req, res, next) {
  console.log('Middleware Two')
  next()
}
function middlewareThree(req, res, next) {
  console.log('Middleware Three')
  next()
}
function middlewareFour(req, res, next) {
  console.log('Middleware Four')
  next()
}
app.listen(3000, () => console.log('Server Started'))

Тъй като операторите app.use идват първи, междинният софтуер в тези оператори ще се изпълни първи в реда, в който са добавени. След това се дефинира app.get мидълуерът и отново те ще бъдат изпълнени в реда, в който са във функцията app.get. Това ще доведе до следния изход на конзолата, ако се изпълни.

Middleware Three
Middleware One
Middleware Two
Middleware Four

Заключение

Това е всичко, което трябва да знаете за мидълуера. Мидълуерът е невероятно мощен за почистване на кода и прави много по-лесни неща като оторизация на потребителите и удостоверяване, но може да се използва за много повече от това поради невероятната гъвкавост на мидълуера.

Това е всичко от моя страна, момчета, мисля, че към този момент е добре да използвате мидълуер. Ако ви харесва, моля, не се колебайте да натиснете бутона за следване и отзивите са добре дошли.

Благодарим ви, момчета, нека наваксаме с новия.