Setiap kali browser menampilkan sebuah halaman web atau aplikasi memanggil API publik, protokol HTTP bekerja sebagai kurir pesan antara klien dan server. Package net/http milik Go menyediakan seluruh peralatan untuk berperan sebagai klien maupun server, tetapi peralatan tersebut jauh lebih mudah dikuasai setelah anatomi protokolnya jelas. Pada chapter ini kita akan membedah URL, request, response, status code, dan sifat stateless HTTP sebagai fondasi seluruh section. Seluruh contoh kode dijalankan secara lokal memakai package net/http/httptest, sehingga Anda tidak memerlukan koneksi internet untuk mengikutinya.
Anatomi URL #
Permintaan HTTP selalu ditujukan ke sebuah alamat berbentuk URL. Sebuah URL memiliki empat bagian utama yang perlu Anda kenali sejak awal:
https://api.example.com:8443/v1/items?halaman=2&tag=go| Bagian | Nama | Peran |
|---|---|---|
https | skema (scheme) | protokol yang dipakai; https berarti koneksi terenkripsi TLS |
api.example.com:8443 | host | alamat server beserta port tujuan |
/v1/items | path | lokasi sumber daya yang diminta di server |
halaman=2&tag=go | query | parameter tambahan berupa pasangan kunci=nilai |
Package net/url dari standard library mampu membedah string URL menjadi struct *url.URL yang siap dipakai. Struct tersebut menyimpan field Scheme, Host, Path, dan RawQuery, sementara method Query() mengembalikan nilai bertipe url.Values — sebuah map dengan tipe map[string][]string — sehingga satu parameter query boleh muncul lebih dari satu kali.
Anatomi Request HTTP #
Sebuah request HTTP terdiri dari tiga bagian: baris permintaan (request line), kumpulan header, dan body yang bersifat opsional. Bentuk mentah sebuah request GET tampak seperti berikut:
GET /v1/items?halaman=2 HTTP/1.1
Host: api.example.com
Accept: application/jsonBaris pertama memuat method, path beserta query, lalu versi protokol. Setelah baris tersebut, header dikirim sebagai pasangan Kunci: Nilai hingga satu baris kosong menjadi penanda akhir header. Method menentukan maksud permintaan:
| Method | Peran umum | Body request |
|---|---|---|
GET | membaca data | umumnya kosong |
POST | mengirim data baru untuk diproses | ada |
PUT | mengganti data secara utuh | ada |
PATCH | mengubah sebagian data | ada |
DELETE | menghapus data | umumnya kosong |
Header Content-Type pada request memberi tahu server format body yang dikirim, misalnya application/json untuk data JSON atau application/x-www-form-urlencoded untuk data form HTML.
Status Code dan Anatomi Response #
Server membalas setiap request dengan response yang terdiri dari status line, header, dan body. Bentuk mentah response yang berhasil tampak seperti ini:
HTTP/1.1 200 OK
Content-Type: application/json
[{"id":1,"nama":"Buku Go"}]Status code adalah angka tiga digit yang merangkum hasil request. Lima kelasnya perlu dihafal:
| Kelas | Arti | Contoh anggota |
|---|---|---|
1xx | informasi protokol, bukan hasil akhir | 101 Switching Protocols |
2xx | request berhasil | 200 OK, 201 Created |
3xx | pengalihan ke lokasi lain | 301 Moved Permanently, 304 Not Modified |
4xx | kesalahan pada sisi klien | 400 Bad Request, 404 Not Found, 405 Method Not Allowed |
5xx | kesalahan pada sisi server | 500 Internal Server Error, 503 Service Unavailable |
Header Content-Type pada response memberi tahu klien cara membaca body, misalnya application/json atau text/html. Handler Go yang tidak mengatur header ini akan mendeteksi tipe konten secara otomatis dari isi body.
HTTP Bersifat Stateless #
Protokol HTTP bersifat stateless: setiap request berdiri sendiri dan server tidak menyimpan ingatan tentang request sebelumnya. Request kedua dari klien yang sama diperlakukan seperti request dari orang asing. Karena sifat ini, aplikasi web mengirim identitas secara berulang pada setiap request, biasanya lewat header Authorization berisi token atau lewat cookie yang disimpan klien.
Siklus Request dan Response #
Satu putaran lengkap HTTP berjalan sebagai berikut: klien membangun baris permintaan, header, dan body; server menerima request tersebut, handler menulis response; klien membaca response; kemudian koneksi ditutup atau dipakai ulang untuk request berikutnya.
Program: Membedah URL dan Menjalankan Satu Siklus #
Berkas dasar-http-demo.go pada bagian bawah halaman menggabungkan seluruh materi chapter ini: bagian pertama membedah sebuah URL dengan net/url, bagian kedua menjalankan satu siklus request-response di dalam memori.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
)
func main() {
// Bagian 1: membedah anatomi sebuah URL dengan package net/url.
alamat := "https://api.example.com:8443/v1/items?halaman=2&tag=go"
u, err := url.Parse(alamat)
if err != nil {
fmt.Println("gagal mem-parse URL:", err)
return
}
fmt.Println("URL :", alamat)
fmt.Println("skema :", u.Scheme)
fmt.Println("host :", u.Host)
fmt.Println("path :", u.Path)
fmt.Println("query :", u.RawQuery)
fmt.Println("halaman :", u.Query().Get("halaman"))
// Bagian 2: satu siklus request-response tanpa jaringan nyata.
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Halo, %s!", r.URL.Query().Get("nama"))
})
req := httptest.NewRequest(http.MethodGet, "/sapa?nama=Budi", nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
fmt.Println("request :", req.Method, req.RequestURI, req.Proto)
fmt.Println("respons :", rec.Code, rec.Header().Get("Content-Type"))
fmt.Println("body :", rec.Body.String())
}Jalankan berkas tersebut dengan perintah berikut dari direktori chapter:
go run dasar-http-demo.goOutput yang Dihasilkan:
URL : https://api.example.com:8443/v1/items?halaman=2&tag=go
skema : https
host : api.example.com:8443
path : /v1/items
query : halaman=2&tag=go
halaman : 2
request : GET /sapa?nama=Budi HTTP/1.1
respons : 200 text/plain; charset=utf-8
body : Halo, Budi!Mari kita telaah baris demi baris program di atas:
- Baris 13 (
u, err := url.Parse(alamat)): mem-parse string URL menjadi struct*url.URL; error dikembalikan apabila format alamat tidak valid. - Baris 19-23 (
fmt.Println("skema ...dst.): mencetak fieldu.Scheme,u.Host,u.Path, danu.RawQuerysatu per satu, laluu.Query().Get("halaman")mengambil nilai pertama parameterhalamandari query. - Baris 26-28 (
handler := http.HandlerFunc(...)): membungkus fungsi anonim menjadi handler; fungsi tersebut membaca parameter querynamalalu menulis sapaan kew. - Baris 29-30 (
req := httptest.NewRequest(...),rec := ...): membuat request tiruanGET /sapa?nama=Budibeserta perekam response yang masih kosong. - Baris 32 (
handler.ServeHTTP(rec, req)): menjalankan handler; selama panggilan ini handler membacareqdan menulis status, header, serta body kerec. - Baris 34-36 (
fmt.Println("request ...")dst.): mencetak baris permintaan (req.Method,req.RequestURI,req.Proto), status code hasil perekaman, headerContent-Typeyang terdeteksi otomatis, dan body hasil perekaman.
Dengan anatomi protokol yang sudah jelas, kita siap berdiri di kedua sisi komunikasi. Chapter berikutnya memulai dari sisi pengirim: mengirim request dengan http.Client dan membaca response dengan rapi.
Referensi #
- Dokumentasi package net/url
— referensi resmi struct
url.URLdanurl.Values. - Dokumentasi package net/http — paket inti client dan server HTTP di Go.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
dasar-http-demo.go
Unduh berkaspackage main
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
)
func main() {
// Bagian 1: membedah anatomi sebuah URL dengan package net/url.
alamat := "https://api.example.com:8443/v1/items?halaman=2&tag=go"
u, err := url.Parse(alamat)
if err != nil {
fmt.Println("gagal mem-parse URL:", err)
return
}
fmt.Println("URL :", alamat)
fmt.Println("skema :", u.Scheme)
fmt.Println("host :", u.Host)
fmt.Println("path :", u.Path)
fmt.Println("query :", u.RawQuery)
fmt.Println("halaman :", u.Query().Get("halaman"))
// Bagian 2: satu siklus request-response tanpa jaringan nyata.
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Halo, %s!", r.URL.Query().Get("nama"))
})
req := httptest.NewRequest(http.MethodGet, "/sapa?nama=Budi", nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
fmt.Println("request :", req.Method, req.RequestURI, req.Proto)
fmt.Println("respons :", rec.Code, rec.Header().Get("Content-Type"))
fmt.Println("body :", rec.Body.String())
}