Объяснение кодирования URL-адресов: процентное кодирование, encodeURI против encodeURIComponent и ошибка %2520
Каждый разработчик, создавший URL-адрес с помощью пользовательского ввода, сталкивался с этим: поисковый запрос с пробелом или амперсандом разрывает ссылку, и внезапно половина запроса отсутствует на сервере. Исправление — это кодирование URL-адресов, также называемое процентным кодированием, но оно включает в себя две функции, которые легко спутать, и одну ошибку, которая приводит к непонятному %2520 в ваших ссылках. Это руководство проясняет все это.
Почему URL-адреса вообще нуждаются в кодировании
URL-адресам разрешено содержать только ограниченный набор символов, определенный стандартом RFC 3986. Этот набор в основном состоит из незарезервированных символов, букв, цифр и нескольких символов, таких как дефис и подчеркивание, а также нескольких зарезервированных символов, которые имеют особое структурное значение, например ? для запуска запроса & для разделения параметров = для сопряжения ключа со значением / для сегментов пути и # для фрагмента. Все, что находится за пределами этого набора, пробелы, буквы с диакритическими знаками, эмодзи или зарезервированный символ, используемый в качестве данных, а не структуры, должно быть закодировано, иначе URL-адрес будет нарушен.
Процентное кодирование делает это путем замены небезопасного символа знаком процента, за которым следует его двузначное шестнадцатеричное значение байта. Пробел становится %20. Амперсанд становится %26. Знак at становится %40. На другом конце сервер декодирует их обратно в исходные символы. Вся система существует для того, чтобы произвольный текст мог находиться внутри жесткой структуры URL-адреса, не будучи ошибочно принят за часть этой структуры.
Основная проблема: данные против структуры
Вот ситуация, которая вызывает большинство ошибок. Представьте себе поиск по буквальному тексту «кошки и собаки». Если вы вставите это прямо в строку запроса как q=cats & Dogs, & будет читаться как разделитель параметров, сервер увидит параметр q=cats и второй бессмысленный параметр Dogs, и ваш поиск будет нарушен. Пространство тоже вызывает проблемы. Амперсанд должен стать %26, а пробел — %20, чтобы сервер понимал, что они являются частью значения, а не структуры URL. Кодирование — это то, как вы говорите: «Этот символ — данные, а не разделитель».
encodeURI против encodeURIComponent
JavaScript предоставляет вам две функции кодирования, и выбор неправильной из них — одна из наиболее распространенных ошибок URL. Разница в том, что они считают «безопасным» и оставляют в покое.
encodeURIComponent кодирует все зарезервированное, включая / ? & = # и многое другое. Он предназначен для кодирования одного значения, которое вы вставляете в URL-адрес, значения параметра запроса, поискового запроса или цели перенаправления. Используйте его для кусочков. encodeURI, напротив, намеренно оставляет символы структуры URL-адреса (/ ? & =) в покое, поскольку он предназначен для кодирования всего, уже собранного URL-адреса, сохраняя при этом его функциональность. Используйте его для всего этого, редко.
Эмпирическое правило: если вы кодируете значение, которое находится внутри URL-адреса, используйте encodeURIComponent. Если вы кодируете полный URL-адрес и хотите исправить только случайные недопустимые символы, не нарушая его структуру, используйте encodeURI. В случае сомнений вам почти всегда потребуется encodeURIComponent, потому что вы почти всегда кодируете значение.
Ошибка %2520: двойное кодирование.
Это первое время всех сбивает с толку. Вы видите %2520 в URL-адресе, где вы ожидали %20, и ссылка не работает. Вот что произошло: уже закодированная строка закодировалась во второй раз. Первая кодировка превратила пробел в %20. Затем вторая кодировка кодировала сам знак процента: % становится %25, превращая %20 в %2520. Теперь сервер декодирует его один раз до %20 (буквально текст «%20») вместо пробела, и все, что происходит дальше, неверно.
Причиной почти всегда является кодирование значения, которое уже было закодировано, например, создание URL-адреса из строки, которая уже была закодирована в URL-адресе, а затем повторное ее прохождение через кодировщик. Правило, позволяющее избегать этого: кодируйте только необработанный, незакодированный текст. Никогда не кодируйте URL-адрес или значение, которые уже закодированы. Если вы не уверены, закодирована ли строка, сначала декодируйте ее, а затем закодируйте ровно один раз.
Неясность со знаком плюс
Еще одна ловушка. В строке запроса знак плюс (+) иногда интерпретируется как пробел, наследие старой кодировки формы HTML, application/x-www-form-urlencoded. Но в URL-пути + — это буквальный плюс. Это несоответствие означает, что + в ваших данных может быть прочитан как пробел в одном месте и плюс в другом. Безопасный ход — всегда кодировать пробел как %20, который везде однозначен, а не полагаться на +. Если вы видите, что пробелы превращаются в плюсы или наоборот, обычно виновата эта двусмысленность.
Практический вывод
Кодируйте значения, которые вы вводите в URL-адреса, используйте encodeURIComponent для этих значений, кодируйте только необработанный текст, чтобы избежать ошибки двойного кодирования %2520, и предпочитайте %20 вместо + для пробелов. Когда вам нужно создать или проверить URL-адрес вручную, быстрый кодировщик и декодер позволяют вам точно увидеть, во что превращается строка, и выявить эти проблемы до ее отправки.