Предыдущие части: 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 и открываем соединение:
Мы видим чёрный экран. Это нормально. Это работает протокол HTTP. Мы соединились с сервером, и теперь сервер молча ждёт, что мы ему скажем.
Мы набираем руками вот такой запрос:
И дважды нажимаем Enter. После чего сервер отдаёт нам HTML-страницу сайта в текстовом виде и закрывает соединение:
Если бы мы были веб-браузером, мы взяли бы этот 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-формы. С ними вы сталкиваетесь постоянно: когда нужно зарегистрироваться, когда нужно отправить файл и т.п.
Форма представляет собой одно или несколько полей и кнопку "отправить".
Давайте посмотрим, как составляется форма в HTML. Сделаем минимальную HTML-страницу с формой:
Форма начинается тэгом <form> и заканчивается тэгом </form>. У тэга <form> есть атрибуты. Атрибут method указывает, какой тип запроса использовать – GET или POST. Мы используем POST. Атрибут action указывает, на какой сайт нужно слать запрос. Мы шлём на https://yandex.ru/search/.
Внутри формы содержатся элементы <input>. Это элементы, в которые можно что-то ввести. У них есть атрибут type, который отвечает за то, как выглядит и ведёт себя элемент. Это может быть: текстовое поле, выпадающий список, чекбокс, радиокнопка, загрузка файла. В нашем случае мы указали для первого <input> атрибут type="text". Это значит, что данный элемент – однострочное текстовое поле. У него также есть имя, которое задано атрибутом name="text". Это собственно имя параметра, которое будет отсылаться на сервер.
У второго элемента type="submit". Это значит, что он является кнопкой, при нажатии на которую форма будет отправлена.
Да, обратите внимание, что тэги <input> не требуют закрывающих тэгов </input>.
Вот так выглядит наша форма в браузере (можно открыть прямо с диска):
Как отправляется форма? Когда нажата кнопка "отправить", браузер собирает все элементы, которые есть внутри формы, и склеивает из них пары "имя=значение", которые нам уже знакомы. У нас в форме есть элемент <input> с типом "text" и c именем "text" (совпадение – случайно), и мы ввели в это текстовое поле, например, "AAA". Браузер возьмёт имя элемента и содержимое элемента и сформирует такую пару для отправки:
text=AAA
Далее браузер просто формирует GET- или POST-запрос с этими данными и отправляет на сервер. Так как у нас указан "yandex.ru/search", можем посмотреть, что получится, если мы отправим туда POST-запрос:
Вводим "AAA", нажимаем кнопку "Submit", и Яндекс получает наш запрос и перебрасывает нас на страницу с результатами поиска:
Обратите внимание: в 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-запросов. Он будет их получать и говорить вам, что всё окей. Но все они пойдут в мусорную корзину. Сервер их никак не обрабатывает. Чтобы эти запросы как-то работали, нужны обработчики. Об этом поговорим в следующей части.
Читайте дальше: