Перейти к основному содержимому

Middlewares

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

Создание middleware-функций

Ниже приведён базовый пример middleware:

import { RequestHandler } from '@shelepuginivan/lunatic' // Только для TypeScript

const myMiddleware: RequestHandler = (req, _res, next) => {
console.log(`${req.method} запрос получен в ${Date.now()}`)
next() // вызов следующей middleware-функции
}

app.use(myMiddleware) // использовать этот middleware

Вы можете указывать методы запроса и пути который определённая middleware-функция должна обрыбытывать, см. Роутинг:

import { RequestHandler } from '@shelepuginivan/lunatic'

const blockTrace: RequestHandler = (_req, res) => {
res.status(405).json({ message: 'Метод TRACE не разрешён' })
}

app.trace(blockTrace) // блокирует TRACE запросы

Встроенные middleware-функции

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

bodyParser

bodyParser - это middleware-функция, позволяющая получить тело запроса. Она поддерживает типы text/plain и application/json. Другие типы тела запроса, такие как multipart/form-data, будут проигнорированы.

Тело запроса можно получить из свойства req.body.

import { bodyParser } from '@shelepuginivan/lunatic'

app.use(bodyParser)

app.post('/', (req, res) => {
console.log(req.body)
res.status(200).json({ message: 'тело получено' })
})

app.listen(8000)

cookieParser

cookieParser - это middleware-функция, которая позволяет получить куки запроса:

import { cookieParser } from '@shelepuginivan/lunatic'

app.use(cookieParser)

app.post('/', (req, res) => {
console.log(req.cookies)
res.status(200).json({ message: 'received cookies' })
})

app.listen(8000)
примечание

Если среди заголовков запроса запросе нет Cookie, req.cookies принимает значение undefined.

cors

cors - это middleware-функция, которая позволяет обрабатывать CORS запросы.

import { cors } from '@shelepuginivan/lunatic'

app.use(cors())

app.post('/', (req, res) => {
res.status(200).json({ message: 'теперь поддерживается CORS!' })
})

app.listen(8000)

cors - это настраеваемая middleware-функция. Это значит, что она принимает объект options с настройками:

import { cors, CorsOptions } from '@shelepuginivan/lunatic'

const corsOptions: CorsOptions = {
origin: 'http://localhost:3000',
credentials: true,
}

app.use(cors(corsOptions)) // можно указать настройки

app.post('/', (req, res) => {
res.status(200).json({ message: 'теперь поддерживается CORS!' })
})

app.listen(8000)

CORS options

подсказка

Если вы используете TypeScript, вы можете импортировать интерфейс CorsOptions:

import { CorsOptions } from '@shelepuginivan/lunatic'
  • origin - разрешённый источник запроса
    • boolean - true разрешает все источники, false запрещает CORS запросы
    • string - устанавливает конкретный источник, например http://localhost:3000
    • RegExp - разрешает CORS запросы для тех источников, которые совпадают с регулярным выражением
    • string[] - разрешает CORS запросы для источников, указанных в массиве
    • RegExp[] - разрешает CORS запросы для источников, которые совпадают с одним из указанных в массиве регулярных выражений
    • (origin: string) => boolean - функция-предикат, должна вернуть true если CORS разрешён, иначе должна вернуть false
  • methods - разрешённые методы запроса
    • HttpMethod - конкретный HTTP-метод
    • HttpMethod[] - массив разрешённых методов
    • * - разрешает все методы
  • allowedHeaders - разрешённые заголовки запроса
    • string - конкретный заголовок
    • string[] - массив разрешённых заголовков
  • credentials - данные пользователя, такие как куки
    • boolean - разрешить или запретить соответственно. Обратите внимание, что если источник указан как * (как по умолчанию), эти данные не будут разрешены
  • exposedHeaders - заголовки ответа, которые могут быть получены через клиентский JavaScript-код
    • string - конкретный заголовок
    • string[] - массив открытых заголовков
  • maxAge - настраивает заголовок Access-Control-Max-Age
    • number - (сек) - значение заголовка Access-Control-Max-Age
  • corsErrorStatus - статус ответа сервера если CORS для этого источника запрещён. По умолчанию: 403
    • number - HTTP статус
  • preflightSuccessStatus - статус ответа сервера на предварительный OPTIONS запрос. По умолчанию: 204
    • number - HTTP статус

