Найти тему
ZDG

Как работает веб-сайт #8: POST-запросы

Оглавление

Предыдущие части: GET-запросы, Основы HTML, Виртуальные хосты, IP-адреса и DNS, Ставим Apache, Порты и сокеты, Введение

Ранее мы выяснили, что GET-запрос – это когда данные для веб-сервера передаются прямо в URL. У них есть ограничение – длина 2048 байт.

Мы можем закачивать на сервер, допустим, файлы размером 100 мегабайт. Очевидно, содержимое такого файла не поместится в URL.

Для передачи больших объёмов данных используется POST-запрос. Он отличается от GET тем, что данные помещаются не в URL, а в тело запроса.

Что такое тело запроса? Здесь нам нужно обратиться к протоколу HTTP. Отмечу, что знать протокол досконально необязательно. Это знание вам понадобится только тогда, когда вы будете разрабатывать собственный веб-сервер или веб-браузер, а также если вы хотите послать запрос на веб-сервер руками. Кстати, давайте попробуем руками.

Вам нужна любая телекоммуникационная программа вроде telnet. На Windows лучший выбор это PuTTY. Рекомендую поставить. Мы открываем PuTTY, и задаём новое соединение:

Необходимо задать те поля, которые я обвёл красным. Мы создаём соединение к хосту zdg.ru на порт 80 – как вы понимаете, мы будем соединяться с веб-сервером. Connection type (тип соединения) мы устанавливаем как Raw (сырые данные), то есть данные будут передаваться как есть, без обработки.

Теперь нажимаем кнопку Open и открываем соединение:

-2

Мы видим чёрный экран. Это нормально. Это работает протокол HTTP. Мы соединились с сервером, и теперь сервер молча ждёт, что мы ему скажем.

Мы набираем руками вот такой запрос:

-3

И дважды нажимаем Enter. После чего сервер отдаёт нам HTML-страницу сайта в текстовом виде и закрывает соединение:

-4

Если бы мы были веб-браузером, мы взяли бы этот HTML-код и нарисовали бы страницу.

Видите, мы успешно использовали HTTP-протокол вручную. Мы построили такой GET-запрос:

GET / HTTP/1.1

Это значит – получить страницу из корня сайта "/", используя версию протокола 1.1.

Затем мы указали хост, к которому хотим обратиться:

Host: zdg.ru

А затем нажали Enter два раза. Почему именно два раза?

Потому что всё, что мы написали выше – это заголовок запроса. Кроме заголовка, у запроса есть ещё тело, которое отделено от заголовка пустой строкой. Когда мы нажали на Enter два раза, мы создали пустую строку и дали серверу понять, что заголовок закончился. После чего сервер сразу отдал нам страницу.

Таким образом, мы послали GET-запрос, у которого есть только заголовок и нет тела.

Теперь посмотрим пример, как должен выглядеть POST-запрос:

POST / HTTP/1.1
Host: zdg.ru
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

field1=value1&field2=value2

В данном случае мы пишем POST вместо GET, и главное – в заголовке появилось поле Content-Length. Это длина тела запроса, и она равна 27 байт.

После пустой строки идёт уже тело запроса:

field1=value1&field2=value2

Как нетрудно догадаться, длина этого куска именно 27 символов, и представляет он собой ничто иное как пары "параметр=значение", о которых нам уже известно из GET-запроса.

Данные в виде параметров и значений кодируются и передаются точно так же, как в GET-запросе, только находятся они не в URL, а в теле запроса. Благодаря этому можно передавать данные любой длины.

HTML-формы

Возникает вопрос: а как собственно передавать данные в POST-запросе, если мы не можем вписать их в URL, а вручную реализовывать HTTP-протокол тоже непрактично?

Для передачи POST-запросов нужны либо специальные программы типа curl, либо HTML-формы. С ними вы сталкиваетесь постоянно: когда нужно зарегистрироваться, когда нужно отправить файл и т.п.

Форма представляет собой одно или несколько полей и кнопку "отправить".

-5

Давайте посмотрим, как составляется форма в HTML. Сделаем минимальную HTML-страницу с формой:

-6

Форма начинается тэгом <form> и заканчивается тэгом </form>. У тэга <form> есть атрибуты. Атрибут method указывает, какой тип запроса использовать – GET или POST. Мы используем POST. Атрибут action указывает, на какой сайт нужно слать запрос. Мы шлём на https://yandex.ru/search/.

Внутри формы содержатся элементы <input>. Это элементы, в которые можно что-то ввести. У них есть атрибут type, который отвечает за то, как выглядит и ведёт себя элемент. Это может быть: текстовое поле, выпадающий список, чекбокс, радиокнопка, загрузка файла. В нашем случае мы указали для первого <input> атрибут type="text". Это значит, что данный элемент – однострочное текстовое поле. У него также есть имя, которое задано атрибутом name="text". Это собственно имя параметра, которое будет отсылаться на сервер.

У второго элемента type="submit". Это значит, что он является кнопкой, при нажатии на которую форма будет отправлена.

Да, обратите внимание, что тэги <input> не требуют закрывающих тэгов </input>.

Вот так выглядит наша форма в браузере (можно открыть прямо с диска):

-7

Как отправляется форма? Когда нажата кнопка "отправить", браузер собирает все элементы, которые есть внутри формы, и склеивает из них пары "имя=значение", которые нам уже знакомы. У нас в форме есть элемент <input> с типом "text" и c именем "text" (совпадение – случайно), и мы ввели в это текстовое поле, например, "AAA". Браузер возьмёт имя элемента и содержимое элемента и сформирует такую пару для отправки:

text=AAA

Далее браузер просто формирует GET- или POST-запрос с этими данными и отправляет на сервер. Так как у нас указан "yandex.ru/search", можем посмотреть, что получится, если мы отправим туда POST-запрос:

-8

Вводим "AAA", нажимаем кнопку "Submit", и Яндекс получает наш запрос и перебрасывает нас на страницу с результатами поиска:

-9

Обратите внимание: в URL отсутствует параметр "text=AAA", то есть данные попали в Яндекс именно через POST-запрос. А "?lr=213" Яндекс дописал сам, я не знаю, зачем он это делает.

Подытожим.

В чём преимущество POST-запросов?

  • Не засоряют URL
  • Передают данные любой длины
  • Берегут личные данные
  • Не сохраняются в серверных логах

В чём недостаток?

  • Как ни странно, не засоряют URL, и потому не видно, что это был за запрос
  • Страницу, сформированную в результате POST-запроса, нельзя обновить, потому что данных, из которых она была сформирована, уже нет (в GET-запросе они присутствовали бы в URL). Нужно отправить POST-запрос ещё раз. Браузеры всегда предупреждают об этом.

Можно ли совмещать GET- и POST-запросы?

Да, можно. В нашем случае в <form> укажем такой action:

action="https://yandex.ru/search/?lr=213"

Раз уж Яндекс так любит свой lr=213, отправим ему его прямо в URL. Тогда часть данных будет получена через URL (что есть GET-запрос), а другая часть через тело запроса (что есть POST-запрос).

Что делает сервер?

Вы можете слать на веб-сервер сколько угодно GET- и POST-запросов. Он будет их получать и говорить вам, что всё окей. Но все они пойдут в мусорную корзину. Сервер их никак не обрабатывает. Чтобы эти запросы как-то работали, нужны обработчики. Об этом поговорим в следующей части.

Читайте дальше: