[Перевод] Руководство по Express.js. Часть 2

le_3dgaasa_ao_agxj8b-2hhgao.png

Доброго времени суток, друзья!

Представляю вашему вниманию перевод второй части Руководства по 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 })


Самыми полезными настройками являются следующие:
Куки можно удалить с помощью:

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-запроса:

eq0jrhvquucoryw8cp7iuwydh84.png

Пример отклонения XHR-запроса:

d9fykqzn4xasjqslglbzsnfikvk.png

Запросы из другого источника завершаются неудачно, если обращение осуществляется:

  • К другому домену
  • К другому поддомену
  • К другому порту
  • К другому протоколу


Это делается для вашей безопасности: для того, чтобы злонамеренные пользователи не сломали приложение.

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

Как это сделать?

Это зависит от того, какие технологии используются на сервере.

Пример с 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, сеть) можно увидеть сообщение от сервера:

gjys2ikzyhhxvxyldfnkkugobx4.png

Разрешаем только запросы из определенного источника


У рассмотренного подхода есть один существенный недостаток: любой запрос будет рассматриваться сервером как допустимый.

Как видите, ответ содержит заголовок Access-Control-Allow-Origin со значением *:

h4eykecpohp4iuwskhltcpwihhq.png

Нам необходимо настроить сервер таким образом, чтобы он допускал запросы лишь из определенных источников.

Перепишем наш код:

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), промежуточном программном обеспечении (промежуточном слое), работе со статическими файлами, отправке файлов и сессиях.

Следите за обновлениями. Благодарю за внимание и хорошего дня.

© Habrahabr.ru