Bir GET isteği
Bu sayfadaki her örnek httptest.NewServer ile kendi sunucusunu başlatır ve onu localhost üzerinde gerçek bir TCP bağlantısıyla çağırır. srv.URL yerine https://api.example.com gibi gerçek bir adres koyun, istemci kodu değişmez.
Burada görünen üç kural:
- Önce
err'i kontrol edin. Hiçbir yanıt alınamamasına yol açan başarısızlıkları bildirir: DNS hataları, reddedilen bağlantılar, timeout'lar, TLS sorunları. - Hata kontrolünün hemen ardından
defer resp.Body.Close(). Gövde bağlantıyı açık tutar. Kapatılmamış bir gövde bağlantıyı sızdırır. - Gövdeyi okuyun ya da çözün. Metin için
io.ReadAll, JSON içinjson.NewDecoder(resp.Body).Decode(&v).
Durum kodları hata değildir
http.Get, 404 ve 500 dahil sunucunun gönderdiği her yanıt için err == nil döndürür. Durumu kendiniz kontrol etmeniz gerekir:
io.LimitReader bir hata gövdesinin ne kadarını okuyacağınızı sınırlar, böylece hatalı davranan bir sunucu sizi bir hata mesajına megabaytlarca veri yüklemeye zorlayamaz. Birçok API başarı için 201, 202 ya da 204 döndürür; herhangi bir 2xx yeterliyse resp.StatusCode >= 200 && resp.StatusCode < 300'ü kontrol edin.
Timeout'lar: gerçek trafik için asla varsayılan istemciyi kullanmayın
http.Get, http.Post ve http.DefaultClient'ın genel bir timeout'u yoktur (varsayılan transport yalnızca bağlanmayı ve TLS el sıkışmasını sınırlar). Bağlantıyı kabul edip hiç yanıt vermeyen bir sunucu goroutine'inizi sonsuza kadar bloklar. Kendi istemcinizi oluşturun:
Client.Timeout tüm alışverişi kapsar: bağlanmak, yönlendirmeler ve gövdeyi okumak. Döndürdüğü hata, Timeout() == true bildiren bir *url.Error'dır ve errors.As onu net.Error interface'i üzerinden bulur. Bir istemci oluşturun ve her yerde yeniden kullanın; bir http.Client eşzamanlı kullanım için güvenlidir. Bağlantı havuzu Client'ta değil Transport'ta yaşar: istek başına yeni bir http.Client{} yine http.DefaultTransport'u ve onun havuzunu paylaşır, ama istek başına yeni bir http.Transport her seferinde boş bir havuzla başlar.
Context ve header'lı istekler
http.NewRequestWithContext, client.Do ile göndermeden önce özelleştirebileceğiniz bir istek oluşturur. Context, istemcinin timeout'unun üzerine istek başına bir deadline ya da iptal ekler ve bir sunucuda giden çağrıları gelen bir isteğin ömrüne bağlar.
Kullanıcı girdisini bir URL'e birleştirmek ("?q=" + q), &, # ve boşluklarda bozulur. url.Values ve onun Encode metodu kaçışlamayı yapar ve anahtarları sıralar. Yol parçaları için url.PathEscape kullanın.
JSON POST
client.Post(url, contentType, body), o Content-Type header'ına sahip bir isteğin kısayoludur. Başka her şey için (bir auth header'ı, PUT, PATCH, DELETE) metotla birlikte http.NewRequestWithContext ve client.Do kullanın. Form gönderimleri için client.PostForm(url, url.Values{...}) değerleri kodlar ve application/x-www-form-urlencoded ayarlar.
Bağlantı yeniden kullanımı
İstemci bir TCP bağlantısını yalnızca önceki gövdeyi sonuna kadar okuyup kapattığınızda yeniden kullanır. Okumayı erken bırakırsanız (örneğin bir hata durumundan sonra), aynı host'a çok istek yapıyorsanız kapatmadan önce gövdeyi boşaltın:
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
Çok büyük olabilecek gövdeler için bunun yerine io.LimitReader üzerinden boşaltın ya da bağlantıyı kaybetmeyi kabul edin.
Yönlendirmeler
İstemci en fazla 10 yönlendirmeyi otomatik olarak izler ve resp.Request.URL nerede son bulduğunuzu söyler. İlk yönlendirmede durmak için, örneğin bir Location header'ını okumak için, CheckRedirect'i ayarlayın:
client := &http.Client{
Timeout: 10 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse // return the 3xx response itself
},
}
Sık yapılan hatalar
- Production'da
http.Getya dahttp.DefaultClientkullanmak. Timeout yok.Timeout'lu birhttp.Clientoluşturun. resp.Body.Close()'u unutmak. İstekler başarısız olmaya başlayana kadar bağlantılar sızar.err'i kontrol etmeden gövdeyi kapatmak.errnildeğilkenrespnil'dir veresp.Body.Close()panic olur.err == nil'i başarı saymak.resp.StatusCode'u kontrol edin.- İstek başına yeni bir
http.Transportoluşturmak. Her transport'un kendi bağlantı havuzu vardır, bu yüzden her istek yeni bir bağlantı (ve yeni bir TLS el sıkışması) açar. Genellikle paylaşılan tek birhttp.Clientüzerinden tek bir transport paylaşın. - Sorgu string'lerini elle oluşturmak.
url.Valueskullanın.
Sıkça Sorulan Sorular
Go'da GET isteği nasıl yapılır?
resp, err := http.Get(url), err'i kontrol edin, sonra defer resp.Body.Close() yazın ve gövdeyi io.ReadAll(resp.Body) ile okuyun ya da json.NewDecoder(resp.Body).Decode(&v) ile çözün. resp.StatusCode'u da kontrol edin: bir 404 ya da 500, http.Get'ten gelen bir hata değildir.
Go'da bir HTTP isteğine timeout nasıl ayarlanır?
Timeout'lu bir istemci oluşturun, client := &http.Client{Timeout: 10 * time.Second}, ve genel bir timeout'u olmayan http.Get ya da http.DefaultClient yerine onu kullanın: bağlantıyı kabul edip hiç yanıt vermeyen bir sunucu çağrıyı sonsuza kadar bloklar. İstek başına bir sınır için isteği http.NewRequestWithContext ve context.WithTimeout'tan gelen bir context ile oluşturun.
Go'da JSON gövdeli bir POST isteği nasıl gönderilir?
Değeri marshal edin, sonra gönderin: body, _ := json.Marshal(v) ve http.Post(url, "application/json", bytes.NewReader(body)). Authorization gibi header'lar eklemek için isteği http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)) ile oluşturun, req.Header.Set("Content-Type", "application/json") ayarlayın ve client.Do(req) ile gönderin.
Go'da resp.Body'yi neden kapatmam gerekiyor?
Gövde açık bir bağlantı üzerindeki bir akıştır. Onu kapatmak bağlantıyı yeniden kullanım için havuza geri verir; unutmak, program sınırına ulaşana kadar bağlantıları ve dosya tanımlayıcılarını sızdırır. Okumasanız bile kapatın ve bağlantının yeniden kullanılmasını istiyorsanız sonuna kadar okuyun.