Русский

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

Почему + становится пробелом при декодировании строки запроса, а когда нет

· Как это работает

URL-кодировка javascript рабочий процесс разработчика

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

Означает ли + пробел, зависит от того, какой декодер вы вызываете. В этом посте объясняется, как decodeURIComponent, URLSearchParams и серверные платформы обрабатывают + и как избежать превращения настоящего плюса в пробел.

Почему + становится пробелом при декодировании строки запроса, а когда нет

При отправке формы HTML используется формат application/x-www-form-urlencoded, где пробел становится плюсом. Сервер, получающий name=Alice+Smith, заменяет каждый плюс пробелом перед извлечением значения. Когда реальный плюс принадлежит данным, например, в вычислении 5+3, он поступает на сервер как 5 3 после этапа декодирования формы. Это невидимое обращение является корнем путаницы.

Декодирование JavaScript дает разные результаты в зависимости от того, какую функцию вы используете. URLSearchParams рассматривает плюс как пробел, соответствующий поведению сервера. Но decodeURIComponent оставляет плюс нетронутым, рассматривая его буквально. Эта асимметрия между функциями является причиной того, что одни и те же входные данные декодируются по-разному. Разработчик, ожидающий, что оба декодера дадут одинаковый результат, обнаруживает, что это не так.

Две похожие кодировки — RFC 3986 процентная кодировка и прикладная /x-www-form-urlencoded

Два стандарта кодирования выглядят одинаково, но работают по-разному. RFC 3986 определяет процентное кодирование: любой символ становится %HH. Пространство становится %20. В стандарте application/x-www-form-urlencoded добавлено сокращение: пробел может быть плюсом. Любой из них работает в контексте формы, но плюс является необязательным и специфичным для этого стандарта. Это разные домены со схожим внешним видом.

Вызов decodeURIComponent применяет только декодирование RFC 3986. Он читается как %20 как пробел и плюс как буквальный плюс. URLSearchParams применяет правила декодирования формы: их символами становятся символы процента, а плюс становится пробелом. Эти две функции решают одну и ту же проблему в разных областях. Их смешивание приводит либо к исчезновению настоящего плюса, либо к тому, что пробел становится плюсом и не может быть преобразован.

decodeURIComponent оставляет + в покое; URLSearchParams превращает его в пробел — сравниваются два поведения JavaScript.

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

Пример: поле номера телефона содержит +1-555-0100 с плюсом в качестве кода страны. Форма HTML кодирует ее как %2B1-555-0100, поскольку JavaScript кодируется плюс как %2B. Сервер получает это. Если применяется декодирование формы, %2B становится плюсом и значение является правильным. Если прокси-сервер удаляет кодировку, вызов decodeURIComponent для результата дает +1-555-0100. Каждый слой декодируется один раз.

Что делают серверы — общее поведение платформы в строке запроса и теле запроса, описанное в общих чертах.

JavaScript может кодировать значения с помощью encodeURIComponent. Учитывая a+b, получается %2Bb. Когда эта закодированная строка достигает сервера или декодера с поддержкой формы, %2B декодируется как плюс, и результат оказывается правильным. Если вместо этого вы кодируете с использованием правила формы, пробел становится плюсом, а реальный плюс становится %2B. В любом случае кодирование создает %2Bb. Интерпретация зависит от того, какое правило декодирования применяется.

Проверьте обратный путь: начните с a+b. Закодируйте с помощью encodeURIComponent, чтобы получить %2Bb. Декодируйте %2Bb с помощью decodeURIComponent и восстановите a+b. Передайте a+b в URLSearchParams: он рассматривает плюс как пробел, создавая b. Передайте %2Bb в URLSearchParams, чтобы получить обратно a+b. Один и тот же входной сигнал, декодированный двумя способами, дает разные выходные данные в зависимости от того, какой декодер вы используете.

Рабочий пример: «a+b» и «a%2Bb» через оба декодера — четыре результата в таблице.

Распространенные ошибки следуют напрямую. Разработчик декодирует с помощью decodeURIComponent и задается вопросом, почему ломаются входящие данные формы с реальным плюсом. Им следовало использовать URLSearchParams. И наоборот, кто-то использует URLSearchParams вместо decodeURIComponent, и каждый буквальный плюс исчезает. Двойное кодирование дает %252B, поэтому для правильного декодирования требуются совпадающие пары кодер-декодер.

Другая ошибка — создание строки запроса вручную как ?q=value без кодирования. Любой амперсанд или равно в значении автоматически создает новый параметр. Браузер не угадывает конкатенацию; он рассматривает результат как правильно сформированный. Только намеренное кодирование с помощью encodeURIComponent предотвращает это. Кодер и декодер URL отображает все три функции, показывая, что каждая из них производит.

Распространенные ошибки — двойное декодирование или кодирование пробела как + в сегменте пути.

Правило кодирования формы называется application/x-www-form-urlencoded, поскольку оно описывает заголовок Content-Type тела запроса HTTP. Формы HTML без загрузки файлов отправляют тело в этом формате. Строки запроса в URL-адресах также используют это соглашение, хотя технически у них нет официального стандарта кодирования. Спецификации URL рассматривают запрос как непрозрачный; плюс значение не обязательно. Но в веб-приложениях плюс обычно означает пробел.

Чтобы гарантировать правильное поведение, сознательно кодируйте и декодируйте с помощью функции сопоставления. Если вы закодировали с помощью encodeURIComponent, декодируйте с помощью decodeURIComponent. При чтении данных формы HTML или тела запроса в формате формы используйте URLSearchParams. Никогда не гадайте по внешнему виду. Строка типа a+b неоднозначна. Декодеры не взаимозаменяемы.

Что здесь не распространяется — данные составной формы и тела запроса JSON.

Данные составных форм, тела запросов JSON и другие стандарты имеют отдельные правила кодирования. JSON не использует плюс для кодирования пробелов или процентов; он использует escape-символы Unicode. Multipart использует разные границы. В этой статье рассматриваются только строки запроса и тела, закодированные в форме, поскольку именно здесь появляется неоднозначность «плюс». Всегда проверяйте заголовок Content-Type и RFC, который его определяет.

Всегда кодируйте литерал плюс как %2B, если он принадлежит значению запроса. Кодер и декодер URL показывает, как плюс защищен как %2B в компонентном режиме, отдельно от пробелов, которые становятся %20. Пропустите a+b и a%2Bb через каждый режим, затем проверьте результаты. Это сравнение показывает, почему одни и те же входные данные декодируются по-разному. Разница заключается в правильном поведении двух разных стандартов.

Вывод: всегда кодируйте литерал плюс как %2B — как кодировщик и декодер URL показывают, как значение выглядит в виде значения запроса, закодированного в процентах.

Вывод: плюс в строке запроса — это сокращение формы кодировки пробела, а не буквальный плюс, если только он не получен из кодировки, защищающей его как %2B. Неправильный декодер теряет эту защиту. URLSearchParams наиболее безопасен в современном JavaScript; он обрабатывает кодировку формы и предоставляет доступ к именованным параметрам. Для необработанных строк encodeURIComponent защищает все; decodeURIComponent интерпретирует %20 и проценты, но воспринимает плюс буквально.

Проверьте это: создайте ?x=a+b вручную и вставьте в кодировщик и декодер URL. Осмотрите его и посмотрите, как URLSearchParams разделяет его на параметр x со значением a b. Вставьте ?x=a%2Bb и увидите значение a+b. Используйте encodeURIComponent для создания URL и сравнения. Это визуальное подтверждение проясняет правило: правила формы используют плюс, процентное кодирование использует %20, их смешивание — вот почему плюс исчезает в пространстве.