Menu

HTTP Client di Golang: GET, POST JSON, Timeout, dan Header

Cara mengirim HTTP request di Go dengan net/http: http.Get, membaca dan menutup body, memeriksa kode status, timeout di http.Client, request dengan context dan header, query parameter, dan mengirim JSON lewat POST.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Request GET

Setiap contoh di halaman ini menjalankan servernya sendiri dengan httptest.NewServer dan memanggilnya lewat koneksi TCP sungguhan di localhost. Ganti srv.URL dengan alamat asli seperti https://api.example.com maka kode client-nya tidak berubah sama sekali.

Tiga aturan yang terlihat di sini:

  1. Periksa err lebih dulu. Nilai ini melaporkan kegagalan mendapatkan response apa pun: error DNS, koneksi ditolak, timeout, masalah TLS.
  2. defer resp.Body.Close() tepat setelah pemeriksaan error. Body menahan koneksi tetap terbuka. Body yang tidak ditutup membuat koneksinya bocor.
  3. Baca atau decode body-nya. io.ReadAll untuk teks, json.NewDecoder(resp.Body).Decode(&v) untuk JSON.

Kode status bukan error

http.Get mengembalikan err == nil untuk response apa pun yang dikirim server, termasuk 404 dan 500. Kamu harus memeriksa statusnya sendiri:

io.LimitReader membatasi seberapa banyak body error yang kamu baca, jadi server yang bermasalah tidak bisa membuatmu memuat data berukuran megabyte ke dalam pesan error. Banyak API mengembalikan 201, 202, atau 204 untuk sukses; periksa resp.StatusCode >= 200 && resp.StatusCode < 300 kalau semua kode 2xx boleh.

Timeout: jangan pakai client default untuk trafik sungguhan

http.Get, http.Post, dan http.DefaultClient tidak punya timeout keseluruhan (transport default hanya membatasi proses dial dan TLS handshake). Server yang menerima koneksi tapi tidak pernah menjawab akan memblokir goroutine-mu selamanya. Buat client sendiri:

Client.Timeout mencakup seluruh pertukaran: membuka koneksi, redirect, dan membaca body. Error yang dikembalikannya adalah *url.Error yang melaporkan Timeout() == true, yang bisa ditemukan errors.As lewat interface net.Error. Buat satu client dan pakai ulang di mana saja; http.Client aman dipakai secara concurrent. Pool koneksinya ada di Transport, bukan di Client: http.Client{} baru untuk setiap request tetap berbagi http.DefaultTransport beserta pool-nya, tetapi http.Transport baru untuk setiap request selalu dimulai dengan pool kosong.

Request dengan context dan header

http.NewRequestWithContext membuat request yang bisa kamu atur dulu sebelum dikirim dengan client.Do. Context memberi deadline atau pembatalan per request di atas timeout milik client, dan mengikat pemanggilan keluar ke umur request yang masuk di sebuah server.

Menyambung input pengguna langsung ke URL ("?q=" + q) akan rusak saat bertemu &, #, dan spasi. url.Values dan method Encode-nya melakukan escaping sekaligus mengurutkan key. Untuk segmen path, pakai url.PathEscape.

POST JSON

client.Post(url, contentType, body) adalah jalan pintas untuk request dengan header Content-Type tersebut. Untuk kebutuhan lain (header auth, PUT, PATCH, DELETE), pakai http.NewRequestWithContext dengan method yang sesuai, lalu client.Do. Untuk mengirim form, client.PostForm(url, url.Values{...}) meng-encode nilainya dan mengatur application/x-www-form-urlencoded.

Pemakaian ulang koneksi

Client hanya memakai ulang koneksi TCP kalau kamu membaca body sebelumnya sampai habis lalu menutupnya. Kalau kamu berhenti membaca lebih awal (misalnya setelah mendapat status error), kosongkan dulu body-nya sebelum ditutup saat kamu mengirim banyak request ke host yang sama:

defer func() {
	io.Copy(io.Discard, resp.Body)
	resp.Body.Close()
}()

Untuk body yang bisa sangat besar, kosongkan lewat io.LimitReader, atau terima saja koneksinya hilang.

Redirect

Client otomatis mengikuti sampai 10 redirect, dan resp.Request.URL memberi tahu di mana kamu akhirnya berada. Untuk berhenti di redirect pertama, misalnya untuk membaca header Location, atur CheckRedirect:

client := &http.Client{
	Timeout: 10 * time.Second,
	CheckRedirect: func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse // return the 3xx response itself
	},
}

Kesalahan umum

  • Memakai http.Get atau http.DefaultClient di production. Tidak ada timeout. Buat http.Client dengan Timeout.
  • Lupa resp.Body.Close(). Koneksi terus bocor sampai request mulai gagal.
  • Menutup body sebelum memeriksa err. Saat err tidak nil, resp bernilai nil dan resp.Body.Close() akan panic.
  • Menganggap err == nil berarti sukses. Periksa resp.StatusCode.
  • Membuat http.Transport baru untuk setiap request. Setiap transport punya pool koneksi sendiri, jadi setiap request membuka koneksi baru (dan TLS handshake baru). Bagikan satu transport, biasanya lewat satu http.Client bersama.
  • Menyusun query string secara manual. Pakai url.Values.

Pertanyaan yang sering diajukan

Bagaimana cara mengirim request GET di Go?

resp, err := http.Get(url), periksa err, lalu defer resp.Body.Close() dan baca body dengan io.ReadAll(resp.Body) atau decode dengan json.NewDecoder(resp.Body).Decode(&v). Periksa juga resp.StatusCode: 404 atau 500 bukan error dari http.Get.

Bagaimana cara mengatur timeout pada HTTP request di Go?

Buat client dengan timeout, client := &http.Client{Timeout: 10 * time.Second}, lalu pakai client itu sebagai ganti http.Get atau http.DefaultClient, yang tidak punya timeout keseluruhan: server yang menerima koneksi tapi tidak pernah menjawab akan memblokir pemanggilan selamanya. Untuk batas per request, buat request dengan http.NewRequestWithContext dan context dari context.WithTimeout.

Bagaimana cara mengirim request POST dengan body JSON di Go?

Marshal nilainya, lalu kirim: body, _ := json.Marshal(v) dan http.Post(url, "application/json", bytes.NewReader(body)). Untuk menambahkan header seperti Authorization, buat request dengan http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)), atur req.Header.Set("Content-Type", "application/json"), lalu kirim dengan client.Do(req).

Kenapa resp.Body harus ditutup di Go?

Body adalah stream di atas koneksi yang masih terbuka. Menutupnya mengembalikan koneksi ke pool agar bisa dipakai lagi; kalau lupa, koneksi dan file descriptor bocor sampai program mencapai batasnya. Tutup body walaupun kamu tidak membacanya, dan baca sampai habis kalau kamu ingin koneksinya dipakai ulang.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai