HTTP-запросы в Go: отправка GET, POST, PUT и DELETE

Что такое HTTP-запрос?
HTTP описывает, как клиент (браузер или приложение) запрашивает ресурс у сервера, а сервер возвращает ответ. Запрос обычно содержит URL ресурса и дополнительные сведения: метод (GET, POST и т. д.), заголовки, параметры строки запроса и, по необходимости, тело запроса.
Краткое определение: HTTP‑запрос — это структурированное сообщение от клиента к серверу, содержащее метод, путь и опциональные данные.
Типичные методы: GET (получить), POST (создать), PUT (обновить целиком), PATCH (частичное обновление), DELETE (удалить), HEAD, OPTIONS, CONNECT.
PUT и PATCH часто используют для обновления: PUT обычно заменяет ресурс целиком, PATCH — вносит частичное изменение. На практике различия зависят от API.
Отправка запросов в Go — общая идея
Пакет net/http предоставляет набор типов и функций для создания запросов и получения ответов. Базовый паттерн:
- Создаёте запрос с http.NewRequest(method, url, body).
- Настраиваете заголовки (Content-Type, Authorization и т.д.).
- Передаёте запрос клиенту (http.Client) методом Do.
- Читаете response.Body, закрываете его через defer.
Для простых случаев можно использовать http.Get и http.Post, они сокращают шаблон.
Важно: всегда обрабатывать ошибки и закрывать тело ответа (defer response.Body.Close()). В продакшн‑коде дополнительно задавайте таймауты и контекст.
Примеры: использование reqres.in для тестов
Ниже приведены примеры функций на Go, которые отправляют POST, GET, PUT и DELETE через API reqres.in. Код оставлен практически без изменений для наглядности — в реальном проекте оборачивайте повторяющиеся операции (создание запросов, чтение тела, логирование) в вспомогательные функции.
POST
Функция создаёт пользователя через POST /api/users.
`package main
import (
bytes"
encoding/json"
fmt"
io"
net/http"
)
funccreateUser(name, job string) {
fmt.Println("Creating user...")
apiUrl := "https://reqres.in/api/users"
userData := []byte(`{"name":"` + name + `","job":"` + job + `"}`)
// create new http request
request, error := http.NewRequest("POST", apiUrl, bytes.NewBuffer(userData))
request.Header.Set("Content-Type", "application/json; charset=utf-8")
// send the request
client := &http.Client{}
response, error := client.Do(request)
if error != nil {
fmt.Println(error)
}
responseBody, error := io.ReadAll(response.Body)
if error != nil {
fmt.Println(error)
}
formattedData := formatJSON(responseBody)
fmt.Println("Status: ", response.Status)
fmt.Println("Response body: ", formattedData)
// clean up memory after execution
defer response.Body.Close()
}
`
Функция formatJSON, которая форматирует JSON для вывода, выглядит так:
`// function to format JSON data
funcformatJSON(data []byte) string {
var out bytes.Buffer
err := json.Indent(&out, data, "", " ")
if err != nil {
fmt.Println(err)
}
d := out.Bytes()
returnstring(d)
}
`
Вызов:
`funcmain() {
fmt.Println("Making POST request...")
createUser("Tim Omolana", "Writer")
}
`
Вывод программы (пример):
GET
Функция получает пользователя по ID через GET /api/users/{id}.
`// main.go
funcgetUser(id string) {
fmt.Println("Getting user by ID...")
// make GET request to API to get user by ID
apiUrl := "https://reqres.in/api/users/" + id
request, error := http.NewRequest("GET", apiUrl, nil)
if error != nil {
fmt.Println(error)
}
request.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{}
response, error := client.Do(request)
if error != nil {
fmt.Println(error)
}
responseBody, error := io.ReadAll(response.Body)
if error != nil {
fmt.Println(error)
}
formattedData := formatJSON(responseBody)
fmt.Println("Status: ", response.Status)
fmt.Println("Response body: ", formattedData)
// clean up memory after execution
defer response.Body.Close()
}
`
Пример вызова:
`funcmain() {
fmt.Println("Making GET request...")
getUser("2")
}
`
Вывод программы (пример):
Замечание: GET обычно не имеет тела запроса — данные передаются в URL или через заголовки.
PUT
PUT похож на POST, но чаще используется для обновления существующего ресурса. Обратите внимание, что для обновления обычно добавляют ID в путь.
`// main.go
funcupdateUser(name, job, id string) {
fmt.Println("Updating user...")
// make PUT request to API to update user
apiUrl := "https://reqres.in/api/users/" + id
userData := []byte(`{"name":"` + name + `","job":"` + job + `"}`)
// create new http PUT request
request, error := http.NewRequest("PUT", apiUrl, bytes.NewBuffer(userData))
request.Header.Set("Content-Type", "application/json; charset=utf-8")
// Remaining function body from createUser function...
// Make request, get response, and clear memory...
}
`
Вызов:
`func main() {
// update entry with the ID 2.
updateUser("Tim Newname", "Staff Writer", "2")
}
`
Пример вывода (иллюстрация):
DELETE
DELETE удаляет ресурс по указанному URI. Часто ответ содержит только статус выполнения.
`funcdeleteUser(id string) {
fmt.Println("Deleting user...")
// make DELETE request to API to delete user
apiUrl := "https://reqres.in/api/users/" + id
// create new http request
request, error := http.NewRequest("DELETE", apiUrl, nil)
request.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{}
response, error := client.Do(request)
if error != nil {
fmt.Println(error)
}
fmt.Println("Status: ", response.Status)
}
`
Пример вызова:
`funcmain() {
fmt.Println("Making DELETE request...")
deleteUser("2")
}
`
Вывод программы (пример):
Совет: для быстрого прототипирования используйте http.Post и http.Get, но в продакшне чаще нужен клиент с заданным таймаутом и политикой повторных попыток.
Практические рекомендации и антипаттерны
Обработка ошибок и освобождение ресурсов
- Всегда проверяйте ошибки после http.NewRequest и client.Do.
- Закрывайте response.Body через defer сразу после проверки ошибки: defer response.Body.Close().
- Не полагайтесь на автоматическое чтение — читайте тело и при необходимости ограничивайте размер через io.LimitReader.
Таймауты и контекст
- Создавайте http.Client с Timeout (например, 5–30 секунд в зависимости от ситуации).
- Для отмены запроса используйте context.Context: кроме контроля времени, это позволяет прерывать запрос при остановке сервиса.
Пример создания клиента с таймаутом:
- client := &http.Client{Timeout: 10 * time.Second}
Повторные попытки и идемпотентность
- GET, PUT, DELETE часто допускают повторные попытки; POST обычно нет (может создать дубликат).
- Для надёжности используйте стратегию экспоненциального бэкоффа и уникальные idempotency‑ключи для операций создания.
Различие между http.Get/http.Post и http.NewRequest
- http.Get/http.Post удобны для прототипов и простых сценариев.
- http.NewRequest + client.Do даёт полный контроль: заголовки, метод, тело, контекст.
Безопасность и надёжность
- Всегда используйте HTTPS для обмена личными данными.
- Проверяйте и обновляйте сертификаты TLS в окружении. Не отключайте проверку сертификата в продакшне.
- Устанавливайте заголовки безопасности (Content-Security-Policy на стороне сервера, если релевантно).
- Валидируйте и ограничивайте размеры входящих/исходящих тел.
Приватность и соответствие требованиям
- Если вы отправляете или получаете персональные данные (имена, email, идентификаторы), проверьте, соответствует ли обработка данным политике конфиденциальности и локальным требованиям (например, GDPR).
- Анонимизируйте или шифруйте данные при передаче и хранении.
Когда этот подход не подходит
- Для высокопроизводительных потоковых API (Server-Sent Events, WebSocket) нет смысла использовать стандартные синхронные HTTP‑запросы; выбирайте соответствующие протоколы.
- Для миллисекундных задержек и сотен тысяч запросов в секунду понадобится пул соединений, оптимизация клиента и нагрузочное тестирование.
Контроль качества: Критерии приёмки
- Запросы выполняются с корректными HTTP‑методами и заголовками.
- Ошибки сетевого уровня и коды ответа 4xx/5xx логируются и корректно обрабатываются.
- response.Body всегда закрывается.
- Для внешних API есть таймауты и стратегия повторных попыток.
- Личные данные передаются только по HTTPS и находятся под контролем политики приватности.
Мини‑чеклист для ролей
Для разработчика:
- Использовать context для отмены запросов.
- Обработать все ошибки и закрыть response.Body.
- Покрыть ключевые сценарии тестами.
Для DevOps:
- Настроить мониторинг и метрики (latency, error rate).
- Контролировать TLS и обновления сертификатов.
Для QA:
- Проверить поведение при сетевых ошибках и при 4xx/5xx ответах.
- Выполнить нагрузочные тесты и тесты на таймауты.
Тесты и примеры приёмки
- Тест: успешный POST возвращает 201/200 и валидный JSON.
- Тест: при недоступном хосте запрос возвращает ошибку и не блокирует горутину.
- Тест: при большом теле ответа чтение ограничено и не приводит к OOM.
Короткое объявление (100–200 слов)
HTTP‑клиенты в Go: быстро и надёжно. В этой статье показано, как отправлять GET, POST, PUT и DELETE запросы с помощью пакета net/http. Примеры используют тестовый API reqres.in и демонстрируют создание запроса, установку заголовков, чтение ответа и форматирование JSON. Вы также найдёте рекомендации по таймаутам, использованию контекста, обработке ошибок, безопасности TLS и работе с персональными данными. Статья полезна для разработчиков, которые хотят перейти от простых вызовов http.Get к более контролируемому использованию http.Client и http.NewRequest в боевых приложениях.
Модель принятия решений (Mermaid)
flowchart TD
A[Нужно сделать HTTP‑запрос?] --> B{Требуется ли сложная конфигурация?}
B -- Да --> C[Использовать http.NewRequest + http.Client]
B -- Нет --> D[Использовать http.Get / http.Post]
C --> E{Нужен таймаут/отмена?}
E -- Да --> F[Передать context и задать Timeout]
E -- Нет --> G[Обычный запрос]
D --> GКраткая 1‑строчная глоссарий
- HTTP: протокол передачи гипертекста; метод: действие над ресурсом; net/http: стандартная библиотека Go для HTTP.
Заключение
net/http в Go даёт мощный, но простой в использовании набор инструментов для работы с HTTP. Для прототипов хватит http.Get и http.Post, но для надёжных приложений используйте http.NewRequest, контекст, таймауты и продуманную обработку ошибок. Безопасность (HTTPS, валидация сертификатов) и управление приватностью данных — обязательные элементы при интеграции с внешними API.
Важно: симплифицируйте повторяющиеся операции через вспомогательные функции и библиотечные обёртки, чтобы обеспечить единообразие логики запросов и обработки ответов в проекте.
Похожие материалы
SSH и Docker: стоит ли подключаться к контейнерам
Перенос и резервное копирование VirtualBox
Как включить PiP (Картинка в картинке) в Chromium Edge
Kubescape: сканирование безопасности Kubernetes
Как выделять и закрывать несколько вкладок в браузере