Как собрать параметры URL через url.Values в Go

Как собрать параметры URL через url.Values в Go
Содержание статьи

Поисковая строка содержит C++ guide, а фильтр допускает сразу две метки. Если собрать запрос конкатенацией, плюс можно перепутать с пробелом, а повтор ключа потерять. На Mentoring.digital используем url.Values и проверим результат обратным разбором.

Храните параметры как значения, а не готовые куски URL

url.Values связывает строковый ключ со срезом строк. Это позволяет хранить несколько значений одного параметра. Метод Add добавляет ещё одно значение, а Set заменяет все значения этого ключа одним. Кодирование выполняется после того, как структура собрана.

package main

import (
    "fmt"
    "net/url"
)

func main() {
    address, err := url.Parse("https://example.test/search")
    if err != nil {
        panic(err)
    }
    query := url.Values{}
    query.Set("q", "C++ guide")
    query.Add("tag", "go")
    query.Add("tag", "sql")
    address.RawQuery = query.Encode()
    fmt.Println(address.String())

    restored, err := url.ParseQuery(address.RawQuery)
    if err != nil {
        panic(err)
    }
    fmt.Println(restored.Get("q"))
    fmt.Println(restored["tag"])
}

Ожидаемый адрес: https://example.test/search?q=C%2B%2B+guide&tag=go&tag=sql. Затем выводятся C++ guide и [go sql]. Пример только собирает и разбирает адрес, не отправляя HTTP-запрос.

Буквальный плюс и пробел кодируются по-разному

Encode() сортирует ключи и кодирует параметры. Пробел в строке запроса представляется знаком +, а буквальный плюс знаком %2B. При обратном разборе исходное значение восстанавливается. Передавайте в Set обычный текст, а не заранее обработанную строку: иначе знак процента может закодироваться повторно.

Правила строки запроса не следует применять ко всему пути URL. Для работы с адресом используйте разобранный url.URL, а в RawQuery присваивайте готовый результат Encode(). Для пользовательского адреса успешный разбор ещё не подтверждает, что схема и хост подходят вашему приложению.

Различайте изменение структуры и изменение адреса

После address.RawQuery = query.Encode() адрес содержит строку на этот момент. Последующий query.Set("tag", "java") заменит значения в структуре, но не обновит поле адреса автоматически. Чтобы получить новый адрес, выполните кодирование и присваивание ещё раз.

Get("tag") возвращает только первое значение. Если контракт разрешает несколько меток, читайте срез query["tag"]. Также различайте отсутствующий ключ и переданное пустое значение: один Get() не показывает эту разницу. Для записи инициализируйте карту через литерал, как в примере, или make(url.Values).

Проверяйте значения после обратного разбора

Сохраните случаи с пробелом, буквальным плюсом, кириллицей, пустым значением и двумя метками. Сравнивайте восстановленные значения и их количество, а не только красивый вид адреса. Для взаимодействия с сервером дополнительно проверьте, как он читает повторяющиеся параметры.

Работу со стандартной библиотекой можно отработать на курсе Go с наставником. Если контракт клиента и сервера уже расходится, подготовьте обезличенный пример запроса для разбора с ментором по Go.

Похожие статьи по данной теме