С чтением данных мы разобрались, давайте теперь попробуем их записывать.
Создание новой записи в нашей телефонной книге через telnet будет таким:
telnet localhost 4000
Trying ::1...
Connected to localhost.
Escape character is '^]'.
POST /users.json HTTP/1.1
Host: localhost:4000
Connection: close
Content-Type: application/json
Content-Length: 38
{"name":"Bob","phone":"912-114-23-22"}
HTTP/1.1 201 Created
Content-Type: application/json
Date: Sat, 08 Oct 2016 10:23:06 GMT
Connection: keep-alive
Transfer-Encoding: chunked
5c
{"meta":{"location":"/users/15.json"},"data":{"name":"Bob","phone":"912-114-23-22","id":15}}
0
Отметим несколько важных моментов:
Добавлению новых данных в http соответствует глагол POST. Такой запрос не
является идемпотентным (в соответствии с семантикой POST). Это означает, что
повторная отправка данных приведёт к созданию дублей либо к ошибке
(если внутри проверяются дубли).
Почти наверняка вы сталкивались с подобным поведением в интернете. Быстрый
двойной клик на кнопке приводит к тому, что появляется, например, два комментария.
А иногда, когда вы нажимаете f5, браузер спрашивает "вы точно хотите повторно
отправить данные?". Это означает, что предыдущий запрос был POST.
Так как в случае POST запроса мы отправляем тело, нам нужно указать два заголовка:
Content-Type- чтобы сервер знал, как парсить наше телоContent-Length- для определения конца запроса
Сервер
import http from 'http'
export default http.createServer((req, res) => {
const body = []
req
.on('data', chunk => body.push(chunk.toString()))
.on('end', () => {
const data = body.join()
req.end(data)
})
})
С точки зрения сервера body не является свойством объекта request,
вместо этого сам request является eventEmitter и позволяет подписаться
на событие data, которое вызывается каждый раз, когда приходит новая порция
данных. Такой подход используется из-за того, что данные можно слать на сервер порциями (chunks).
После того, как запрос был полностью обработан, request инициирует событие
end, внутри которого и нужно делать обработку поступивших данных и отвечать
на запрос.
Обратите внимание на то, как идет работа с чанками данных. Перед тем, как попасть
в массив body, делается преобразование в строку с помощью toString.
Это связано с тем, что сырой чанк приходит как тип данных Buffer, который
позволяет работать в том числе с данными в бинарном виде, а не только текстовом.
Валидация
В разработке существует правило: "никогда не доверяй данным из внешнего источника". Это означает, что данные пришедшие извне (от других систем, от пользователя), всегда рассматриваются, как потенциально опасные и не соответствующие требованиям формата этих данных. Если этого не делать, не проверять их, не фильтровать, то легко можно получить ситуацию, при которой вашу систему смогут обойти и, в худшем случае, разрушить или увести данные.
Процесс проверки данных на корректность называется валидацией. В типичных веб-приложениях данные приходят из форм, которые заполняют пользователи или по API, и перед тем, как добавлять их к себе в хранилище, важно провалидировать эти данные. В самой валидации нет ничего сверхъестественного, это обычная проверка в коде, например, на то, что телефон приходит в строго определенном формате.
Ответ
Важно отвечать на POST запрос правильно, и это зависит от разных факторов.
Выделим три ситуации (хотя, их больше на самом деле):
Успешный запрос (API)
Код ответа 201 (Created). В теле ответа обычно возвращают данные, такие как
присвоенный идентификатор, что позволяет обратиться к вновь созданной сущности.
Успешный запрос (Web)
Необходимо обязательно сделать перенаправление (редирект) на другую страницу. Предпочтительно
использовать код 303 (See Other).
Ошибка валидации
В этой ситуации код ответа должен быть 422 (unprocessable entity) в любом случае.
Для разных видов приложения будет различаться только тело и его формат.