formParser

formParser - это middleware-функция, которая парсит multipart/form-data. Другие типы тела запроса игнорируются.

Текстовые поля формы доступны в свойстве req.body, а загруженные файлы - в req.files.

import { formParser, UploadedFile } from '@shelepuginivan/lunatic'
import { writeFile } from 'fs/promises'

app.use(formParser)

app.post('/', async (req, res) => {
console.log(req.body)

const [uploadedFile] = (req.files as Record<'file', UploadedFile[]>).file

await writeFile('new.txt', uploadedFile.data)
await res.status(200).json({ message: 'форма обработана' })
})

app.listen(8000)

UploadedFile

UploadedFile - это интерфейс файлов, загруженных через форму. Он имеет следующие свойста:

  • data: Buffer - содержание файла
  • filename: string - название файла
  • mimetype: string - MIME-тип файла
примечание

Свойства объекта req.files всегда имеют тип UploadedFile[], даже если был загружен лишь один файл. Это сделано с целью стандартизировать обработку разных форм.

notFound

notFound - это небольшая middleware-функция, которая возвращает статус 404 Not Found.

import { notFound } from '@shelepuginivan/lunatic'

app.post('/', (req, res) => {
res.status(200).json({ message: 'this IS found' })
})

app.use(notFound) // должна идти в самом конце, чтобы не отправить статус 404 преждевременно

app.listen(8000)
примечание

Обратите внимание, что по умолчанию, если ни один из маршрутов не совпал с запросом, сервер отвечает статусом 501 Not Implemented.

serveStatic

serveStatic - это middleware-функция, которая позволяет отправлять файлы из указанной директории.

import { serveStatic } from '@shelepuginivan/lunatic'
import { join } from 'path'

const staticDir = join(__dirname, 'static')

app.use('/static', serveStatic(staticDir))

app.listen(8000)

В этом примере все запросы, поступающие на /static and /static/* (так как serveStatic - это Router, см. Роутинг) будут обработаны serveStatic, тот, в свою очередь, отправит запрошенный файл в соответствии с путём запроса:

  • Путь запроса /static/style.css -> Файл <staticDir>/style.css

  • Путь запроса /static/images/d430849b.png -> Файл <staticDir>/images/d430849b.png

и так далее.

Если запрошенного файла не существует, сервер ответит статусом 404 Not Found.

serveStatic - это настраеваемая middleware-функция. Это значит, что она принимает объект options с настройками:

import { serveStatic, ServeStaticOptions } from '@shelepuginivan/lunatic'
import { join } from 'path'

const serveStaticOptions: ServeStaticOptions = {
dotfiles: 'forbid',
index: 'index.html',
lastModified: false,
}

app.use('/static', serveStatic(join(__dirname, 'static'), serveStaticOptions))

app.listen(8000)

ServeStaticOptions

подсказка

Если вы используете TypeScript, вы можете импортировать интерфейс ServeStaticOptions`:

import { ServeStaticOptions } from '@shelepuginivan/lunatic'
  • dotfiles - как сервер должен отвечать при попытке запросить dotfile (файл, имя которого начинается с ., например .htaccess). По умолчанию: 'ignore'

    • 'allow' - ответить статусом 200 OK и отправить файл
    • 'forbid' - ответить статусом 403 Forbidden и не отправлять файл
    • 'ignore' - ответить статусом 404 Not Found и не отправлять файл, то есть сделать вид, что файла не существует
  • etag - разрешить или запретить заголовок ETag. По умолчанию: true

    • boolean - разрешить или запретить заголовок ETag соответственно
  • index - как сервер должен отвечать, если запрошенный путь является директорией, какой файл из этой директории он отправит. По умолчанию: 'index.html'.

    • string - имя файла
    • false - установите, если хотите отключить эту функцию, тогда сервер ответит статусом 404 Not Found
  • lastModified - разрешить или запретить заголовок Last-Modified. По умолчанию: true

    • boolean - разрешить или запретить заголовок Last-Modified соответственно
к сведению

Значение заголовка ETag устанавливается как хэш от времени последнего изменения файла.