Доброго времени суток, друзья!
Представляю вашему вниманию перевод второй части Руководства по Express — веб-феймворку для Node.js автора Flavio Copes.
Предполагается, что вы знакомы с Node.js. Если нет, то прошу сюда.
Без дальнейших предисловий.
11. Управление куки
Для управления куки используйте метод Response.cookie().
Например:
res.cookie('username', 'John')
Данный метод принимает третий аргумент, содержащий определенные настройки:
res.cookie('username', 'John', { domain: '.exmaple.com', path: '/admin', secure: true })
res.cookie('username', 'John', { expires: new Date(Date.now() + 900000), httpOnly: true })
Самыми полезными настройками являются следующие:
Значение | Описание |
---|---|
domain | название домена куки |
expires | определяет время жизни куки. При отсутствии или при значении, равном 0, куки будет удалена по окончанию сессии (при закрытии вкладки браузера) |
httpOnly | если true, то куки будет доступна только через веб-сервер |
maxAge | время жизни куки относительно текущего времени, определяется в миллисекундах |
path | путь к куки. По умолчанию / |
secure | если true, то куки будет доступна только по протоколу HTTPS |
signed | делает куки подписанной |
sameSite | если true, то куки доступна только запросам из одного источника |
Куки можно удалить с помощью:
res.clearCookie('username')
12. Работа с HTTP-заголовками
Получение заголовков из запроса
Заголовки запроса можно получить через свойство Request.headers:
app.get('/', (req, res) => {
console.log(req.headers)
})
Метод Request.header() используется для получения конкретного заголовка:
app.get('/', (req, res) => {
req.header('User-Agent')
})
Изменение значения заголовка в ответе
Значение любого заголовка можно изменить с помощью метода Response.set():
res.set('Content-Type': 'text/html')
Для заголовка Content-Type имеется сокращенный вариант:
res.type('.html') // => 'text/html'
res.type('html') // => 'text/html'
res.type('json') // => 'application/json'
res.type('application/json') // => 'application/json'
res.type('png') // => 'image/png'
13. Перенаправления
Перенаправления являются распространенным явлением в веб-разработке. Для перенаправления используется метод Response.redirect():
res.redirect('/go-there')
Это создает постоянное перенаправление (302).
Временное перенаправление (301) можно сделать так:
res.redirect(301, '/go-there')
В качестве аргумента может использоваться абсолютный путь (/go-there), абсолютный URL (https://anothersite.com), относительный путь (go-there) или… для того, чтобы подняться на один уровень выше.
res.redirect('../go-there')
res.redirect('..')
Также можно вернуться к странице, указанной в заголовке Referer (по умолчанию имеет значение /):
res.redirect('back')
14. Маршрутизация
Маршрутизация или роутинг — это определение того, что должно произойти при обращении к конкретному URL. Другими словами, это определение того, какие части приложения должны обрабатывать конкретные запросы.
В примере Hello World мы использовали следующее:
app.get('/', (req, res) => {/* */})
Здесь обрабатываем GET-запросы к корневому домену /, отправляя определенный ответ.
Именованные параметры
Что если мы хотим обрабатывать определенные запросы. Допустим, мы хотим принимать строку и возвращать эту же строку, но состоящую только из заглавных букв и, при этом, не хотим использовать строку запроса. В этом случае нам на помощь приходят именованные параметры:
app.get('/uppercase/:theValue', (req, res) => res.send(req.params.theValue.toUpperCase()))
Если мы отправим такой запрос: /uppercase/test, то получим TEST в теле ответа.
В одном URL можно указывать несколько именованных параметров, все они будут сохранены в свойстве req.params.
Использлование регулярного выражения для определения пути
Для обработки нескольких запросов одним роутером можно использовать регулярные выражения:
app.get(/post/, (req, res) => {/* */})
В данном случае совпадения будут найдены для /post, /post/first, /thepost, /posting/something и т.д.
15. SOP (политика общего происхождения или одного источника)
JavaScript-приложения, запущенные в браузере, как правило, могут получать только ресурсы, находящиеся в том же источнике (origin — протокол, хост и порт).
Загрузка изображений или стилей/скриптов, обычно, работает, но запросы XHR или Fetch к другому серверу терпят неудачу до тех пор, пока сервер не разрешит совместное использование ресурсов.
Данный механизм называется CORS (Cross-Origin Resource Sharing).
Загрузка веб-шрифтов с помощью @font-face также следует этой политике, впрочем, как и загрузка других вещей (таких как текстуры WebGL или ресурсы для метода drawImage Canvas API).
Более того, CORS применяется и в отношении ES6 модулей.
Если вы не определите CORS на сервере для отправки ответов на запросы из других источников, эти запросы будут отклонены.
Пример отклонения Fetch-запроса:
Пример отклонения XHR-запроса:
Запросы из другого источника завершаются неудачно, если обращение осуществляется:
- К другому домену
- К другому поддомену
- К другому порту
- К другому протоколу
Это делается для вашей безопасности: для того, чтобы злонамеренные пользователи не сломали приложение.
Однако, если вы контролируете и сервер, и клиента, у вас есть все основания для того, чтобы разрешить им общаться между собой.
Как это сделать?
Это зависит от того, какие технологии используются на сервере.
Пример с Express
Если вы используете Node.js и фреймворк Express, ППО CORS — это то, что вам нужно.
Вот реализация простого сервера:
const express = require('express')
const app = express()
app.get('/without-cors', (req, res, next) => {
res.json({ msg: ':( no cors, no party!' })
})
const server = app.listen(3000, () => {
console.log('Listening on port %s', server.address().port)
})
Если вы отправите запрос к этому серверу из другого источника, то получите ошибку.
Для того, чтобы это стало возможным, необходимо определить cors и добавить его в качестве ППО в роутер:
const express = require('express')
const cors = require('cors')
const app = express()
app.get('/with-cors', cors(), (req, res, next) => {
res.json({ msg: 'With cors it work! :)' })
})
Я сделал небольшой пример. Вот код клиента: https://glitch.com/edit/#!/flavio-cors-client
А вот код сервера: https://glitch.com/edit/#!/flaviocopes-cors-example-express
Обратите внимание, что ответы на неудачные запросы все равно отправляются. В соответствующем разделе инструментов разработчика (Network, сеть) можно увидеть сообщение от сервера:
Разрешаем только запросы из определенного источника
У рассмотренного подхода есть один существенный недостаток: любой запрос будет рассматриваться сервером как допустимый.
Как видите, ответ содержит заголовок Access-Control-Allow-Origin со значением *:
Нам необходимо настроить сервер таким образом, чтобы он допускал запросы лишь из определенных источников.
Перепишем наш код:
const cors = require('cors')
const corsOptions = {
origin: 'https://yourdomain.com'
}
app.get('/products/:id', cors(corsOptions), (req, res, next) => {
// ...
})
Также можно определить несколько допустимых источников:
const whitelist = ['https://example.com', 'https://example2.com']
const corsOptions = {
origin: (origin, cb) =>
(whitelist.indexOf(origin) !== -1)
? cb(null, true)
: cb(new Error('Not allowed by CORS'))
}
Отправка предаврительных запросов
Некоторые запросы являются простыми, например, GET, POST, HEAD.
Что касается POST-запроса, то он является простым только в том случае, если его заголовок Content-Type имеет одно из следующих значений:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
Все остальные запросы нуждаются в отправке предварительного запроса. Это требуется браузеру для предоставления или отказа в доступе на основании информации из OPTIONS-запроса.
Предварительный запрос содержит несколько заголовков, позволяющих браузеру определить права на доступ:
OPTIONS /the/resource/you/request
Access-Control-Request-Method: POST
Access-Control-Request-Headers: origin, x-requested-with, accept
Origin: https://yourdomain.com
Ответ сервера выглядит примерно так:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Мы провели проверку для POST-запроса, однако сервер сообщил нам, что мы можем использовать и другие запросы.
Возвращаясь к Express, вот как сервер должен обрабатывать OPTIONS-запросы:
const express = require('express')
const cors = require('cors')
const app = express()
// разрешаем OPTIONS для одного ресурса
app.options('the/resource/you/request', cors())
// разрешаем OPTIONS для всех ресурсов
app.options('*', cors())
На сегодня это все. В следующей части мы поговорим о шаблонизации (Pug), промежуточном программном обеспечении (промежуточном слое), работе со статическими файлами, отправке файлов и сессиях.
Следите за обновлениями. Благодарю за внимание и хорошего дня.
gfarniev
Но ведь express практически не поддерживается, теперь же koa рулит.
Alexufo
и fafstify