Русский

Инструменты разработчика · Кодер и декодер URL

Плюс против %20: история применения/x-www-form-urlencoded

· Фон

URL-кодировка html-формы http-стандарты

Отправка формы, показывающая пространство кодировки знака плюс в строке запроса по сравнению с двадцати процентами в синтаксисе URI
Оригинальная векторная иллюстрация ToolAcre

В формах пробел кодируется как +, тогда как стандарт URI говорит %20, и причина историческая. В этом посте прослеживается соглашение от ранних форм HTML до сегодняшнего определения WHATWG и объясняется, почему оно так и не исчезло.

Плюс против %20: почему формы и URI по-разному кодируют пробелы

Формы HTML, представленные как GET, кодируют пробелы как знаки плюса в строке запроса. Это же пространство становится %20 в URL-адресах, следующих за RFC 3986. Оба верны, потому что они следуют разным стандартам. Поля формы, содержащие пробелы, в кодировке формы становятся именем=значение+с+пробелами, а %20 в RFC 3986. Знак отличия плюс двадцать процентов, какой стандарт применяется к вашим данным.

При кодировании формы используется плюс для пробелов в соответствии с историческим соглашением из исходного определения отправки формы RFC 1866 (1995), HTML 2.0. GET запрашивает закодированные пробелы как знаки плюса, резервируя плюс для литерала +, закодированного как %2B. Это правило применялось только к приложению /x-www-form-urlencoded,, а не к общему синтаксису URI. Миллиарды серверных инфраструктур стали зависеть от этого соглашения. Тестирование обоих показывает явные различия: в режиме формы выдается плюс; В режиме URI создается %20. Кодер и декодер URL предлагает оба режима для прямого сравнения.

Ранние формы HTML и отправка GET — как определялась кодировка формы и почему был выбран +

RFC 1866 (1995) определил отправку формы, где пробелы становятся плюсом, а буквальный плюс становится %2B. Это применимо только к приложению /x-www-form-urlencoded. RFC 3986, указанному %20 для общего синтаксиса URI. Два стандарта сознательно сосуществовали.

RFC 2396 уточняет наборы символов более строго, чем более ранние стандарты. Он формализовал зарезервированные символы, служащие структуре URI, а не незарезервированные как литеральные данные. Органы по стандартизации кодифицируют поведение браузеров и прокси-серверов по мере их развития. RFC 3986 появился позже без изменения поведения кодирования, просто с уточнением обозначений. Все современные браузеры стандартизируют кодировку UTF-8. HTML формирует через кнопки отправки, отправляет заявку в формате /x-www-form-urlencoded с плюсом вместо пробелов. Ручная конструкция URI использует %20. Понимание обоих стандартов позволяет избежать сюрпризов при интеграции.

RFC 1866 и более поздние спецификации HTML — где было записано правило и чем оно отличается от синтаксиса URI

WHATWG URL Стандарт определяет, что URLSearchParams.toString() выдает выходные данные приложения/x-www-form-urlencoded со знаками плюса вместо пробелов. Конструктор URL выполняет процентное кодирование после RFC 3986. Браузер переходит к URL с пробелом, кодируя %20; форма, представленная как GET, кодирует plus. Эти принципиально разные инструменты служат разным целям. Ручное кодированиеURIComponent дает %20 для пробелов — стиль RFC 3986. Формы, отправленные на тот же адрес URL, отправьте плюс. Серверы, обрабатывающие отправленные формы, ожидают плюса; получение %20 приводит к сбоям параметров без вывода сообщений.

Тестирование обоих выявляет предположения на стороне сервера, от которых вы зависите. JavaScript URLSearchParams обеспечивает безопасное скрытие кодировки в стиле формы и усложнение. Создайте URLSearchParams, добавьте записи, вызовите toString(), чтобы получить application/x-www-form-urlencoded с правильным плюсом. Альтернативно создайте строки запроса с помощью encodeURIComponent; вы получаете RFC 3986 %20. Никогда не смешивайте подходы. Строки запроса с ручным плюсом и encodeURIComponent создают неоднозначность. Получатели не могут отличить, означает ли плюс пробел или буквальный плюс. Стандартные подходы обрабатываются последовательно.

Стандарт URL сегодня — приложение/x-www-form-urlencoded как отдельный сериализатор со своими правилами

JSON API обычно отклоняют плюс как пробел, ожидая %20 для RFC 3986. Клиенты, отправляющие плюс, молча терпят неудачу: параметры пропадают. Тестирование API с обеими кодировками показывает, какой стандарт они принимают. URLSearchParams в JavaScript обрабатывает кодировку формы. Кодер и декодер URL создают RFC 3986 %20.

