Как получить JSON ответ от HTTP GET запроса в Go
Пошаговое решение: получаем JSON в Go
Вы на правильном пути! Действительно, в Go нужно делать несколько шагов, но это даёт больше контроля. Разберём всё по порядку.
1. Базовая структура для ответа
Сначала определим структуру, в которую будем парсить JSON. Допустим, API возвращает пользователя:
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
Обратите внимание на теги json:"fieldname" — они указывают, какому полю JSON соответствует поле структуры.
2. Полный пример с обработкой ошибок
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// 1. Выполняем GET запрос
resp, err := http.Get("https://api.example.com/user/1")
if err != nil {
fmt.Printf("Ошибка выполнения запроса: %v\n", err)
return
}
defer resp.Body.Close()
// 2. Проверяем статус-код
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Сервер вернул ошибку %d: %s\n", resp.StatusCode, string(body))
return
}
// 3. Читаем тело ответа
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Ошибка чтения ответа: %v\n", err)
return
}
// 4. Парсим JSON в структуру
var user User
err = json.Unmarshal(body, &user)
if err != nil {
fmt.Printf("Ошибка парсинга JSON: %v\n", err)
return
}
// 5. Используем данные
fmt.Printf("Пользователь: %s (ID: %d, Email: %s)\n", user.Name, user.ID, user.Email)
}
3. Альтернатива: используем json.Decoder (для потоков)
Если вы не хотите читать всё тело в память, можно использовать json.Decoder напрямую из resp.Body:
var user User
err = json.NewDecoder(resp.Body).Decode(&user)
if err != nil {
fmt.Printf("Ошибка декодирования JSON: %v\n", err)
return
}
4. Важные моменты
- Всегда закрывайте resp.Body через
defer resp.Body.Close(), чтобы не утекали соединения - Проверяйте статус-код — даже при 200 OK тело может быть пустым или содержать ошибку
- Используйте io.ReadAll для небольших ответов (до нескольких МБ), иначе берите
json.NewDecoder - Обрабатывайте ошибки на каждом шаге — это стандартная практика в Go
5. Пример с таймаутом (рекомендуется)
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Get("https://api.example.com/data")
Теперь вы знаете, как получать JSON в Go. Это немного многословнее, чем в Python, но зато вы полностью контролируете процесс и обработку ошибок. Удачи!
<<Использование вспомогательной функции для повторного использования
Чтобы не писать один и тот же код каждый раз, можно создать универсальную функцию для GET запросов с JSON ответом:
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// GetJSON выполняет GET запрос и парсит JSON ответ в target
func GetJSON(url string, target interface{}) error {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(url)
if err != nil {
return fmt.Errorf("ошибка запроса: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("статус %d", resp.StatusCode)
}
return json.NewDecoder(resp.Body).Decode(target)
}
func main() {
var user User
err := GetJSON("https://api.example.com/user/1", &user)
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Printf("Пользователь: %s\n", user.Name)
}
Преимущества такого подхода:
- Переиспользование кода — одна функция для любых структур
- Централизованная обработка таймаутов и статус-кодов
- Простота использования — передал URL и указатель на структуру
Этот паттерн очень распространён в реальных Go проектах. Вы можете расширить функцию, добавив заголовки, логирование или поддержку разных HTTP методов.