Как отправить JSON строку в POST запросе на Go?
Правильный способ отправки JSON в POST запросе на Go
Вы на верном пути — действительно нужно использовать json.Marshal для сериализации структуры в JSON, а затем создать из неё bytes.Reader для передачи в запрос. Вот полное решение с объяснением каждого шага.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// 1. Создаём структуру с данными
user := User{
Name: "Иван Петров",
Email: "ivan@example.com",
}
// 2. Сериализуем структуру в JSON (срез байт)
jsonData, err := json.Marshal(user)
if err != nil {
fmt.Printf("Ошибка сериализации JSON: %v\n", err)
return
}
// 3. Создаём POST запрос
resp, err := http.Post(
"https://api.example.com/users",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
fmt.Printf("Ошибка выполнения запроса: %v\n", err)
return
}
defer resp.Body.Close()
// 4. Читаем ответ сервера
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Ошибка чтения ответа: %v\n", err)
return
}
// 5. Выводим статус и тело ответа
fmt.Printf("Статус: %d\n", resp.StatusCode)
fmt.Printf("Ответ: %s\n", string(body))
}
Пошаговое объяснение
- json.Marshal — преобразует структуру в срез байт формата JSON. Важно проверять ошибку, так как структура может содержать поля, которые не сериализуются (например, каналы).
- bytes.NewBuffer — создаёт буфер из среза байт, который реализует интерфейс
io.Reader, необходимый для передачи тела запроса. - Content-Type: application/json — обязательный заголовок, указывающий серверу формат данных.
- defer resp.Body.Close() — обязательно закрывайте тело ответа, чтобы избежать утечки ресурсов.
Продвинутый вариант с http.Client
Для более гибкого управления запросами (таймауты, заголовки, куки) используйте http.Client:
func sendUser(user User) (*http.Response, error) {
jsonData, err := json.Marshal(user)
if err != nil {
return nil, fmt.Errorf("marshal error: %w", err)
}
req, err := http.NewRequest(
http.MethodPost,
"https://api.example.com/users",
bytes.NewBuffer(jsonData),
)
if err != nil {
return nil, fmt.Errorf("request creation error: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer your-token-here")
client := &http.Client{Timeout: 10 * time.Second}
return client.Do(req)
}
Декодирование JSON-ответа
Если сервер возвращает JSON, декодируйте его сразу в структуру:
type APIResponse struct {
ID int `json:"id"`
Message string `json:"message"`
}
var response APIResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
fmt.Printf("Ошибка декодирования: %v\n", err)
return
}
fmt.Printf("ID: %d, Сообщение: %s\n", response.ID, response.Message)
Использование json.NewDecoder эффективнее, чем io.ReadAll + json.Unmarshal, так как декодирует потоково, без загрузки всего ответа в память.
Типичные ошибки
- Забыть указать
Content-Type: application/json— сервер может не распознать формат. - Не закрыть
resp.Body— утечка соединений. - Использовать
strings.NewReaderс нестроковыми данными — он принимает только строки. - Не обрабатывать ошибки — любая операция с сетью может провалиться.
Альтернативный подход: использование `json.NewEncoder`
Вместо json.Marshal + bytes.NewBuffer можно использовать json.NewEncoder, который сразу записывает JSON в поток. Это особенно удобно, когда нужно отправить данные напрямую в тело запроса без промежуточного буфера:
func sendUserDirect(user User) error {
// Создаём канал для передачи данных
pr, pw := io.Pipe()
go func() {
defer pw.Close()
if err := json.NewEncoder(pw).Encode(user); err != nil {
fmt.Printf("Ошибка кодирования: %v\n", err)
}
}()
resp, err := http.Post("https://api.example.com/users", "application/json", pr)
if err != nil {
return err
}
defer resp.Body.Close()
// Обработка ответа...
return nil
}
Этот метод полезен для потоковой передачи больших JSON-объектов, так как не требует хранения всего JSON в памяти. Однако для большинства случаев первый вариант (с json.Marshal) проще и надёжнее.
Ещё один совет: если структура содержит поля, которые могут быть nil, используйте указатели или опциональные типы. Например:
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"`
}
Тег omitempty исключает пустые поля из JSON, а указатель позволяет различать отсутствие поля и пустое значение.
Ответов пока нет.
Войдите чтобы оставить ответ