Отправка формы HTML автоматически обрабатывает кодировку. Ваша серверная структура решает, какие правила применяются. Rails, Django, PHP автоматически воспринимают плюс как пробел в полученных данных формы. Но построение строк запроса вручную для одних и тех же конечных точек имеет огромное значение. Загруженный плюс создает двусмысленность. Соответствие спецификациям и реальное поведение сервера немного расходятся. Задокументируйте, какой стандарт ожидают ваши конечные точки. Проверьте оба стиля кодирования. Защитный код изящно справляется с обеими задачами.

Рабочий пример: одно и то же поле формы отображается как строка запроса и как тело запроса — с + в одном месте и %20 в другом.

JavaScript URLSearchParams применяет кодировку формы: пробел становится плюсом, а не %20. new URLSearchParams({q: "hello world"}) выдает "q=hello+world", а не "q=hello%20world". Это историческое правило приложения /x-www-form-urlencoded, встроенное специально в JavaScript. Но передача этой строки в качестве необработанного запроса в новый URL сохраняет плюс как плюс; только URLSearchParams декодирует его как пробел. Конструктор верен тому, что видит. Разница в знаке плюс вызывает распространенные ошибки при неправильном смешивании функций.

Конструктор URL и encodeURIComponent — это разные инструменты. encodeURIComponent кодирует почти все, кроме незарезервированных букв, цифр и - _ . ! ~ * ' ( ). Он не предполагает никакого контекста. Конструктор URL анализирует фактический URL и применяет WHATWG правил для каждого компонента. encodeURIComponent превращает «hello/world» в «hello%2Fworld»; новый URL воспринимает косую черту как разделитель пути. Тот же вход, другой выход. Используйте encodeURIComponent при создании URL-адресов путем объединения частей. Используйте конструктор URLSearchParams или URL для полных или частичных URL-адресов.

Почему это невозможно исправить — десятилетия серверов и клиентов, которые зависят от текущего поведения

Правила процентного кодирования эволюционировали от RFC 1738 (1994) через RFC 2396 (1998) к RFC 3986 (2005). Каждое поколение проясняло неясности. RFC 1738 был консервативным, рассматривая символы небезопасно, поскольку ранняя сеть имела ограниченную поддержку символов. Развертывания стандартизированы на UTF-8, реализации стали согласованными. Более поздние стандарты ослабили ограничения на безопасность персонажей в разных системах. Современный консенсус: UTF-8 повсюду. Органы по стандартизации яростно поддерживают обратную совместимость. Исправление потребует всемирной координации, что невозможно спустя три десятилетия. Два стандарта сознательно сосуществуют.

Тестирование с плюсом и %20 выявляет предположения о сервере. Журналы сервера показывают, что отправляют клиенты. Формы используйте плюс; URL-адреса, созданные вручную, используют %20. Выбирайте по контексту и следуйте документации API.

Что здесь не распространяется — тела multipart/form-data и JSON.

Тестирование обеих кодировок позволяет выявить поведение сервера. Отправьте a+b в обе стороны. Многие производственные серверы ожидают кодирования формы; новые API ожидают %20. Ваш выбор зависит от ожиданий получателя. URLSearchParams обрабатывает кодировку формы; encodeURIComponent обрабатывает кодировку RFC.

Никогда не комбинируйте методы кодирования. Значение, закодированное с помощью encodeURIComponent %2B, а затем переданное в URLSearchParams, дважды кодируется как %252B. Один раз декодирование дает %2B вместо плюса. Символ становится буквальной строкой процентов два-шесть, а не знаком плюс. Проверьте промежуточные этапы процесса сборки. Кодирование происходит ровно один раз для каждого значения. Документ, какой стандарт кодирования использует ваш конвейер. Тестируйте со специальными символами, включая плюс, пробел, амперсанд.

Вывод: два стандарта, оба верны в своем контексте — как кодировщик и декодер URL дают вам форму RFC 3986 с %20 для пробелов, чтобы вы знали, на какой из них вы смотрите.

Разделение по принципу «плюс против двадцати» — это не ошибка, которую можно исправить. Это исторический артефакт стандартов, по-разному решающих отдельные проблемы. Исправление потребует всемирной координации, что невозможно спустя тридцать лет. Органы по стандартизации не разрывают сеть задним числом. RFC 3986, правила формы, конструкция браузера URL имеют стандарты и причины. Кодировка формы RFC 1866 и кодировка RFC 3986 URI служат разным уровням. Кодируйте сознательно, зная свой стандарт. Тестируйте реалистичные полезные нагрузки.

Выберите кодировку по контексту. Формы используют плюс в соответствии со стандартами HTML. В ручных URI используется %20 для RFC 3986. API указывают, чего ожидать; следуйте документации или протестируйте и то, и другое. Кодер и декодер URL отображают RFC 3986. Нужна кодировка формы? URLSearchParams делает это. Инструмент не смешивает кодировки; понимание стандартов предотвращает сюрпризы. Несогласованность кодирования между уровнями приводит к незначительной потере параметров, усечению и повреждению данных. Оба стандарта верны в своей области. Применяйте сознательно и документируйте.