Плюс и амперсанд потерялись в URL Go-клиента: как собрать query

Поисковый запрос A&B+C отправили внешнему API склеиванием строк, и сервер получил другие параметры. Символы & и + имеют специальное значение в форме query. Неправильная сборка меняет запрос до того, как его увидит бизнес-логика.

Как разобрать ситуацию

Используйте url.Values и Encode для значений параметров. Это сохраняет разделение между структурой query и пользовательским текстом. Не экранируйте готовую строку целиком и не применяйте одну процедуру одинаково к path и query: это разные части URL. На стороне получения проверяйте декодированное значение, а не внешний вид процента в адресе.

Пример и ожидаемый результат

Пример создаёт единственный параметр q со значением A&B+C, кодирует его и снова разбирает. Правильный круговой результат сохраняет исходный текст и ровно один ключ. При ручном добавлении &B возник бы дополнительный параметр.

package main

import (
	"fmt"
	"net/url"
)

func main() {
	q := url.Values{}
	q.Set("q", "A&B+C")
	parsed, err := url.ParseQuery(q.Encode())
	if err != nil {
		panic(err)
	}
	fmt.Println(parsed.Get("q"), len(parsed))
}

Программа выводит A&B+C 1: текст восстановлен точно, число ключей равно одному. Это локальная проверка кодирования без вызова внешнего API.

Проверка перед применением

  • Проверьте пробел, плюс, амперсанд, кириллицу и пустое значение.
  • Для повторяемого параметра используйте несколько значений осознанно, не перезаписывайте их случайным Set.
  • Не выводите полный URL в журнал, если query содержит персональные или секретные данные.

Для подписи API проверяйте точную каноническую форму по контракту поставщика: порядок параметров, повторяющиеся ключи и кодирование пробела могут иметь значение. Успешное round-trip декодирование подтверждает сохранность текста, но не гарантирует, что подпись совпадёт с чужим алгоритмом без отдельной тестовой пары.

Границы решения

Правильное экранирование не заменяет проверку допустимого хоста и пути. Если пользователь управляет целым URL, возникает отдельная задача ограничения сетевого доступа. Также поставщик может использовать собственную канонизацию при подписи запросов.

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

Микросервисы на Go: HTTP, PostgreSQL, gRPC, Kafka и Docker. Перед записью сравните программу со своей задачей и подготовкой.

Техническая справка: pkg.go.dev: документация.

Следующий шаг: Go backend: HTTP, конкурентность и устойчивость сервиса. По смежным вопросам: Отмена HTTP-запроса в Go: как убедиться, что context дошёл до клиента, Как оборачивать ошибки в Go и не потерять машинную проверку причины.