diff --git a/README.md b/README.md index dbe8e7e..cda417c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Unicode, разрешение ссылок относительно базово - работа с Unicode, IDN и Punycode по UTS #46; - получение кодированного URI-представления; - работа с параметрами запроса с сохранением порядка и повторяющихся имён; -- кодирование параметров в формате `application/x-www-form-urlencoded`. +- процентное кодирование параметров по RFC 3986 и кодирование `application/x-www-form-urlencoded`. ## Требования @@ -68,7 +68,7 @@ opm install url Результат: ```text -https://example.com/users/42?q=hello+world&include=orders&include=contacts +https://example.com/users/42?q=hello%20world&include=orders&include=contacts ``` Параметры сохраняют порядок добавления и повторяющиеся имена. @@ -176,8 +176,8 @@ https://%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%80%D1%84/%D0%BF%D1%83%D1%82%D1% [`ДиагностикаОшибок`](https://github.com/Stivo182/oscript-errors/blob/main/docs/ДиагностикаОшибок.md). `url` работает только с текстовым представлением адресов: не выполняет DNS-запросы, HTTP-запросы и другие сетевые -операции. Библиотека не реализует браузерный алгоритм разбора WHATWG URL; из этого стандарта используется только -формат `application/x-www-form-urlencoded` для параметров запроса. +операции. Библиотека не реализует браузерный алгоритм разбора WHATWG URL. Для параметров запроса по умолчанию +используется профиль RFC 3986, а формат `application/x-www-form-urlencoded` выбирается явно. ## Документация @@ -186,6 +186,7 @@ https://%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%80%D1%84/%D0%BF%D1%83%D1%82%D1% - [РазборURL](docs/РазборURL.md) - разбор без передачи исключений - [ВидыХостаURL](docs/ВидыХостаURL.md) - классификация хоста - [ПараметрыЗапросаURL](docs/ПараметрыЗапросаURL.md) - коллекция параметров запроса +- [ФорматыПараметровЗапросаURL](docs/ФорматыПараметровЗапросаURL.md) - форматы параметров запроса - [КодированиеURL](docs/КодированиеURL.md) - кодирование компонентов - [КодыОшибокURL](docs/КодыОшибокURL.md) - машинные коды ошибок diff --git a/docs/README.md b/docs/README.md index d095769..7b6e4a9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,8 +3,9 @@ Публичный программный интерфейс пакета `url` предназначен для работы с абсолютными и относительными URI-ссылками, в том числе содержащими символы Unicode. Сетевой ввод-вывод не выполняется. Синтаксис основан на [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986.html) и -[RFC 3987](https://www.rfc-editor.org/rfc/rfc3987.html), а обработка параметров формы - на -[WHATWG URL §5](https://url.spec.whatwg.org/#application-x-www-form-urlencoded). +[RFC 3987](https://www.rfc-editor.org/rfc/rfc3987.html). Параметры запроса поддерживают профиль процентного +кодирования RFC 3986 и формат веб-формы из +[WHATWG URL, §5](https://url.spec.whatwg.org/#application-x-www-form-urlencoded). ## По задачам @@ -33,3 +34,4 @@ - [КодыОшибокURL](КодыОшибокURL.md) - машинные коды ошибок - [РазборURL](РазборURL.md) - разбор с результатом `URL` или `Неопределено` - [ВидыХостаURL](ВидыХостаURL.md) - значения классификации хоста +- [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md) - форматы разбора и сериализации параметров diff --git a/docs/URL.md b/docs/URL.md index 4b5177b..5c6633c 100644 --- a/docs/URL.md +++ b/docs/URL.md @@ -129,10 +129,10 @@ Конструктор и методы изменения проверяют общую грамматику URI, но не ограничения конкретной схемы. Например, `http:/path` синтаксически корректен, однако `СоответствуетСхеме()` вернёт `Ложь`, потому что у `http` нет хоста. -Конструктор проверяет форму процентных последовательностей, но декодирование UTF-8 выполняется при вызове -декодирующих методов. Поэтому успешно созданный объект всё ещё может вызвать `encoding.invalid` при обращении к -`Путь()`, `СтрокаЗапроса()`, `Фрагмент()` или другому декодированному представлению. Единой операции строгого -декодирования всех компонентов нет: проверяйте только те декодированные компоненты, которые использует приложение. +Конструктор проверяет форму процентных последовательностей в пути, данных пользователя, хосте и фрагменте. +В строке запроса допускается литеральный `%`. Подробнее это поведение описано в +[`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md#создание). Декодирование UTF-8 выполняется при вызове декодирующих +методов, поэтому они могут вызвать `encoding.invalid`. ## Создание @@ -959,13 +959,13 @@ ASCII, но не заменяет доменное имя на Punycode. ## Строка запроса URL `СтрокаЗапроса()` работает со всем запросом как с компонентом URI и оставляет `+` без изменений. -`ПараметрыЗапроса()` разбирает запрос как форму `application/x-www-form-urlencoded`, где `+` означает пробел. -Сохранённая коллекция параметров является живым представлением запроса, а не снимком. Она увидит последующие -изменения URL и изменения через другую связанную коллекцию при следующем вызове своего метода. +`ПараметрыЗапроса()` по умолчанию разбирает пары в формате +[`RFC3986()`](ФорматыПараметровЗапросаURL.md#rfc3986), где `+` является обычным символом. Для интерпретации +`application/x-www-form-urlencoded` вызовите `РазобратьКак(ФорматыПараметровЗапросаURL.ВебФорма())`. -Чтение параметров сохраняет исходную строку запроса. Любой изменяющий метод коллекции кодирует имена и заданные -значения заново по правилам формы, сохраняя параметры без значения без знака `=`. При этом, например, `%20` -станет `+`. +Повторные вызовы `ПараметрыЗапроса()` возвращают одну и ту же коллекцию. Ранее полученная ссылка на неё остаётся +актуальной после изменения строки запроса через методы URL. Чтение параметров сохраняет исходное написание, +а изменение пар влияет на последующее представление URL в соответствии с текущим форматом. ### СтрокаЗапроса @@ -999,9 +999,7 @@ ASCII, но не заменяет доменное имя на Punycode. ### ПараметрыЗапроса -Возвращает связанную с URL коллекцию параметров формы. Чтение коллекции не изменяет исходное написание запроса; -при изменении коллекции запрос сериализуется по алгоритму -[WHATWG `application/x-www-form-urlencoded`](https://url.spec.whatwg.org/#application-x-www-form-urlencoded). +Возвращает связанную с URL коллекцию параметров запроса. ```bsl .ПараметрыЗапроса() @@ -1009,9 +1007,7 @@ ASCII, но не заменяет доменное имя на Punycode. #### Возвращаемое значение -Новый объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md), связанный с текущим URL. - -Каждый вызов возвращает новый объект коллекции, но все такие объекты читают и изменяют запрос одного владельца. +Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md) на протяжении жизни URL. ### УстановитьСтрокуЗапроса @@ -1066,18 +1062,16 @@ ASCII, но не заменяет доменное имя на Punycode. | --- | --- | | `argument.invalid_type` | Передан неподдерживаемый тип | | `url.invalid_syntax` | Значение содержит недопустимый символ | -| `url.invalid_percent_encoding` | Нарушена форма `%HH` | #### Поведение -Пустая строка создаёт определённый пустой запрос `?`. `Неопределено` полностью удаляет запрос. +Пустая строка создаёт определённый пустой запрос `?`. `Неопределено` полностью удаляет запрос. Процент, не +образующий последовательность `%HH`, в кодированном представлении заменяется на `%25`. ### УстановитьПараметрыЗапроса -Копирует текущие параметры в запрос URL. Пустая коллекция удаляет компонент запроса. -Самостоятельная или связанная с другим URL коллекция после этого не влияет на целевой URL. Коллекция, -уже связанная с целевым URL, сохраняет связь, и её -следующее изменение снова изменит этот URL. +Копирует пары и текущий формат. Если в пустой коллекции строка запроса отсутствует, компонент запроса удаляется. +Переданная коллекция остаётся независимой, а ранее полученная ссылка на параметры целевого URL остаётся актуальной. ```bsl .УстановитьПараметрыЗапроса(<Значение>) @@ -1455,7 +1449,7 @@ ASCII, но не заменяет доменное имя на Punycode. // https://example.com/files/reports%2F2025.pdf ``` -Строка запроса и параметры формы: +Строка запроса и параметры веб-формы: ```bsl #Использовать url @@ -1465,6 +1459,9 @@ ASCII, но не заменяет доменное имя на Punycode. Сообщить(Адрес.СтрокаЗапроса()); // q=hello+world +Адрес.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() +); Сообщить(Адрес.ПараметрыЗапроса().Получить("q")); // hello world ``` diff --git "a/docs/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265URL.md" "b/docs/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265URL.md" index 45b8fbc..3f76554 100644 --- "a/docs/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265URL.md" +++ "b/docs/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265URL.md" @@ -54,8 +54,9 @@ ``` `КодироватьСтрокуЗапроса()` не разбирает пары параметров и не защищает `&` и `=` как данные. Для построения -параметров используйте [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md) либо кодируйте имя и значение отдельно -через `КодироватьПараметрФормы()`. +параметров используйте [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). Для совместимости с веб-формой можно +явно выбрать [`ВебФорма()`](ФорматыПараметровЗапросаURL.md#вебформа) или кодировать каждое имя и значение через +`КодироватьПараметрФормы()`. ## Функции diff --git "a/docs/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" "b/docs/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" index c53133a..cb107b5 100644 --- "a/docs/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" +++ "b/docs/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" @@ -1,364 +1,320 @@ # ПараметрыЗапросаURL -Упорядоченная коллекция параметров запроса. Строковые имена и значения кодируются по алгоритму -[WHATWG `application/x-www-form-urlencoded`](https://url.spec.whatwg.org/#application-x-www-form-urlencoded). -Параметр может не иметь значения и знака `=`. Регистр имён учитывается, дубликаты и порядок сохраняются. Коллекция -может быть самостоятельной или связанной с запросом [`URL`](URL.md). +Упорядоченная коллекция пар параметров запроса. Коллекция поддерживает два формата разбора и сериализации: + +- [`RFC3986()`](ФорматыПараметровЗапросаURL.md#rfc3986) используется по умолчанию, кодирует пробел как `%20` + и воспринимает `+` буквально; +- [`ВебФорма()`](ФорматыПараметровЗапросаURL.md#вебформа) следует + `application/x-www-form-urlencoded`, кодирует пробел как `+` и воспринимает `+` как пробел. + +[RFC 3986 §3.4](https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4) определяет компонент запроса как +неиерархические данные, но не задаёт модель пар `имя=значение`. Профиль `RFC3986()` использует для каждого имени +и значения незарезервированный набор из +[RFC 3986 §2.3](https://www.rfc-editor.org/rfc/rfc3986.html#section-2.3): +`ALPHA / DIGIT / "-" / "." / "_" / "~"`. ## Оглавление - [Создание](#создание) +- [Форматы](#форматы) - [Методы](#методы) - - [ВМассив](#вмассив) - - [ВСтроку](#встроку) - - [Добавить](#добавить) - - [Количество](#количество) - - [Очистить](#очистить) - - [Получить](#получить) - - [ПолучитьВсе](#получитьвсе) - - [Содержит](#содержит) - - [Сортировать](#сортировать) - - [Удалить](#удалить) - - [Установить](#установить) +- [Связь с URL](#связь-с-url) - [Примеры](#примеры) -- [Связанные сущности](#связанные-сущности) ## Создание ```bsl -Новый ПараметрыЗапросаURL(<СтрокаПараметров> = "", <ВладелецURL> = Неопределено) +Новый ПараметрыЗапросаURL( + <СтрокаПараметров> = Неопределено, + <Формат> = Неопределено +) ``` -**Параметры:** - -| Имя | Тип | Описание | +| Параметр | Тип | Описание | | --- | --- | --- | -| СтрокаПараметров | `Строка` | Параметры без начального `?`; по умолчанию пустая строка | -| ВладелецURL | [`URL`](URL.md), `Неопределено` | URL, запрос которого является источником состояния | +| СтрокаПараметров | `Строка`, `Неопределено` | Кодированная строка без `?` или отсутствие строки запроса | +| Формат | `Строка`, `Неопределено` | Значение из [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md). По умолчанию `RFC3986()` | -Конструктор возвращает новый [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md) либо вызывает исключение: +`Неопределено` и `""` различаются. Первое значение означает отсутствие строки запроса, второе означает явно +заданную пустую строку запроса. Метод [`ВСтроку()`](#встроку) для обоих состояний возвращает `""`, а метод +[`КодированнаяСтрока()`](#кодированнаястрока) сохраняет различие. -| Код | Условие | -| --- | --- | -| `argument.invalid_type` | Передан неподдерживаемый тип параметра | +Последовательность `%HH` определена в +[RFC 3986 §2.1](https://www.rfc-editor.org/rfc/rfc3986.html#section-2.1). Некорректный `%` принимается как +литеральный символ: исходная строка сохраняется, а в кодированном представлении `%` заменяется на `%25`. +Это не последовательность процентного кодирования по RFC 3986, а нестрогое поведение URL. Оно согласуется с +[моделью ошибок WHATWG URL](https://url.spec.whatwg.org/#validation-error), в которой ошибка проверки не обязана +прерывать разбор адреса. +При разборе пар некорректный UTF-8 заменяется символом `U+FFFD`. -При разборе `+` заменяется пробелом, некорректный `%` сохраняется, а некорректные последовательности UTF-8 -заменяются на `U+FFFD`. Части без `=` получают значение `Неопределено`, а пустые части между `&` пропускаются. +### Исключения -Если указан владелец, его текущий запрос является источником состояния. Переданная `СтрокаПараметров` не заменяет -запрос владельца. Обычный способ получить связанную коллекцию: - -```bsl -Параметры = Адрес.ПараметрыЗапроса(); -``` +| Код | Условие | +| --- | --- | +| `argument.invalid_type` | Строка параметров или формат имеет недопустимый тип | +| `argument.invalid_value` | Передан неизвестный формат | +| `url.invalid_syntax` | Строка содержит недопустимый символ компонента запроса | -Связанная коллекция является живым представлением, а не снимком. Перед каждым публичным вызовом она перечитывает -текущий запрос владельца. Поэтому сохранённая коллекция увидит прямое изменение URL и изменение через другую -связанную коллекцию. Изменяющий метод сразу записывает результат обратно в URL. +## Форматы -Чтение не меняет исходное написание запроса. Любое изменение сериализует весь запрос заново: пробелы становятся -`+`, отсутствие `=` сохраняется, а некорректные последовательности заменяются в соответствии с правилами разбора. +| Символ | `RFC3986()` | `ВебФорма()` | +| --- | --- | --- | +| пробел | `%20` | `+` | +| `+` | `%2B` | `%2B` | +| `~` | `~` | `%7E` | +| `*` | `%2A` | `*` | -Самостоятельную коллекцию можно скопировать в URL методом -[`URL.УстановитьПараметрыЗапроса()`](URL.md#установитьпараметрызапроса). +В формате `RFC3986()` пара без значения сохраняется как `flag`. В формате `ВебФорма()` каждая пара имеет +строковое значение, поэтому `flag` разбирается и сериализуется как `flag=`. ## Методы ### ВМассив -Возвращает глубокие независимые копии пар. - ```bsl .ВМассив() ``` -#### Возвращаемое значение - -Новый `Массив` структур с полями `Имя` и `Значение`. Значение имеет тип `Строка` или `Неопределено`. Изменение -массива и структур не влияет на коллекцию. +Возвращает новый `Массив` структур `Имя, Значение`. Изменение результата не влияет на коллекцию. ### ВСтроку -Кодирует строковые имена и значения по алгоритму `application/x-www-form-urlencoded`. Для параметра без значения -выводит только имя без знака `=`. - ```bsl -.ВСтроку() -``` - -#### Возвращаемое значение - -`Строка` без начального `?`. Пробел кодируется как `+`, а остальные байты UTF-8 - по правилам формы. - -#### Поведение - -Чтение связанной коллекции не изменяет исходное написание запроса владельца. - -### Добавить - -Добавляет новую пару в конец коллекции. - -```bsl -.Добавить(<Имя>, <Значение> = Неопределено) +.ВСтроку(<Формат> = Неопределено) ``` #### Параметры | Параметр | Тип | Описание | | --- | --- | --- | -| Имя | `Строка` | Имя параметра | -| Значение | `Произвольный`, кроме `Null` | Значение параметра; по умолчанию `Неопределено` | +| Формат | `Строка`, `Неопределено` | Значение из [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md). По умолчанию текущий формат | #### Возвращаемое значение -Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). - -#### Ошибки +Возвращает пары, сериализованные в текущем или явно указанном формате. Явный формат действует только на этот +вызов и не меняет текущий формат, пары, исходную кодированную строку или связанный URL. | Код | Условие | | --- | --- | -| `argument.invalid_type` | Имя не является строкой или значение равно `Null` | -| `argument.invalid_value` | Имя пустое, а значение равно `Неопределено` | +| `argument.invalid_type` | Формат имеет недопустимый тип | +| `argument.invalid_value` | Передан неизвестный формат | -#### Преобразование значения - -| Тип | Строковое представление | -| --- | --- | -| `Строка` | Без изменений | -| `Булево` | Результат `XMLСтрока()`: `true` или `false` | -| `Число` | Результат `XMLСтрока()`: без группировки разрядов, с точкой как знаком дробной части | -| `Дата` | Результат [`XMLСтрока()`](https://www.oscript.io/syntax/XmlGlobalFunctions), например `2026-09-06T12:34:56` | -| `Неопределено` | Параметр без значения и без знака `=`, например `flag` | -| Остальные типы | Результат `Строка()` | - -`Null` не преобразуется и вызывает исключение. Для удаления параметра предназначен метод [`Удалить()`](#удалить). -Параметр без значения должен иметь непустое имя, так как безымянный параметр без значения неотличим от пустой -строки запроса. - -[RFC 3986, раздел 3.4](https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4) допускает форму `flag` в компоненте -запроса, но не определяет модель пар и преобразование прикладных типов. - -Пустая строка и `Неопределено` различаются: +### КодированнаяСтрока ```bsl -Параметры.Добавить("flag"); // flag -Параметры.Добавить("empty", ""); // empty= +.КодированнаяСтрока() ``` -Тип `Дата` не хранит часовой пояс, поэтому полученная строка не содержит `Z` или числовое смещение. Такая строка -не является полной отметкой времени по [RFC 3339, раздел 5.6](https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6), -где смещение обязательно. +Возвращает актуальное ASCII-представление строки без `?` либо `Неопределено`, если компонент запроса отсутствует. -### Количество - -Возвращает количество всех пар, включая пары с повторяющимся именем. +### ИсходнаяСтрока ```bsl -.Количество() +.ИсходнаяСтрока() ``` -#### Возвращаемое значение +Возвращает исходное представление строки без `?` либо `Неопределено`, если компонент запроса отсутствует. До +изменения пар сохраняет исходное написание Unicode и `%HH`. После изменения пар возвращает строку, +сериализованную в текущем формате. -`Число`: количество пар. - -### Очистить - -Удаляет все пары. +### ТекущийФормат ```bsl -.Очистить() +.ТекущийФормат() ``` #### Возвращаемое значение -Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). У связанной коллекции компонент запроса -удаляется полностью. +`Строка`: текущее значение из +[ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md). -### Получить - -Возвращает значение первой пары с указанным именем. +### РазобратьКак ```bsl -.Получить(<Имя>) +.РазобратьКак(<Формат>) ``` #### Параметры | Параметр | Тип | Описание | | --- | --- | --- | -| Имя | `Строка` | Имя параметра | +| Формат | `Строка` | Значение из [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md) | #### Возвращаемое значение -`Строка` или `Неопределено`. Результат `Неопределено` означает, что пара не найдена либо параметр не имеет -значения. Эти случаи различаются методом [`Содержит()`](#содержит). +Тот же объект `ПараметрыЗапросаURL`. -#### Ошибки +#### Поведение + +Повторно разбирает текущую непустую кодированную строку в указанном формате. Строка сохраняется без изменений, +а смысл пар может измениться. Например, `q=a+b` означает `a+b` в `RFC3986()` и `a b` в `ВебФорма()`. +Если формат недопустим, коллекция остаётся без изменений. | Код | Условие | | --- | --- | -| `argument.invalid_type` | Имя не является строкой | - -### ПолучитьВсе +| `argument.invalid_type` | Формат имеет недопустимый тип | +| `argument.invalid_value` | Формат неизвестен, строка отсутствует или пуста | -Возвращает значения всех пар с указанным именем в порядке коллекции. +### УстановитьКодированнуюСтроку ```bsl -.ПолучитьВсе(<Имя>) +.УстановитьКодированнуюСтроку(<Значение>, <Формат> = Неопределено) ``` #### Параметры | Параметр | Тип | Описание | | --- | --- | --- | -| Имя | `Строка` | Имя параметра | +| Значение | `Строка`, `Неопределено` | Строка без `?` или признак удаления строки запроса | +| Формат | `Строка`, `Неопределено` | Значение из [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md). По умолчанию текущий формат | #### Возвращаемое значение -Новый `ФиксированныйМассив` строковых и неопределенных значений. Если пары отсутствуют, массив пуст. +Тот же объект `ПараметрыЗапросаURL`. + +#### Поведение -#### Ошибки +Заменяет строку и разбирает её в указанном формате. Если формат не передан, сохраняется текущий формат. +`Неопределено` удаляет строку запроса. Явный формат вместе с `Неопределено` выбирает формат будущих изменений, +не создавая пустую строку запроса. +Если строка или формат недопустимы, коллекция остаётся без изменений. Процент, +не образующий последовательность `%HH`, в кодированном представлении заменяется на `%25`. | Код | Условие | | --- | --- | -| `argument.invalid_type` | Имя не является строкой | +| `argument.invalid_type` | Значение или формат имеет недопустимый тип | +| `argument.invalid_value` | Передан неизвестный формат | +| `url.invalid_syntax` | Значение содержит недопустимый символ компонента запроса | -### Содержит - -Проверяет наличие хотя бы одной пары с точным именем. +### Добавить ```bsl -.Содержит(<Имя>) +.Добавить(<Имя>, <Значение> = Неопределено) ``` -#### Параметры - -| Параметр | Тип | Описание | -| --- | --- | --- | -| Имя | `Строка` | Имя параметра | - -#### Возвращаемое значение +Добавляет пару в конец и возвращает тот же объект. Строки сохраняются, булевы значения, числа и даты преобразуются +через `XMLСтрока()`, остальные значения через `Строка()`. `Null` вызывает `argument.invalid_type`. -`Булево`: `Истина`, если параметр существует. +В `RFC3986()` значение `Неопределено` создаёт параметр без `=`, а в `ВебФорма()` нормализуется в пустую строку. +Пустое имя без значения недопустимо только в `RFC3986()`. -#### Ошибки +### Установить -| Код | Условие | -| --- | --- | -| `argument.invalid_type` | Имя не является строкой | +```bsl +.Установить(<Имя>, <Значение> = Неопределено) +``` -### Сортировать +Заменяет первую пару с указанным именем, удаляет остальные пары с тем же именем или добавляет новую пару. +Правила значений совпадают с методом [`Добавить()`](#добавить). -Стабильно сортирует пары по последовательностям кодовых единиц UTF-16 в именах. +### Получить ```bsl -.Сортировать() +.Получить(<Имя>) ``` -#### Возвращаемое значение - -Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). Порядок пар с одинаковыми именами сохраняется. +Возвращает первое значение или `Неопределено`. Для различения отсутствующей пары и пары без значения используйте +[`Содержит()`](#содержит). -#### Поведение +### ПолучитьВсе -Сортировка связанной пустой коллекции полностью удаляет компонент запроса. +```bsl +.ПолучитьВсе(<Имя>) +``` -### Удалить +Возвращает новый `ФиксированныйМассив` всех значений с указанным именем в порядке коллекции. -Удаляет все пары с указанным именем. +### Содержит ```bsl -.Удалить(<Имя>) +.Содержит(<Имя>) ``` -#### Параметры +Возвращает `Истина`, если коллекция содержит хотя бы одну пару с точным именем. -| Параметр | Тип | Описание | -| --- | --- | --- | -| Имя | `Строка` | Имя параметра | +### Количество -#### Возвращаемое значение +```bsl +.Количество() +``` -Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). +Возвращает число пар, включая пары с повторяющимися именами. -#### Ошибки +### Удалить -| Код | Условие | -| --- | --- | -| `argument.invalid_type` | Имя не является строкой | +```bsl +.Удалить(<Имя>) +``` -#### Поведение +Удаляет все пары с указанным именем и возвращает тот же объект. -Связанная коллекция обновляет запрос владельца, даже если указанное имя отсутствует. +### Очистить -### Установить +```bsl +.Очистить() +``` + +Удаляет все пары и компонент запроса, затем возвращает тот же объект. -Заменяет значение первой пары и удаляет остальные пары с тем же именем. Если имя отсутствует, добавляет пару в -конец. +### Сортировать ```bsl -.Установить(<Имя>, <Значение> = Неопределено) +.Сортировать() ``` -#### Параметры +Стабильно сортирует пары по именам в порядке кодовых единиц UTF-16 и возвращает тот же объект. -| Параметр | Тип | Описание | -| --- | --- | --- | -| Имя | `Строка` | Имя параметра | -| Значение | `Произвольный`, кроме `Null` | Новое значение; по умолчанию `Неопределено` | +## Связь с URL -#### Возвращаемое значение +Повторные вызовы `URL.ПараметрыЗапроса()` возвращают одну и ту же коллекцию. Изменения коллекции становятся видны +в URL. Ранее полученная ссылка на коллекцию остаётся актуальной после изменения строки запроса через методы URL, +а её текущий формат сохраняется. -Тот же объект [`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). +`URL.УстановитьПараметрыЗапроса()` копирует пары и текущий формат. Переданная коллекция остаётся независимой. -#### Ошибки +Формат следует за источником строки запроса при операциях, создающих URL: -| Код | Условие | -| --- | --- | -| `argument.invalid_type` | Имя не является строкой или значение равно `Null` | -| `argument.invalid_value` | Имя пустое, а значение равно `Неопределено` | +- копирование и нормализация используют формат исходного URL; +- разрешение ссылки использует формат того URL, от которого результат наследует строку запроса; +- релятивизация использует формат целевого URL. -Значение преобразуется по тем же [правилам](#преобразование-значения), что и в методе `Добавить()`. +Формат не участвует в строковом равенстве или эквивалентности URL. ## Примеры -```bsl -#Использовать url - -Параметры = Новый ПараметрыЗапросаURL("a=1&a=2&q=hello+world"); +Формат по умолчанию: -Параметры - .Установить("a", 3) - .Добавить("include", "orders") - .Добавить("active", Истина) - .Добавить("isValid"); +```bsl +Параметры = Новый ПараметрыЗапросаURL; +Параметры.Установить("q", "hello world"); Сообщить(Параметры.ВСтроку()); -// a=3&q=hello+world&include=orders&active=true&isValid +// q=hello%20world ``` -Синхронизация связанных коллекций и пересериализация запроса: +Разбор строки запроса как веб-формы: ```bsl -#Использовать url - -Адрес = Новый URL("https://example.com/?a=hello%20world&flag"); -ПервыеПараметры = Адрес.ПараметрыЗапроса(); -ВторыеПараметры = Адрес.ПараметрыЗапроса(); +Адрес = Новый URL("https://example.com/?q=hello+world"); -ПервыеПараметры.Добавить("b", "2"); -Сообщить(ВторыеПараметры.Получить("b")); -// 2 +Адрес.ПараметрыЗапроса() + .РазобратьКак(ФорматыПараметровЗапросаURL.ВебФорма()) + .Установить("page", 2); Сообщить(Адрес.ВСтроку()); -// https://example.com/?a=hello+world&flag&b=2 +// https://example.com/?q=hello+world&page=2 +``` + +Однократная сериализация в другом формате: -Адрес.УстановитьКодированнуюСтрокуЗапроса("c=3"); -Сообщить(ПервыеПараметры.Получить("c")); -// 3 +```bsl +СтрокаФормы = Параметры.ВСтроку( + ФорматыПараметровЗапросаURL.ВебФорма() +); ``` ## Связанные сущности - [URL](URL.md) +- [ФорматыПараметровЗапросаURL](ФорматыПараметровЗапросаURL.md) - [КодированиеURL](КодированиеURL.md) diff --git "a/docs/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" "b/docs/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" new file mode 100644 index 0000000..2849d12 --- /dev/null +++ "b/docs/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.md" @@ -0,0 +1,31 @@ +# ФорматыПараметровЗапросаURL + +Модуль возвращает значения форматов, которые принимает +[`ПараметрыЗапросаURL`](ПараметрыЗапросаURL.md). + +## RFC3986 + +```bsl +ФорматыПараметровЗапросаURL.RFC3986() +``` + +Возвращает строку `RFC3986`. Это формат по умолчанию. Пробел кодируется как `%20`, а `+` воспринимается буквально. +При сериализации имён и значений без кодирования остаётся набор незарезервированных символов из +[RFC 3986 §2.3](https://www.rfc-editor.org/rfc/rfc3986.html#section-2.3): + +```text +ALPHA / DIGIT / "-" / "." / "_" / "~" +``` + +RFC 3986 не устанавливает формат пар `имя=значение`. Имя `RFC3986` обозначает профиль библиотеки, основанный на +правилах процентного кодирования RFC 3986. + +## ВебФорма + +```bsl +ФорматыПараметровЗапросаURL.ВебФорма() +``` + +Возвращает строку `FormUrlEncoded`. Формат следует алгоритму +[WHATWG `application/x-www-form-urlencoded`](https://url.spec.whatwg.org/#application-x-www-form-urlencoded): +пробел кодируется как `+`, исходный `+` как `%2B`, а каждая пара получает строковое значение. diff --git a/packagedef b/packagedef index 8a12a64..e6691d9 100644 --- a/packagedef +++ b/packagedef @@ -19,4 +19,5 @@ .ОпределяетМодуль("КодыОшибокURL", "src/Модули/КодыОшибокURL.os") .ОпределяетМодуль("КодированиеURL", "src/Модули/КодированиеURL.os") .ОпределяетМодуль("РазборURL", "src/Модули/РазборURL.os") - .ОпределяетМодуль("ВидыХостаURL", "src/Модули/ВидыХостаURL.os"); + .ОпределяетМодуль("ВидыХостаURL", "src/Модули/ВидыХостаURL.os") + .ОпределяетМодуль("ФорматыПараметровЗапросаURL", "src/Модули/ФорматыПараметровЗапросаURL.os"); diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URI.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URI.os" index f87ea64..cd68bd7 100644 --- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URI.os" +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\264\320\270\321\200\320\276\320\262\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URI.os" @@ -227,6 +227,65 @@ Функция ДекодироватьФорму(Значение) Экспорт + Возврат ДекодироватьНестрого(Значение, Истина); + +КонецФункции + +// Декодирует процентные последовательности с заменой некорректного UTF-8. +// Некорректные последовательности %HH сохраняются без изменения. +// +// Параметры: +// Значение - Строка - Кодированное значение. +// +// Возвращаемое значение: +// Строка - Декодированное значение. +Функция ДекодироватьПроценты(Значение) Экспорт + + Возврат ДекодироватьНестрого(Значение, Ложь); + +КонецФункции + +// Кодирует символы %, которые не начинают последовательность %HH. +// +// Параметры: +// Значение - Строка - Кодированное значение компонента URL. +// +// Возвращаемое значение: +// Строка - Значение, в котором литеральные символы % заменены на %25. +Функция КодироватьЛитеральныеПроценты(Значение) Экспорт + + Фрагменты = Новый Массив; + Индекс = 1; + + Пока Индекс <= СтрДлина(Значение) Цикл + Символ = Сред(Значение, Индекс, 1); + ПроцентЗакодирован = Символ = "%" + И Индекс + 2 <= СтрДлина(Значение) + И _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 1, 1)) + И _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 2, 1)); + + Если ПроцентЗакодирован Тогда + Фрагменты.Добавить(Сред(Значение, Индекс, 3)); + Индекс = Индекс + 3; + ИначеЕсли Символ = "%" Тогда + Фрагменты.Добавить("%25"); + Индекс = Индекс + 1; + Иначе + Фрагменты.Добавить(Символ); + Индекс = Индекс + 1; + КонецЕсли; + КонецЦикла; + + Возврат СтрСоединить(Фрагменты, ""); + +КонецФункции + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция ДекодироватьНестрого(Значение, ПлюсКакПробел) + ИсходныеДанные = ПолучитьДвоичныеДанныеИзСтроки(Значение, КодировкаТекста.UTF8, Ложь); ИсходнаяHexСтрока = ПолучитьHexСтрокуИзДвоичныхДанных(ИсходныеДанные); ФрагментыHexСтроки = Новый Массив; @@ -235,7 +294,7 @@ Пока Индекс <= СтрДлина(ИсходнаяHexСтрока) Цикл HexСимвола = Сред(ИсходнаяHexСтрока, Индекс, 2); - Если HexСимвола = "2B" Тогда + Если HexСимвола = "2B" И ПлюсКакПробел Тогда ФрагментыHexСтроки.Добавить("20"); Индекс = Индекс + 2; ИначеЕсли HexСимвола = "25" И Индекс + 4 <= СтрДлина(ИсходнаяHexСтрока) Тогда @@ -275,10 +334,6 @@ КонецФункции -#КонецОбласти - -#Область СлужебныеПроцедурыИФункции - Функция HexСтрокаASCIIСимвола(Символ) Код = КодСимвола(Символ); diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200URI.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200URI.os" index 1066934..3ea4376 100644 --- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200URI.os" +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\202\320\276\321\200URI.os" @@ -47,7 +47,7 @@ Путь = НормализоватьКомпонент(Компоненты.ПредставлениеПути); Путь = НормализоватьПуть(Путь, Схема, Компоненты.СервернаяЧастьОпределена); - Запрос = НормализоватьНеобязательныйКомпонент(Компоненты.ПредставлениеЗапроса); + Запрос = НормализоватьНеобязательныйЗапрос(Компоненты.ПредставлениеЗапроса); Фрагмент = НормализоватьНеобязательныйКомпонент(Компоненты.ПредставлениеФрагмента); ПортУказан = Компоненты.ПортУказан; ТекстПорта = Компоненты.ТекстПорта; @@ -181,6 +181,17 @@ КонецФункции +Функция НормализоватьНеобязательныйЗапрос(Запрос) + + Если Запрос = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + КодированныйЗапрос = _КодировщикКомпонентовURI.КодироватьЛитеральныеПроценты(Запрос); + Возврат НормализоватьКомпонент(КодированныйЗапрос); + +КонецФункции + Функция НормализоватьКомпонент(Компонент) Фрагменты = Новый Массив; diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200IRI.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200IRI.os" index 68eb87c..80ac92a 100644 --- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200IRI.os" +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\321\201\320\265\321\200IRI.os" @@ -3,6 +3,7 @@ Перем _ПроверяющийСинтаксисURI; // ПроверяющийСинтаксисURI Перем _КодировщикКомпонентаIRI; // КодировщикКомпонентаIRI Перем _КлассификаторХостаURI; // КлассификаторХостаURI +Перем _КодировщикКомпонентовURI; // КодировщикКомпонентовURI #Область ОбработчикиСобытий @@ -12,15 +13,18 @@ // ПроверяющийСинтаксисURI - ПроверяющийСинтаксисURI - Проверяющий синтаксис URI. // КодировщикКомпонентаIRI - КодировщикКомпонентаIRI - Кодировщик компонента IRI. // КлассификаторХостаURI - КлассификаторХостаURI - Классификатор хоста URI. +// КодировщикКомпонентовURI - КодировщикКомпонентовURI - Кодировщик компонентов URI. Процедура ПриСозданииОбъекта( ПроверяющийСинтаксисURI, КодировщикКомпонентаIRI, - КлассификаторХостаURI + КлассификаторХостаURI, + КодировщикКомпонентовURI ) _ПроверяющийСинтаксисURI = ПроверяющийСинтаксисURI; _КодировщикКомпонентаIRI = КодировщикКомпонентаIRI; _КлассификаторХостаURI = КлассификаторХостаURI; + _КодировщикКомпонентовURI = КодировщикКомпонентовURI; КонецПроцедуры @@ -53,6 +57,11 @@ ОтделитьСервернуюЧасть(Контекст); ПроверитьASCIIСимволыКомпонентов(Контекст); ПодготовитьКодированныеКомпоненты(Контекст, ЕстьСимволыUnicode); + Если Контекст.КодированныйЗапрос <> Неопределено Тогда + Контекст.КодированныйЗапрос = _КодировщикКомпонентовURI.КодироватьЛитеральныеПроценты( + Контекст.КодированныйЗапрос + ); + КонецЕсли; ПроверитьСтруктуруКомпонентов(Контекст); Возврат Контекст; @@ -329,19 +338,32 @@ ЕстьСимволыUnicode = Ложь; Индекс = 1; + НачалоЗапроса = СтрНайти(Значение, "?"); + НачалоФрагмента = СтрНайти(Значение, "#"); + ЕстьЗапрос = НачалоЗапроса > 0 + И (НачалоФрагмента = 0 Или НачалоЗапроса < НачалоФрагмента); Пока Индекс <= СтрДлина(Значение) Цикл Символ = Сред(Значение, Индекс, 1); ЕстьСимволыUnicode = ЕстьСимволыUnicode Или КодСимвола(Символ) > 127; Если Символ = "%" Тогда - Если Индекс + 2 > СтрДлина(Значение) - Или Не _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 1, 1)) - Или Не _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 2, 1)) Тогда + ПроцентЗакодирован = Индекс + 2 <= СтрДлина(Значение) + И _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 1, 1)) + И _ПроверяющийСинтаксисURI.ЭтоШестнадцатеричныйСимвол(Сред(Значение, Индекс + 2, 1)); + ПроцентВЗапросе = ЕстьЗапрос + И Индекс > НачалоЗапроса + И (НачалоФрагмента = 0 Или Индекс < НачалоФрагмента); + + Если Не ПроцентЗакодирован И Не ПроцентВЗапросе Тогда ВызватьНедопустимоеПроцентноеКодирование(Индекс); КонецЕсли; - Индекс = Индекс + 3; + Если ПроцентЗакодирован Тогда + Индекс = Индекс + 3; + Иначе + Индекс = Индекс + 1; + КонецЕсли; Иначе Индекс = Индекс + 1; КонецЕсли; diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\240\320\260\320\267\320\261\320\276\321\200\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URL.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\240\320\260\320\267\320\261\320\276\321\200\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URL.os" index b585d8e..36307c8 100644 --- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\240\320\260\320\267\320\261\320\276\321\200\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URL.os" +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\240\320\260\320\267\320\261\320\276\321\200\321\211\320\270\320\272\320\232\320\276\320\274\320\277\320\276\320\275\320\265\320\275\321\202\320\276\320\262URL.os" @@ -371,14 +371,16 @@ // // Исключения: // url.invalid_syntax - Запрос содержит недопустимый символ. -// url.invalid_percent_encoding - Процентное кодирование имеет недопустимый синтаксис. Функция ПроверитьКодированныйЗапрос(Значение) Экспорт - Возврат ПроверитьКодированныйКомпонент( - Значение, + КодированноеЗначение = _КодировщикКомпонентовURI.КодироватьЛитеральныеПроценты(Значение); + РезультатПроверки = ПроверитьКодированныйКомпонент( + КодированноеЗначение, _ПроверяющийСинтаксисURI.ДопустимыеСимволыЗапросаИФрагмента() ); + Возврат ПодготовленныйКомпонент(РезультатПроверки.КодированноеЗначение, Значение); + КонецФункции // Кодирует декодированный фрагмент. diff --git "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\262\320\270\321\201\321\213\320\221\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\270URL.os" "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\262\320\270\321\201\321\213\320\221\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\270URL.os" index ed1c6b9..0bcf3ec 100644 --- "a/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\262\320\270\321\201\321\213\320\221\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\270URL.os" +++ "b/src/internal/\320\234\320\276\320\264\321\203\320\273\320\270/\320\241\320\265\321\200\320\262\320\270\321\201\321\213\320\221\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\270URL.os" @@ -228,6 +228,7 @@ Параметры.Добавить(ПроверяющийСинтаксисURI()); Параметры.Добавить(КодировщикКомпонентаIRI()); Параметры.Добавить(КлассификаторХостаURI()); + Параметры.Добавить(КодировщикКомпонентовURI()); ИначеЕсли ИмяКласса = "ПреобразовательДоменов" Тогда Параметры.Добавить(ПреобразовательКодовыхТочекUnicode()); Параметры.Добавить(НормализаторСтрокUnicode()); diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/URL.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/URL.os" index f826bfc..18059e4 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/URL.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/URL.os" @@ -10,16 +10,14 @@ Перем _ПортУказан; // Булево Перем _ТекстПорта; // Строка, Неопределено Перем _КодированныйПуть; // Строка -Перем _ЗапросОпределен; // Булево -Перем _КодированныйЗапрос; // Строка, Неопределено Перем _ФрагментОпределен; // Булево Перем _КодированныйФрагмент; // Строка, Неопределено Перем _ПредставлениеДанныхПользователя; // Строка, Неопределено Перем _ПредставлениеХоста; // Строка, Неопределено Перем _ПредставлениеПути; // Строка -Перем _ПредставлениеЗапроса; // Строка, Неопределено Перем _ПредставлениеФрагмента; // Строка, Неопределено Перем _ОпределительСтандартногоПорта; // ОпределительСтандартногоПортаURI, Неопределено +Перем _ПараметрыЗапроса; // ПараметрыЗапросаURL #Область ОбработчикиСобытий @@ -52,6 +50,12 @@ КонецЕсли; УстановитьКомпоненты(СтрокаАдреса("Адрес", Адрес)); + Если Строка(ТипЗнч(Адрес)) = "URL" Тогда + _ПараметрыЗапроса.УстановитьКодированнуюСтроку( + Адрес.ПараметрыЗапроса().ИсходнаяСтрока(), + Адрес.ПараметрыЗапроса().ТекущийФормат() + ); + КонецЕсли; Возврат; КонецЕсли; @@ -63,8 +67,8 @@ ); КонецЕсли; - АдресURL = Новый URL(СтрокаАдреса("Адрес", Адрес)); - БазаURL = Новый URL(СтрокаАдреса("База", База)); + АдресURL = ПривестиКURL("Адрес", Адрес); + БазаURL = ПривестиКURL("База", База); Если БазаURL.Схема() = Неопределено Тогда ВызватьИсключение ФабрикаОшибок.Создать( @@ -76,6 +80,11 @@ Результат = СервисыБиблиотекиURL.РазрешительURI().Разрешить(БазаURL, АдресURL); ЗаполнитьКомпоненты(Результат.СнимокКомпонентов()); + ПеренестиФорматЗапроса( + ЭтотОбъект, + ИсточникЗапросаПриРазрешении(БазаURL, АдресURL) + ); + КонецПроцедуры #КонецОбласти @@ -445,13 +454,11 @@ // Строка, Неопределено - Кодированная строка запроса или Неопределено при её отсутствии. Функция КодированнаяСтрокаЗапроса() Экспорт - Возврат ?(_ЗапросОпределен, _КодированныйЗапрос, Неопределено); + Возврат _ПараметрыЗапроса.КодированнаяСтрока(); КонецФункции -// Возвращает связанную с URL коллекцию параметров запроса формы. -// Чтение коллекции сохраняет исходное написание запроса, а её изменение сериализует запрос -// по алгоритму application/x-www-form-urlencoded WHATWG URL §5. +// Возвращает хранимую в URL коллекцию параметров запроса. // // Ссылки: // RFC 3986, раздел 3.4 - компонент запроса: @@ -463,13 +470,7 @@ // ПараметрыЗапросаURL - Связанная с текущим URL коллекция параметров. Функция ПараметрыЗапроса() Экспорт - СтрокаПараметров = КодированнаяСтрокаЗапроса(); - - Если СтрокаПараметров = Неопределено Тогда - СтрокаПараметров = ""; - КонецЕсли; - - Возврат Новый ПараметрыЗапросаURL(СтрокаПараметров, ЭтотОбъект); + Возврат _ПараметрыЗапроса; КонецФункции @@ -489,13 +490,18 @@ "Значение", Значение, "ПараметрыЗапросаURL" ); - СтрокаПараметров = Значение.ВСтроку(); + КодированнаяСтрока = Значение.КодированнаяСтрока(); - Если СтрокаПараметров = "" Тогда - Возврат УдалитьЗапрос(); + Если КодированнаяСтрока <> Неопределено И КодированнаяСтрока <> "" Тогда + КодированнаяСтрока = Значение.ВСтроку(); КонецЕсли; - Возврат УстановитьКодированнуюСтрокуЗапроса(СтрокаПараметров); + _ПараметрыЗапроса.УстановитьКодированнуюСтроку( + КодированнаяСтрока, + Значение.ТекущийФормат() + ); + + Возврат ЭтотОбъект; КонецФункции @@ -908,7 +914,6 @@ // Исключения: // argument.invalid_type - Значение имеет недопустимый тип. // url.invalid_syntax - Значение содержит недопустимый символ. -// url.invalid_percent_encoding - Значение содержит недопустимую последовательность %HH. Функция УстановитьКодированнуюСтрокуЗапроса(Значение) Экспорт Если Значение = Неопределено Тогда @@ -1052,8 +1057,15 @@ // url.invalid_host - Хост строки ссылки имеет недопустимый синтаксис. Функция Разрешить(Ссылка) Экспорт - СсылкаURL = Новый URL(СтрокаАдреса("Ссылка", Ссылка)); - Возврат СервисыБиблиотекиURL.РазрешительURI().Разрешить(ЭтотОбъект, СсылкаURL); + СсылкаURL = ПривестиКURL("Ссылка", Ссылка); + Результат = СервисыБиблиотекиURL.РазрешительURI().Разрешить(ЭтотОбъект, СсылкаURL); + + ПеренестиФорматЗапроса( + Результат, + ИсточникЗапросаПриРазрешении(ЭтотОбъект, СсылкаURL) + ); + + Возврат Результат; КонецФункции @@ -1085,7 +1097,10 @@ ); КонецЕсли; - Возврат СервисыБиблиотекиURL.РелятивизаторURI().СделатьОтносительным(ЭтотОбъект, ЦельURL); + Результат = СервисыБиблиотекиURL.РелятивизаторURI().СделатьОтносительным(ЭтотОбъект, ЦельURL); + ПеренестиФорматЗапроса(Результат, ЦельURL); + + Возврат Результат; КонецФункции @@ -1099,7 +1114,10 @@ // URL - Новый независимый нормализованный URL. Функция Нормализованный() Экспорт - Возврат СервисыБиблиотекиURL.НормализаторURI().Нормализовать(ЭтотОбъект); + Результат = СервисыБиблиотекиURL.НормализаторURI().Нормализовать(ЭтотОбъект); + ПеренестиФорматЗапроса(Результат, ЭтотОбъект); + + Возврат Результат; КонецФункции @@ -1109,7 +1127,10 @@ // URL - Новый независимый нормализованный URL. Функция НормализованныйДляСхемы() Экспорт - Возврат СервисыБиблиотекиURL.НормализаторURI().НормализоватьДляСхемы(ЭтотОбъект); + Результат = СервисыБиблиотекиURL.НормализаторURI().НормализоватьДляСхемы(ЭтотОбъект); + ПеренестиФорматЗапроса(Результат, ЭтотОбъект); + + Возврат Результат; КонецФункции @@ -1119,7 +1140,13 @@ // URL - Новый URL с независимым состоянием. Функция Скопировать() Экспорт - Возврат Новый URL(ВСтроку()); + Результат = Новый URL(ВСтроку()); + Результат.ПараметрыЗапроса().УстановитьКодированнуюСтроку( + _ПараметрыЗапроса.ИсходнаяСтрока(), + _ПараметрыЗапроса.ТекущийФормат() + ); + + Возврат Результат; КонецФункции @@ -1244,6 +1271,9 @@ // ФиксированнаяСтруктура - Неизменяемый снимок компонентов URL. Функция СнимокКомпонентов() Экспорт + КодированныйЗапрос = _ПараметрыЗапроса.КодированнаяСтрока(); + ПредставлениеЗапроса = _ПараметрыЗапроса.ИсходнаяСтрока(); + Компоненты = Новый Структура; Компоненты.Вставить("Схема", _Схема); Компоненты.Вставить("СервернаяЧастьОпределена", _СервернаяЧастьОпределена); @@ -1254,14 +1284,14 @@ Компоненты.Вставить("ПортУказан", _ПортУказан); Компоненты.Вставить("ТекстПорта", _ТекстПорта); Компоненты.Вставить("КодированныйПуть", _КодированныйПуть); - Компоненты.Вставить("ЗапросОпределен", _ЗапросОпределен); - Компоненты.Вставить("КодированныйЗапрос", _КодированныйЗапрос); + Компоненты.Вставить("ЗапросОпределен", КодированныйЗапрос <> Неопределено); + Компоненты.Вставить("КодированныйЗапрос", КодированныйЗапрос); Компоненты.Вставить("ФрагментОпределен", _ФрагментОпределен); Компоненты.Вставить("КодированныйФрагмент", _КодированныйФрагмент); Компоненты.Вставить("ПредставлениеДанныхПользователя", _ПредставлениеДанныхПользователя); Компоненты.Вставить("ПредставлениеХоста", _ПредставлениеХоста); Компоненты.Вставить("ПредставлениеПути", _ПредставлениеПути); - Компоненты.Вставить("ПредставлениеЗапроса", _ПредставлениеЗапроса); + Компоненты.Вставить("ПредставлениеЗапроса", ПредставлениеЗапроса); Компоненты.Вставить("ПредставлениеФрагмента", _ПредставлениеФрагмента); Возврат Новый ФиксированнаяСтруктура(Компоненты); @@ -1327,16 +1357,19 @@ _ПортУказан = Компоненты.ПортУказан; _ТекстПорта = Компоненты.ТекстПорта; _КодированныйПуть = Компоненты.КодированныйПуть; - _ЗапросОпределен = Компоненты.ЗапросОпределен; - _КодированныйЗапрос = Компоненты.КодированныйЗапрос; _ФрагментОпределен = Компоненты.ФрагментОпределен; _КодированныйФрагмент = Компоненты.КодированныйФрагмент; _ПредставлениеДанныхПользователя = Компоненты.ПредставлениеДанныхПользователя; _ПредставлениеХоста = Компоненты.ПредставлениеХоста; _ПредставлениеПути = Компоненты.ПредставлениеПути; - _ПредставлениеЗапроса = Компоненты.ПредставлениеЗапроса; _ПредставлениеФрагмента = Компоненты.ПредставлениеФрагмента; + Если _ПараметрыЗапроса = Неопределено Тогда + _ПараметрыЗапроса = Новый ПараметрыЗапросаURL; + КонецЕсли; + + _ПараметрыЗапроса.УстановитьКодированнуюСтроку(Компоненты.ПредставлениеЗапроса); + КонецПроцедуры Процедура ПроверитьИнварианты(ТребоватьАбсолютный = Ложь) @@ -1358,11 +1391,13 @@ Или Не ЭтоБулево(_СервернаяЧастьОпределена) Или Не ЭтоБулево(_ДанныеПользователяОпределены) Или Не ЭтоБулево(_ПортУказан) - Или Не ЭтоБулево(_ЗапросОпределен) + Или _ПараметрыЗапроса = Неопределено Или Не ЭтоБулево(_ФрагментОпределен) Тогда ВызватьНедопустимоеСостояние(); КонецЕсли; + ЗапросОпределен = _ПараметрыЗапроса.КодированнаяСтрока() <> Неопределено; + ПроверитьСогласованностьОпциональногоКомпонента(_Схема, _Схема <> Неопределено); ПроверитьСогласованностьОпциональногоКомпонента( _КодированныеДанныеПользователя, @@ -1373,8 +1408,7 @@ _ДанныеПользователяОпределены ); ПроверитьСогласованностьОпциональногоКомпонента(_ТекстПорта, _ПортУказан); - ПроверитьСогласованностьОпциональногоКомпонента(_КодированныйЗапрос, _ЗапросОпределен); - ПроверитьСогласованностьОпциональногоКомпонента(_ПредставлениеЗапроса, _ЗапросОпределен); + ПроверитьСогласованностьОпциональногоКомпонента(_ПараметрыЗапроса.ИсходнаяСтрока(), ЗапросОпределен); ПроверитьСогласованностьОпциональногоКомпонента(_КодированныйФрагмент, _ФрагментОпределен); ПроверитьСогласованностьОпциональногоКомпонента(_ПредставлениеФрагмента, _ФрагментОпределен); @@ -1505,9 +1539,15 @@ Фрагменты.Добавить(?(Исходное, _ПредставлениеПути, _КодированныйПуть)); - Если _ЗапросОпределен Тогда + Запрос = ?( + Исходное, + _ПараметрыЗапроса.ИсходнаяСтрока(), + _ПараметрыЗапроса.КодированнаяСтрока() + ); + + Если Запрос <> Неопределено Тогда Фрагменты.Добавить("?"); - Фрагменты.Добавить(?(Исходное, _ПредставлениеЗапроса, _КодированныйЗапрос)); + Фрагменты.Добавить(Запрос); КонецЕсли; Если _ФрагментОпределен Тогда @@ -1704,6 +1744,28 @@ КонецФункции +Функция ИсточникЗапросаПриРазрешении(База, Ссылка) + + Если Ссылка.Схема() <> Неопределено + Или Ссылка.КодированнаяСервернаяЧасть() <> Неопределено + Или Ссылка.КодированныйПуть() <> "" + Или Ссылка.КодированнаяСтрокаЗапроса() <> Неопределено Тогда + Возврат Ссылка; + КонецЕсли; + + Возврат База; + +КонецФункции + +Процедура ПеренестиФорматЗапроса(Результат, Источник) + + Результат.ПараметрыЗапроса().УстановитьКодированнуюСтроку( + Результат.ПараметрыЗапроса().ИсходнаяСтрока(), + Источник.ПараметрыЗапроса().ТекущийФормат() + ); + +КонецПроцедуры + Функция СкопироватьКомпоненты() Возврат Новый Структура(СнимокКомпонентов()); diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" index 73206b7..fba0292 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -1,32 +1,37 @@ #Использовать errors +#Использовать "../internal" Перем _Пары; // ТаблицаЗначений Перем _СледующийПорядок; // Число -Перем _ВладелецURL; // URL, Неопределено +Перем _КодированнаяСтрокаПараметров; // Строка, Неопределено +Перем _ПредставлениеСтрокиПараметров; // Строка, Неопределено +Перем _Формат; // Строка +Перем _СтрокиАктуальны; // Булево #Область ОбработчикиСобытий -// Создаёт самостоятельную или связанную с URL коллекцию параметров формы. +// Создаёт коллекцию параметров URL. // // Параметры: -// СтрокаПараметров - Строка - Параметры без начального символа ?. -// ВладелецURL - URL, Неопределено - URL, состояние запроса которого является источником истины. +// СтрокаПараметров - Строка, Неопределено - Кодированная строка без ? или её отсутствие. +// Формат - Строка, Неопределено - Формат параметров или RFC3986 при Неопределено. // // Исключения: // argument.invalid_type - Аргумент имеет недопустимый тип. -Процедура ПриСозданииОбъекта(СтрокаПараметров = "", ВладелецURL = Неопределено) - - ПроверитьСтроковыйАргумент("СтрокаПараметров", СтрокаПараметров); - - Если ВладелецURL <> Неопределено Тогда - ПроверкаАргументов.ПроверитьТип("ВладелецURL", ВладелецURL, "URL"); - КонецЕсли; +// argument.invalid_value - Формат не поддерживается. +// url.invalid_syntax - СтрокаПараметров содержит недопустимый символ. +Процедура ПриСозданииОбъекта(СтрокаПараметров = Неопределено, Формат = Неопределено) _Пары = НоваяТаблицаПар(); _СледующийПорядок = 0; - _ВладелецURL = ВладелецURL; + _Формат = НормализоватьФормат(Формат); + _КодированнаяСтрокаПараметров = Неопределено; + _ПредставлениеСтрокиПараметров = Неопределено; + _СтрокиАктуальны = Истина; - РазобратьСтрокуПараметров(СтрокаПараметров); + Если СтрокаПараметров <> Неопределено Тогда + ИнициализироватьИзСтрокиПараметров(СтрокаПараметров); + КонецЕсли; КонецПроцедуры @@ -40,8 +45,6 @@ // Число - Количество пар параметров. Функция Количество() Экспорт - СинхронизироватьСВладельцемURL(); - Возврат _Пары.Количество(); КонецФункции @@ -58,8 +61,6 @@ // argument.invalid_type - Имя имеет недопустимый тип. Функция Содержит(Имя) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); Возврат НайтиПары(Имя).Количество() > 0; @@ -78,8 +79,6 @@ // argument.invalid_type - Имя имеет недопустимый тип. Функция Получить(Имя) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); Пары = НайтиПары(Имя); @@ -104,8 +103,6 @@ // argument.invalid_type - Имя имеет недопустимый тип. Функция ПолучитьВсе(Имя) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); Значения = Новый Массив; @@ -123,7 +120,8 @@ // // Параметры: // Имя - Строка - Имя параметра. -// Значение - Произвольный, Неопределено - Значение параметра. Неопределено добавляет параметр без значения. +// Значение - Произвольный, Неопределено - Значение параметра. В RFC3986 Неопределено добавляет параметр без +// значения, в ВебФорма нормализуется в пустую строку. // Строки сохраняются без изменений, булевы значения, числа и даты преобразуются в независимое от локали // строковое представление, остальные значения преобразуются функцией Строка(). // @@ -135,15 +133,14 @@ // argument.invalid_value - Имя пустое для параметра без значения. Функция Добавить(Имя, Значение = Неопределено) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); - ПроверитьДопустимостьПараметраБезЗначения(Имя, Значение); Значение = СтроковоеПредставлениеЗначения(Значение); + Значение = НормализоватьЗначениеДляФормата(Значение, _Формат); + ПроверитьДопустимостьПараметраБезЗначения(Имя, Значение); ДобавитьПаруВКонец(Имя, Значение); - ОбновитьВладельцаURL(); + СделатьСтрокиНеактуальными(); Возврат ЭтотОбъект; @@ -153,9 +150,10 @@ // // Параметры: // Имя - Строка - Имя параметра. -// Значение - Произвольный, Неопределено - Новое значение параметра. Неопределено устанавливает параметр без -// значения. Строки сохраняются без изменений, булевы значения, числа и даты преобразуются в независимое -// от локали строковое представление, остальные значения преобразуются функцией Строка(). +// Значение - Произвольный, Неопределено - Новое значение параметра. В RFC3986 Неопределено устанавливает +// параметр без значения, в ВебФорма нормализуется в пустую строку. Строки сохраняются без изменений, +// булевы значения, числа и даты преобразуются в независимое от локали строковое представление, остальные +// значения преобразуются функцией Строка(). // // Возвращаемое значение: // ПараметрыЗапросаURL - Этот объект. @@ -165,12 +163,11 @@ // argument.invalid_value - Имя пустое для параметра без значения. Функция Установить(Имя, Значение = Неопределено) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); - ПроверитьДопустимостьПараметраБезЗначения(Имя, Значение); Значение = СтроковоеПредставлениеЗначения(Значение); + Значение = НормализоватьЗначениеДляФормата(Значение, _Формат); + ПроверитьДопустимостьПараметраБезЗначения(Имя, Значение); Пары = НайтиПары(Имя); @@ -187,7 +184,7 @@ КонецЦикла; КонецЕсли; - ОбновитьВладельцаURL(); + СделатьСтрокиНеактуальными(); Возврат ЭтотОбъект; @@ -205,15 +202,13 @@ // argument.invalid_type - Имя имеет недопустимый тип. Функция Удалить(Имя) Экспорт - СинхронизироватьСВладельцемURL(); - ПроверитьСтроковыйАргумент("Имя", Имя); Для Каждого Пара Из НайтиПары(Имя) Цикл _Пары.Удалить(Пара); КонецЦикла; - ОбновитьВладельцаURL(); + СделатьСтрокиНеактуальными(); Возврат ЭтотОбъект; @@ -225,12 +220,11 @@ // ПараметрыЗапросаURL - Этот объект. Функция Очистить() Экспорт - СинхронизироватьСВладельцемURL(); - _Пары = НоваяТаблицаПар(); _СледующийПорядок = 0; - - ОбновитьВладельцаURL(); + _КодированнаяСтрокаПараметров = Неопределено; + _ПредставлениеСтрокиПараметров = Неопределено; + _СтрокиАктуальны = Истина; Возврат ЭтотОбъект; @@ -242,8 +236,6 @@ // ПараметрыЗапросаURL - Этот объект. Функция Сортировать() Экспорт - СинхронизироватьСВладельцемURL(); - ОтсортированныеПары = ОтсортироватьПарыПоИмени(); _Пары = НоваяТаблицаПар(); @@ -253,7 +245,7 @@ ДобавитьПаруВКонец(Пара.Имя, Пара.Значение); КонецЦикла; - ОбновитьВладельцаURL(); + СделатьСтрокиНеактуальными(); Возврат ЭтотОбъект; @@ -265,8 +257,6 @@ // Массив - Новый массив структур Имя и Значение. Значение имеет тип Строка или Неопределено. Функция ВМассив() Экспорт - СинхронизироватьСВладельцемURL(); - Результат = Новый Массив; Для Каждого Пара Из _Пары Цикл @@ -277,16 +267,121 @@ КонецФункции -// Кодирует имена и строковые значения по алгоритму application/x-www-form-urlencoded WHATWG URL §5. -// Параметры без значения сериализуются без знака =. +// Заменяет кодированную строку и разбирает её в указанном или текущем формате. +// +// Параметры: +// Значение - Строка, Неопределено - Строка без ? или признак удаления компонента запроса. +// Формат - Строка, Неопределено - Новый формат или текущий при Неопределено. +// +// Возвращаемое значение: +// ПараметрыЗапросаURL - Этот объект. +// +// Исключения: +// argument.invalid_type - Значение или Формат имеет недопустимый тип. +// argument.invalid_value - Формат не поддерживается. +// url.invalid_syntax - Значение содержит недопустимый символ. +Функция УстановитьКодированнуюСтроку(Значение, Формат = Неопределено) Экспорт + + Если Значение <> Неопределено Тогда + ПроверитьСтроковыйАргумент("Значение", Значение); + КонецЕсли; + + НовыйФормат = ?(Формат = Неопределено, _Формат, НормализоватьФормат(Формат)); + Кандидат = Новый ПараметрыЗапросаURL(Значение, НовыйФормат); + + ЗаменитьПары(Кандидат.ВМассив()); + + _Формат = НовыйФормат; + _КодированнаяСтрокаПараметров = Кандидат.КодированнаяСтрока(); + _ПредставлениеСтрокиПараметров = Кандидат.ИсходнаяСтрока(); + _СтрокиАктуальны = Истина; + + Возврат ЭтотОбъект; + +КонецФункции + +// Повторно разбирает текущую кодированную строку в указанном формате. +// +// Параметры: +// Формат - Строка - Формат разбора параметров. +// +// Возвращаемое значение: +// ПараметрыЗапросаURL - Этот объект. +// +// Исключения: +// argument.invalid_type - Формат имеет недопустимый тип. +// argument.invalid_value - Формат не поддерживается или кодированная строка пуста. +Функция РазобратьКак(Формат) Экспорт + + ПроверитьСтроковыйАргумент("Формат", Формат); + + Формат = НормализоватьФормат(Формат); + + ОбеспечитьСтроковыеПредставления(); + ПроверкаАргументов.ПроверитьДопустимость( + "КодированнаяСтрока", + _КодированнаяСтрокаПараметров <> Неопределено И _КодированнаяСтрокаПараметров <> "", + "непустая кодированная строка" + ); + + Кандидат = Новый ПараметрыЗапросаURL(_КодированнаяСтрокаПараметров, Формат); + ЗаменитьПары(Кандидат.ВМассив()); + _Формат = Формат; + + Возврат ЭтотОбъект; + +КонецФункции + +// Возвращает текущий формат разбора и сериализации параметров. +// +// Возвращаемое значение: +// Строка - Одно из значений ФорматыПараметровЗапросаURL. +Функция ТекущийФормат() Экспорт + + Возврат _Формат; + +КонецФункции + +// Возвращает актуальную кодированную строку параметров. +// +// Возвращаемое значение: +// Строка, Неопределено - Строка без начального ? или отсутствие компонента запроса. +Функция КодированнаяСтрока() Экспорт + + ОбеспечитьСтроковыеПредставления(); + + Возврат _КодированнаяСтрокаПараметров; + +КонецФункции + +// Возвращает исходное представление строки параметров. +// +// Возвращаемое значение: +// Строка, Неопределено - Строка без ? или отсутствие компонента запроса. +Функция ИсходнаяСтрока() Экспорт + + ОбеспечитьСтроковыеПредставления(); + + Возврат _ПредставлениеСтрокиПараметров; + +КонецФункции + +// Сериализует пары в текущем или указанном формате. +// +// Параметры: +// Формат - Строка, Неопределено - Целевой формат или текущий при Неопределено. // // Возвращаемое значение: // Строка - Строка параметров без начального символа ?. -Функция ВСтроку() Экспорт +// +// Исключения: +// argument.invalid_type - Формат имеет недопустимый тип. +// argument.invalid_value - Формат не поддерживается. +Функция ВСтроку(Формат = Неопределено) Экспорт - СинхронизироватьСВладельцемURL(); + Формат = ?(Формат = Неопределено, _Формат, НормализоватьФормат(Формат)); - Возврат СериализоватьПары(); + Возврат СериализоватьПары(Формат); КонецФункции @@ -294,18 +389,40 @@ #Область СлужебныеПроцедурыИФункции -Функция СериализоватьПары() +Процедура ИнициализироватьИзСтрокиПараметров(СтрокаПараметров) + + ПроверитьСтроковыйАргумент("СтрокаПараметров", СтрокаПараметров); + + Компоненты = СервисыБиблиотекиURL.ПарсерIRI().Разобрать("?" + СтрокаПараметров); + + Если Компоненты.ФрагментОпределен Тогда + ВызватьНедопустимыйСинтаксисСтрокиПараметров(); + КонецЕсли; + + СервисыБиблиотекиURL.РазборщикКомпонентовURL() + .ПроверитьКодированныйЗапрос(Компоненты.КодированныйЗапрос); + + _КодированнаяСтрокаПараметров = Компоненты.КодированныйЗапрос; + _ПредставлениеСтрокиПараметров = Компоненты.ПредставлениеЗапроса; + + РазобратьСтрокуПараметров(_КодированнаяСтрокаПараметров, _Формат); + +КонецПроцедуры + +Функция СериализоватьПары(Формат) Фрагменты = Новый Массив; Для Каждого Пара Из _Пары Цикл - КодированноеИмя = КодированиеURL.КодироватьПараметрФормы(Пара.Имя); + КодированноеИмя = КодироватьПараметр(Пара.Имя, Формат); - Если Пара.Значение = Неопределено Тогда + Если Пара.Значение = Неопределено + И Формат = ФорматыПараметровЗапросаURL.RFC3986() Тогда Фрагменты.Добавить(КодированноеИмя); Иначе + Значение = ?(Пара.Значение = Неопределено, "", Пара.Значение); Фрагменты.Добавить( - КодированноеИмя + "=" + КодированиеURL.КодироватьПараметрФормы(Пара.Значение) + КодированноеИмя + "=" + КодироватьПараметр(Значение, Формат) ); КонецЕсли; КонецЦикла; @@ -314,39 +431,51 @@ КонецФункции -Процедура СинхронизироватьСВладельцемURL() +Функция КодироватьПараметр(Значение, Формат) - Если _ВладелецURL = Неопределено Тогда - Возврат; + Если Формат = ФорматыПараметровЗапросаURL.ВебФорма() Тогда + Возврат КодированиеURL.КодироватьПараметрФормы(Значение); КонецЕсли; - СтрокаПараметров = _ВладелецURL.КодированнаяСтрокаЗапроса(); + Возврат СервисыБиблиотекиURL.КодировщикКомпонентовURI().Кодировать( + Значение, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" + ); - Если СтрокаПараметров = Неопределено Тогда - СтрокаПараметров = ""; - КонецЕсли; +КонецФункции - _Пары = НоваяТаблицаПар(); - _СледующийПорядок = 0; +Процедура СделатьСтрокиНеактуальными() + + _СтрокиАктуальны = Ложь; - РазобратьСтрокуПараметров(СтрокаПараметров); +КонецПроцедуры + +Процедура ВызватьНедопустимыйСинтаксисСтрокиПараметров() + + ВызватьИсключение ФабрикаОшибок.Создать( + КодыОшибокURL.НедопустимыйСинтаксис(), + "Строка параметров содержит разделитель фрагмента" + ); КонецПроцедуры -Процедура ОбновитьВладельцаURL() +Процедура ОбеспечитьСтроковыеПредставления() - Если _ВладелецURL = Неопределено Тогда + Если _СтрокиАктуальны Тогда Возврат; КонецЕсли; - СтрокаПараметров = СериализоватьПары(); + ЗапросБылЯвноПустым = _КодированнаяСтрокаПараметров = ""; + СтрокаПараметров = СериализоватьПары(_Формат); - Если СтрокаПараметров = "" Тогда - _ВладелецURL.УстановитьКодированнуюСтрокуЗапроса(Неопределено); - Иначе - _ВладелецURL.УстановитьКодированнуюСтрокуЗапроса(СтрокаПараметров); + _КодированнаяСтрокаПараметров = СтрокаПараметров; + Если СтрокаПараметров = "" И Не ЗапросБылЯвноПустым Тогда + _КодированнаяСтрокаПараметров = Неопределено; КонецЕсли; + _ПредставлениеСтрокиПараметров = _КодированнаяСтрокаПараметров; + _СтрокиАктуальны = Истина; + КонецПроцедуры Функция НоваяТаблицаПар() @@ -372,6 +501,17 @@ КонецПроцедуры +Процедура ЗаменитьПары(Пары) + + _Пары = НоваяТаблицаПар(); + _СледующийПорядок = 0; + + Для Каждого Пара Из Пары Цикл + ДобавитьПаруВКонец(Пара.Имя, Пара.Значение); + КонецЦикла; + +КонецПроцедуры + Функция НайтиПары(Имя) Возврат _Пары.НайтиСтроки(Новый Структура("Имя", Имя)); @@ -442,7 +582,7 @@ КонецПроцедуры -Процедура РазобратьСтрокуПараметров(СтрокаПараметров) +Процедура РазобратьСтрокуПараметров(СтрокаПараметров, Формат) Для Каждого Часть Из СтрРазделить(СтрокаПараметров, "&") Цикл Если Часть = "" Тогда @@ -459,10 +599,12 @@ Значение = Сред(Часть, ПозицияРазделителя + 1); КонецЕсли; - Имя = КодированиеURL.ДекодироватьПараметрФормы(Имя); + Имя = ДекодироватьПараметр(Имя, Формат); Если Значение <> Неопределено Тогда - Значение = КодированиеURL.ДекодироватьПараметрФормы(Значение); + Значение = ДекодироватьПараметр(Значение, Формат); + ИначеЕсли Формат = ФорматыПараметровЗапросаURL.ВебФорма() Тогда + Значение = ""; КонецЕсли; ДобавитьПаруВКонец(Имя, Значение); @@ -470,12 +612,50 @@ КонецПроцедуры +Функция ДекодироватьПараметр(Значение, Формат) + + Если Формат = ФорматыПараметровЗапросаURL.ВебФорма() Тогда + Возврат КодированиеURL.ДекодироватьПараметрФормы(Значение); + КонецЕсли; + + Возврат СервисыБиблиотекиURL.КодировщикКомпонентовURI().ДекодироватьПроценты(Значение); + +КонецФункции + Функция НоваяКопияПары(Имя, Значение) Возврат Новый Структура("Имя,Значение", Имя, Значение); КонецФункции +Функция НормализоватьФормат(Формат) + + Если Формат = Неопределено Тогда + Возврат ФорматыПараметровЗапросаURL.RFC3986(); + КонецЕсли; + + ПроверитьСтроковыйАргумент("Формат", Формат); + ПроверкаАргументов.ПроверитьДопустимость( + "Формат", + Формат = ФорматыПараметровЗапросаURL.RFC3986() Или Формат = ФорматыПараметровЗапросаURL.ВебФорма(), + "RFC3986, FormUrlEncoded" + ); + + Возврат Формат; + +КонецФункции + +Функция НормализоватьЗначениеДляФормата(Значение, Формат) + + Если Значение = Неопределено + И Формат = ФорматыПараметровЗапросаURL.ВебФорма() Тогда + Возврат ""; + КонецЕсли; + + Возврат Значение; + +КонецФункции + Процедура ПроверитьСтроковыйАргумент(ИмяАргумента, Значение) ПроверкаАргументов.ПроверитьТип( diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" new file mode 100644 index 0000000..06097e7 --- /dev/null +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -0,0 +1,23 @@ +#Область ПрограммныйИнтерфейс + +// Возвращает формат параметров с процентным кодированием по профилю RFC 3986. +// +// Возвращаемое значение: +// Строка - Формат параметров URL. +Функция RFC3986() Экспорт + + Возврат "RFC3986"; + +КонецФункции + +// Возвращает формат параметров application/x-www-form-urlencoded. +// +// Возвращаемое значение: +// Строка - Формат параметров URL. +Функция ВебФорма() Экспорт + + Возврат "FormUrlEncoded"; + +КонецФункции + +#КонецОбласти diff --git "a/tests/URL/\320\230\320\267\320\274\320\265\320\275\320\265\320\275\320\270\320\265\320\227\320\260\320\277\321\200\320\276\321\201\320\260\320\230\320\244\321\200\320\260\320\263\320\274\320\265\320\275\321\202\320\260URL.os" "b/tests/URL/\320\230\320\267\320\274\320\265\320\275\320\265\320\275\320\270\320\265\320\227\320\260\320\277\321\200\320\276\321\201\320\260\320\230\320\244\321\200\320\260\320\263\320\274\320\265\320\275\321\202\320\260URL.os" index dc199f5..8d14ec4 100644 --- "a/tests/URL/\320\230\320\267\320\274\320\265\320\275\320\265\320\275\320\270\320\265\320\227\320\260\320\277\321\200\320\276\321\201\320\260\320\230\320\244\321\200\320\260\320\263\320\274\320\265\320\275\321\202\320\260URL.os" +++ "b/tests/URL/\320\230\320\267\320\274\320\265\320\275\320\265\320\275\320\270\320\265\320\227\320\260\320\277\321\200\320\276\321\201\320\260\320\230\320\244\321\200\320\260\320\263\320\274\320\265\320\275\321\202\320\260URL.os" @@ -54,6 +54,22 @@ КонецПроцедуры +&Тест +Процедура ТестДолжен_НормализоватьНекорректныйПроцентКодированногоЗапроса() Экспорт + + // Подготовка + Адрес = Новый URL("https://host/a?old=1#f"); + + // Действие + Результат = Адрес.УстановитьКодированнуюСтрокуЗапроса("q=%ZZ"); + + // Проверка + Ожидаем.Что(Результат).Идентично(Адрес); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://host/a?q=%ZZ#f"); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://host/a?q=%25ZZ#f"); + +КонецПроцедуры + &Тест Процедура ТестДолжен_УдалитьЗапросНеопределеннымЗначениемИОчисткой() Экспорт @@ -139,7 +155,6 @@ &ИсточникЗначение("Путь", "a%ZZ", "url.invalid_percent_encoding") &ИсточникЗначение("Запрос", "тест", "url.invalid_syntax") &ИсточникЗначение("Запрос", "a#b", "url.invalid_syntax") -&ИсточникЗначение("Запрос", "a%ZZ", "url.invalid_percent_encoding") &ИсточникЗначение("Фрагмент", "тест", "url.invalid_syntax") &ИсточникЗначение("Фрагмент", "a#b", "url.invalid_syntax") &ИсточникЗначение("Фрагмент", "a%ZZ", "url.invalid_percent_encoding") diff --git "a/tests/URL/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217.os" "b/tests/URL/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217.os" index e316ce6..66c2d64 100644 --- "a/tests/URL/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217.os" +++ "b/tests/URL/\320\235\320\276\321\200\320\274\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217.os" @@ -7,6 +7,7 @@ &ИсточникЗначение("HTTP://%45XAMPLE.COM/%7euser", "http://example.com/~user") &ИсточникЗначение("X://[2001:DB8::A]/%2f?%2a#%41", "x://[2001:db8::a]/%2F?%2A#A") &ИсточникЗначение("x://EXAMPLE%2f.COM/", "x://example%2F.com/") +&ИсточникЗначение("https://example.com/?q=%ZZ", "https://example.com/?q=%25ZZ") &ИсточникЗначение("http://e:80?#", "http://e:80?#") &ИсточникЗначение("x://host:", "x://host") &ИсточникЗначение("//host:/path", "//host/path") @@ -56,6 +57,7 @@ &ИсточникЗначение("ftp://e:", "ftp://e") &ИсточникЗначение("x://e:", "x://e") &ИсточникЗначение("http://e:?%7e#%7e", "http://e/?~#~") +&ИсточникЗначение("https://example.com/?q=%ZZ", "https://example.com/?q=%25ZZ") &ИсточникЗначение("http://e/?", "http://e/?") &ИсточникЗначение("http://e/#", "http://e/#") Процедура ТестДолжен_НормализоватьURLДляСхемы(ИсходнаяСтрока, ОжидаемаяСтрока) Экспорт diff --git "a/tests/URL/\320\240\320\260\320\267\320\261\320\276\321\200.os" "b/tests/URL/\320\240\320\260\320\267\320\261\320\276\321\200.os" index a3e321d..10429ec 100644 --- "a/tests/URL/\320\240\320\260\320\267\320\261\320\276\321\200.os" +++ "b/tests/URL/\320\240\320\260\320\267\320\261\320\276\321\200.os" @@ -22,6 +22,39 @@ КонецПроцедуры +&Тест +Процедура ТестДолжен_СохранитьUnicodeВПредставленииЗапроса() Экспорт + + // Подготовка + ИсходнаяСтрока = "https://example.com/?ключ=значение"; + + // Действие + Адрес = Новый URL(ИсходнаяСтрока); + + // Проверка + Ожидаем.Что(Адрес.ВСтроку()).Равно(ИсходнаяСтрока); + Ожидаем.Что(Адрес.КодированнаяСтрокаЗапроса()).Равно( + "%D0%BA%D0%BB%D1%8E%D1%87=%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НормализоватьНекорректныйПроцентВКодированномПредставленииЗапроса() Экспорт + + // Подготовка + ИсходнаяСтрока = "https://example.com/?q=%ZZ"; + + // Действие + Адрес = Новый URL(ИсходнаяСтрока); + + // Проверка + Ожидаем.Что(Адрес.ВСтроку()).Равно(ИсходнаяСтрока); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://example.com/?q=%25ZZ"); + Ожидаем.Что(Адрес.ПараметрыЗапроса().Получить("q")).Равно("%ZZ"); + +КонецПроцедуры + &ПараметризованныйТест &ИсточникЗначение("file:///C:/FooBar/Baz.txt") &ИсточникЗначение("magnet:?xt=urn:btih:abcdef&dn=example") @@ -174,6 +207,7 @@ &ИсточникЗначение("/%", "url.invalid_percent_encoding") &ИсточникЗначение("/%1", "url.invalid_percent_encoding") &ИсточникЗначение("/%ZZ", "url.invalid_percent_encoding") +&ИсточникЗначение("/?%\", "url.invalid_syntax") Процедура ТестДолжен_ОтклонитьНедопустимуюИсходнуюСтрокуURL(Значение, ОжидаемыйКодОшибки) Экспорт // Подготовка diff --git "a/tests/URL/\320\240\320\260\320\267\321\200\320\265\321\210\320\265\320\275\320\270\320\265.os" "b/tests/URL/\320\240\320\260\320\267\321\200\320\265\321\210\320\265\320\275\320\270\320\265.os" index 53181a9..6fc2f22 100644 --- "a/tests/URL/\320\240\320\260\320\267\321\200\320\265\321\210\320\265\320\275\320\270\320\265.os" +++ "b/tests/URL/\320\240\320\260\320\267\321\200\320\265\321\210\320\265\320\275\320\270\320\265.os" @@ -170,6 +170,21 @@ КонецПроцедуры +&Тест +Процедура ТестДолжен_НеДекодироватьСервернуюЧастьПриРазрешении() Экспорт + + // Подготовка + База = Новый URL("https://example.com/base"); + Ссылка = Новый URL("//user%FF@example.com/path"); + + // Действие + Результат = База.Разрешить(Ссылка); + + // Проверка + Ожидаем.Что(Результат.ВСтроку()).Равно("https://user%FF@example.com/path"); + +КонецПроцедуры + &Тест Процедура ТестДолжен_ОтклонитьОтносительнуюБазуПриРазрешении() Экспорт diff --git "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\240\320\260\320\267\320\261\320\276\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\240\320\260\320\267\320\261\320\276\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" index 0237976..97b5908 100644 --- "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\240\320\260\320\267\320\261\320\276\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" +++ "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\240\320\260\320\267\320\261\320\276\321\200\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -14,7 +14,7 @@ &ИсточникЗначение("a=%", "a=%25") &ИсточникЗначение("a=%ZZ", "a=%25ZZ") &ИсточникЗначение("a=%FF", "a=%EF%BF%BD") -&ИсточникЗначение("q=a+b", "q=a+b") +&ИсточникЗначение("q=a+b", "q=a%2Bb") &ИсточникЗначение("q=a%2Bb", "q=a%2Bb") &ИсточникЗначение("a=1;b=2", "a=1%3Bb%3D2") &ИсточникЗначение("z=last&a=first&z=again", "z=last&a=first&z=again") @@ -26,7 +26,7 @@ &ИсточникЗначение("a=&b=", "a=&b=") &ИсточникЗначение("=value", "=value") &ИсточникЗначение("a=b=c", "a=b%3Dc") -&ИсточникЗначение("a+b=c+d", "a+b=c+d") +&ИсточникЗначение("a+b=c+d", "a%2Bb=c%2Bd") &ИсточникЗначение("a%2Bb=c%2Bd", "a%2Bb=c%2Bd") &ИсточникЗначение("a=%00%7F", "a=%00%7F") &ИсточникЗначение("a=1&a=2&a=3", "a=1&a=2&a=3") @@ -46,6 +46,22 @@ КонецПроцедуры +&Тест +Процедура ТестДолжен_ОтклонитьРазделительФрагментаВСтрокеЗапроса() Экспорт + + // Действие + КодОшибки = Неопределено; + Попытка + Параметры = Новый ПараметрыЗапросаURL("q=value#fragment"); + Исключение + КодОшибки = ДиагностикаОшибок.КодОшибки(ИнформацияОбОшибке()); + КонецПопытки; + + // Проверка + Ожидаем.Что(КодОшибки).Равно("url.invalid_syntax"); + +КонецПроцедуры + &Тест Процедура ТестДолжен_ПропускатьПустыеЧастиФормы() Экспорт @@ -64,8 +80,6 @@ &ПараметризованныйТест &ИсточникЗначение("СоздатьСNull") -&ИсточникЗначение("СоздатьСНеопределено") -&ИсточникЗначение("СоздатьСНевернымВладельцем") &ИсточникЗначение("ЕстьСNull") &ИсточникЗначение("ЕстьСНеопределено") &ИсточникЗначение("ПолучитьСNull") @@ -103,18 +117,6 @@ КонецПроцедуры -Процедура СоздатьСНеопределено() Экспорт - - Параметры = Новый ПараметрыЗапросаURL(Неопределено); - -КонецПроцедуры - -Процедура СоздатьСНевернымВладельцем() Экспорт - - Параметры = Новый ПараметрыЗапросаURL("", "не URL"); - -КонецПроцедуры - Процедура ЕстьСNull() Экспорт Параметры = Новый ПараметрыЗапросаURL(); diff --git "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" index 1de7364..e2fdbca 100644 --- "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" +++ "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\265\321\200\320\270\320\260\320\273\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -8,7 +8,10 @@ Процедура ТестДолжен_СериализоватьФорму() Экспорт // Подготовка - Параметры = Новый ПараметрыЗапросаURL(); + Параметры = Новый ПараметрыЗапросаURL( + Неопределено, + ФорматыПараметровЗапросаURL.ВебФорма() + ); Параметры .Добавить("пробел", "a b") .Добавить("плюс", "+") diff --git "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\270\320\275\321\205\321\200\320\276\320\275\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\270\320\275\321\205\321\200\320\276\320\275\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" index ba283c5..6c83e28 100644 --- "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\270\320\275\321\205\321\200\320\276\320\275\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" +++ "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\241\320\270\320\275\321\205\321\200\320\276\320\275\320\270\320\267\320\260\321\206\320\270\321\217\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -57,34 +57,19 @@ КонецПроцедуры &Тест -Процедура ТестДолжен_СвязыватьПараметрыСURLЧерезКонструктор() Экспорт +Процедура ТестДолжен_ВозвращатьОдинОбъектПараметровЗапроса() Экспорт // Подготовка Адрес = Новый URL("https://example.com/?a=1"); - Параметры = Новый ПараметрыЗапросаURL("a=1", Адрес); + ПервыйОбъект = Адрес.ПараметрыЗапроса(); + ВторойОбъект = Адрес.ПараметрыЗапроса(); // Действие - Адрес.УстановитьКодированнуюСтрокуЗапроса("b=2"); - - // Проверка - Ожидаем.Что(Параметры.Получить("b")).Равно("2"); - Ожидаем.Что(Параметры.Содержит("a")).ЭтоЛожь(); - -КонецПроцедуры - -&Тест -Процедура ТестДолжен_СинхронизироватьНесколькоФасадовПараметровЗапроса() Экспорт - - // Подготовка - Адрес = Новый URL("https://example.com/?a=1"); - ПервыйФасад = Адрес.ПараметрыЗапроса(); - ВторойФасад = Адрес.ПараметрыЗапроса(); - - // Действие - ПервыйФасад.Добавить("a", "2"); - Значения = ВторойФасад.ПолучитьВсе("a"); + ПервыйОбъект.Добавить("a", "2"); + Значения = ВторойОбъект.ПолучитьВсе("a"); // Проверка + Ожидаем.Что(ВторойОбъект).Идентично(ПервыйОбъект); Ожидаем.Что(Значения.Количество()).Равно(2); Ожидаем.Что(Значения[0]).Равно("1"); Ожидаем.Что(Значения[1]).Равно("2"); @@ -92,7 +77,7 @@ КонецПроцедуры &Тест -Процедура ТестДолжен_ОбновлятьСтарыйФасадПослеПрямойУстановкиЗапроса() Экспорт +Процедура ТестДолжен_ОбновлятьОбъектПараметровПослеПрямойУстановкиЗапроса() Экспорт // Подготовка Адрес = Новый URL("https://example.com/?a=1"); @@ -101,11 +86,11 @@ // Действие Адрес.УстановитьКодированнуюСтрокуЗапроса("b=3"); Значение = Параметры.Получить("b"); - СтарыйПараметрСуществует = Параметры.Содержит("a"); + ИсходныйПараметрСуществует = Параметры.Содержит("a"); // Проверка Ожидаем.Что(Значение).Равно("3"); - Ожидаем.Что(СтарыйПараметрСуществует).ЭтоЛожь(); + Ожидаем.Что(ИсходныйПараметрСуществует).ЭтоЛожь(); КонецПроцедуры @@ -128,7 +113,7 @@ // Проверка Ожидаем.Что(ПослеЧтения).Равно("https://e/?a=hello%20world&flag"); - Ожидаем.Что(ПослеМутации).Равно("https://e/?a=hello+world&flag&b=x"); + Ожидаем.Что(ПослеМутации).Равно("https://e/?a=hello%20world&flag&b=x"); КонецПроцедуры @@ -147,7 +132,22 @@ КонецПроцедуры &Тест -Процедура ТестДолжен_УдалятьПустойКомпонентЗапросаПриСортировке() Экспорт +Процедура ТестДолжен_УдалятьКомпонентЗапросаПриОчисткеСвязаннойКоллекции() Экспорт + + // Подготовка + Адрес = Новый URL("https://e/?a=1"); + + // Действие + Адрес.ПараметрыЗапроса().Очистить(); + + // Проверка + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://e/"); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://e/"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПустойКомпонентЗапросаПриСортировке() Экспорт // Подготовка Адрес = Новый URL("https://e/?"); @@ -156,7 +156,23 @@ Адрес.ПараметрыЗапроса().Сортировать(); // Проверка - Ожидаем.Что(Адрес.ВСтроку()).Равно("https://e/"); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://e/?"); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://e/?"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПустойКомпонентЗапросаПриУдаленииОтсутствующегоПараметра() Экспорт + + // Подготовка + Адрес = Новый URL("https://e/?"); + + // Действие + Адрес.ПараметрыЗапроса().Удалить("missing"); + + // Проверка + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://e/?"); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://e/?"); КонецПроцедуры diff --git "a/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" new file mode 100644 index 0000000..84639fc --- /dev/null +++ "b/tests/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL/\320\244\320\276\321\200\320\274\320\260\321\202\321\213\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\320\276\320\262\320\227\320\260\320\277\321\200\320\276\321\201\320\260URL.os" @@ -0,0 +1,483 @@ +#Использовать asserts +#Использовать "../../src" +#Использовать "../fixtures" + +#Область Тесты + +&Тест +Процедура ТестДолжен_ВернутьСтроковыеЗначенияФорматов() Экспорт + + // Действие + RFC3986 = ФорматыПараметровЗапросаURL.RFC3986(); + ВебФорма = ФорматыПараметровЗапросаURL.ВебФорма(); + + // Проверка + Ожидаем.Что(RFC3986).Равно("RFC3986"); + Ожидаем.Что(ВебФорма).Равно("FormUrlEncoded"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ИспользоватьRFC3986ПоУмолчанию() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL; + Параметры.Добавить("q", "hello world"); + + // Действие + Результат = Параметры.ВСтроку(); + + // Проверка + Ожидаем.Что(Результат).Равно("q=hello%20world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НормализоватьПараметрБезЗначенияВебФормы() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL( + Неопределено, + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Параметры.Добавить("flag"); + + // Проверка + Ожидаем.Что(Параметры.Получить("flag")).Равно(""); + Ожидаем.Что(Параметры.ВСтроку()).Равно("flag="); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматВНезависимойКопииURL() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?q=hello+world"); + Адрес.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Копия = Адрес.Скопировать(); + Копия.ПараметрыЗапроса().Добавить("copy", "two words"); + + // Проверка + Ожидаем.Что(Копия.ВСтроку()).Равно( + "https://example.com/?q=hello+world©=two+words" + ); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://example.com/?q=hello+world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматВКонструктореКопииURL() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?q=hello+world"); + Адрес.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Копия = Новый URL(Адрес); + Копия.ПараметрыЗапроса().Добавить("copy", "two words"); + + // Проверка + Ожидаем.Что(Копия.ВСтроку()).Равно( + "https://example.com/?q=hello+world©=two+words" + ); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://example.com/?q=hello+world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматПриНормализацииURL() Экспорт + + // Подготовка + Адрес = Новый URL("HTTPS://EXAMPLE.COM/a/../?q=hello+world"); + Адрес.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Результат = Адрес.НормализованныйДляСхемы(); + Результат.ПараметрыЗапроса().Добавить("page", "two words"); + + // Проверка + Ожидаем.Что(Результат.ВСтроку()).Равно( + "https://example.com/?q=hello+world&page=two+words" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НаследоватьФорматИсточникаЗапросаПриРазрешении() Экспорт + + // Подготовка + База = Новый URL("https://example.com/a/?base=hello+world"); + База.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + Ссылка = Новый URL("?ref=two+words"); + Ссылка.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + ИзБазы = База.Разрешить("#part"); + ИзСсылки = База.Разрешить(Ссылка); + ИзБазы.ПараметрыЗапроса().Добавить("next", "a b"); + ИзСсылки.ПараметрыЗапроса().Добавить("next", "a b"); + + // Проверка + Ожидаем.Что(ИзБазы.ВСтроку()).Равно( + "https://example.com/a/?base=hello+world&next=a+b#part" + ); + Ожидаем.Что(ИзСсылки.ВСтроку()).Равно( + "https://example.com/a/?ref=two+words&next=a+b" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматЦелиПриРелятивизации() Экспорт + + // Подготовка + База = Новый URL("https://example.com/a/"); + Цель = Новый URL("https://example.com/a/b?q=hello+world"); + Цель.ПараметрыЗапроса().РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Результат = База.СделатьОтносительным(Цель); + Результат.ПараметрыЗапроса().Добавить("next", "a b"); + + // Проверка + Ожидаем.Что(Результат.ВСтроку()).Равно("b?q=hello+world&next=a+b"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СкопироватьПарыИФорматВURL() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?old=1"); + ПараметрыАдреса = Адрес.ПараметрыЗапроса(); + Источник = Новый ПараметрыЗапросаURL( + "q=hello+world", + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Действие + Адрес.УстановитьПараметрыЗапроса(Источник); + ПараметрыАдреса.Добавить("page", "two words"); + + // Проверка + Ожидаем.Что(Адрес.ПараметрыЗапроса()).Идентично(ПараметрыАдреса); + Ожидаем.Что(Адрес.ВСтроку()).Равно( + "https://example.com/?q=hello+world&page=two+words" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СвязатьОдинОбъектПараметровСURL() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?q=a+b"); + ПерваяСсылка = Адрес.ПараметрыЗапроса(); + + // Действие + ВтораяСсылка = Адрес.ПараметрыЗапроса(); + ПерваяСсылка + .РазобратьКак(ФорматыПараметровЗапросаURL.ВебФорма()) + .Добавить("page", 2); + + // Проверка + Ожидаем.Что(ВтораяСсылка).Идентично(ПерваяСсылка); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://example.com/?q=a+b&page=2"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьФорматБезСозданияЗапроса() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL; + + // Действие + Результат = Параметры.УстановитьКодированнуюСтроку( + Неопределено, + ФорматыПараметровЗапросаURL.ВебФорма() + ); + СтрокаДоДобавления = Параметры.КодированнаяСтрока(); + Параметры.Добавить("q", "hello world"); + + // Проверка + Ожидаем.Что(Результат).Идентично(Параметры); + Ожидаем.Что(СтрокаДоДобавления).Равно(Неопределено); + Ожидаем.Что(Параметры.ВСтроку()).Равно("q=hello+world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РазобратьТекущуюСтрокуКакВебФорму() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=a+b"); + ИсходнаяСтрока = Параметры.КодированнаяСтрока(); + + // Действие + Результат = Параметры.РазобратьКак( + ФорматыПараметровЗапросаURL.ВебФорма() + ); + + // Проверка + Ожидаем.Что(Результат).Идентично(Параметры); + Ожидаем.Что(Параметры.Получить("q")).Равно("a b"); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно(ИсходнаяСтрока); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматИКодированнуюСтроку() Экспорт + + // Подготовка + Формат = ФорматыПараметровЗапросаURL.ВебФорма(); + + // Действие + Параметры = Новый ПараметрыЗапросаURL("q=%68ello+world", Формат); + + // Проверка + Ожидаем.Что(Параметры.ТекущийФормат()).Равно(Формат); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=%68ello+world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РазобратьИСериализоватьВебФорму() Экспорт + + // Подготовка + Формат = ФорматыПараметровЗапросаURL.ВебФорма(); + + // Действие + Параметры = Новый ПараметрыЗапросаURL("q=hello+world&flag", Формат); + + // Проверка + Ожидаем.Что(Параметры.Получить("q")).Равно("hello world"); + Ожидаем.Что(Параметры.Получить("flag")).Равно(""); + Ожидаем.Что(Параметры.ВСтроку()).Равно("q=hello+world&flag="); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_КодироватьСпециальныеСимволыПоФормату() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL; + Параметры.Добавить("q", "a b+~*"); + + // Действие + RFC3986 = Параметры.ВСтроку(); + ВебФорма = Параметры.ВСтроку(ФорматыПараметровЗапросаURL.ВебФорма()); + + // Проверка + Ожидаем.Что(RFC3986).Равно("q=a%20b%2B~%2A"); + Ожидаем.Что(ВебФорма).Равно("q=a+b%2B%7E*"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СериализоватьВДругомФорматеБезИзмененияСостояния() Экспорт + + // Подготовка + Формат = ФорматыПараметровЗапросаURL.ВебФорма(); + Параметры = Новый ПараметрыЗапросаURL("q=%68ello+world", Формат); + + // Действие + Результат = Параметры.ВСтроку(ФорматыПараметровЗапросаURL.RFC3986()); + + // Проверка + Ожидаем.Что(Результат).Равно("q=hello%20world"); + Ожидаем.Что(Параметры.ТекущийФормат()).Равно(Формат); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=%68ello+world"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РазличатьОтсутствующийИПустойЗапрос() Экспорт + + // Подготовка + БезЗапроса = Новый ПараметрыЗапросаURL; + ПустойЗапрос = Новый ПараметрыЗапросаURL(""); + + // Действие + СтрокаБезЗапроса = БезЗапроса.ВСтроку(); + СтрокаПустогоЗапроса = ПустойЗапрос.ВСтроку(); + + // Проверка + Ожидаем.Что(БезЗапроса.КодированнаяСтрока()).Равно(Неопределено); + Ожидаем.Что(ПустойЗапрос.КодированнаяСтрока()).Равно(""); + Ожидаем.Что(СтрокаБезЗапроса).Равно(""); + Ожидаем.Что(СтрокаПустогоЗапроса).Равно(""); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ЗаменитьСтрокуИФормат() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=old"); + Формат = ФорматыПараметровЗапросаURL.ВебФорма(); + + // Действие + Результат = Параметры.УстановитьКодированнуюСтроку("q=new+value", Формат); + + // Проверка + Ожидаем.Что(Результат).Идентично(Параметры); + Ожидаем.Что(Параметры.Получить("q")).Равно("new value"); + Ожидаем.Что(Параметры.ТекущийФормат()).Равно(Формат); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=new+value"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьФорматПриПрямойУстановкеЗапросаURL() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?q=old+value"); + Параметры = Адрес.ПараметрыЗапроса(); + Параметры.РазобратьКак(ФорматыПараметровЗапросаURL.ВебФорма()); + + // Действие + Адрес.УстановитьКодированнуюСтрокуЗапроса("q=new+value"); + Параметры.Добавить("next", "a b"); + + // Проверка + Ожидаем.Что(Адрес.ПараметрыЗапроса()).Идентично(Параметры); + Ожидаем.Что(Параметры.Получить("q")).Равно("new value"); + Ожидаем.Что(Адрес.ВСтроку()).Равно( + "https://example.com/?q=new+value&next=a+b" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_АтомарноОтклонитьНеизвестныйФормат() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=a+b"); + КодОшибки = Неопределено; + + // Действие + Попытка + Параметры.РазобратьКак("Unknown"); + Исключение + КодОшибки = ДиагностикаОшибок.КодОшибки(ИнформацияОбОшибке()); + КонецПопытки; + + // Проверка + Ожидаем.Что(КодОшибки).Равно("argument.invalid_value"); + Ожидаем.Что(Параметры.ТекущийФормат()).Равно( + ФорматыПараметровЗапросаURL.RFC3986() + ); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=a+b"); + Ожидаем.Что(Параметры.Получить("q")).Равно("a+b"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьНеопределенныйФорматПовторногоРазбора() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=a+b"); + КодОшибки = Неопределено; + + // Действие + Попытка + Параметры.РазобратьКак(Неопределено); + Исключение + КодОшибки = ДиагностикаОшибок.КодОшибки(ИнформацияОбОшибке()); + КонецПопытки; + + // Проверка + Ожидаем.Что(КодОшибки).Равно("argument.invalid_type"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НормализоватьНекорректныйПроцентВСвязаннойКоллекции() Экспорт + + // Подготовка + Адрес = Новый URL("https://example.com/?q=old"); + Параметры = Адрес.ПараметрыЗапроса(); + + // Действие + Параметры.УстановитьКодированнуюСтроку("q=%"); + + // Проверка + Ожидаем.Что(Параметры.Получить("q")).Равно("%"); + Ожидаем.Что(Параметры.ИсходнаяСтрока()).Равно("q=%"); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=%25"); + Ожидаем.Что(Адрес.ВСтроку()).Равно("https://example.com/?q=%"); + Ожидаем.Что(Адрес.КодированноеПредставление()).Равно("https://example.com/?q=%25"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьИсходнуюИКодированнуюСтроки() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=привет"); + + // Действие + ИсходнаяСтрока = Параметры.ИсходнаяСтрока(); + КодированнаяСтрока = Параметры.КодированнаяСтрока(); + + // Проверка + Ожидаем.Что(ИсходнаяСтрока).Равно("q=привет"); + Ожидаем.Что(КодированнаяСтрока).Равно( + "q=%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82" + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НормализоватьНекорректныйПроцентВАвтономнойКоллекции() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL("q=old"); + + // Действие + Параметры.УстановитьКодированнуюСтроку("q=%ZZ"); + + // Проверка + Ожидаем.Что(Параметры.Получить("q")).Равно("%ZZ"); + Ожидаем.Что(Параметры.ИсходнаяСтрока()).Равно("q=%ZZ"); + Ожидаем.Что(Параметры.КодированнаяСтрока()).Равно("q=%25ZZ"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьПовторныйРазборБезСтроки() Экспорт + + // Подготовка + Параметры = Новый ПараметрыЗапросаURL; + + // Действие + КодОшибки = Неопределено; + Попытка + Параметры.РазобратьКак(ФорматыПараметровЗапросаURL.ВебФорма()); + Исключение + КодОшибки = ДиагностикаОшибок.КодОшибки(ИнформацияОбОшибке()); + КонецПопытки; + + // Проверка + Ожидаем.Что(КодОшибки).Равно("argument.invalid_value"); + +КонецПроцедуры + +#КонецОбласти