Рассмотрим этот пример для выполнения HTTP-запроса в Go с базовой аутентификацией c:
package main
import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
)
var userName = "myUserName"
var password = "myPassword"
func main() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !checkAuth(w, r) {
http.Error(w, "You're not authorized!", http.StatusUnauthorized)
return
}
w.Write([]byte("You're authorized!"))
}))
defer ts.Close()
req, err := http.NewRequest("GET", ts.URL, nil)
check(err)
req.SetBasicAuth(userName, password+"foo")
resp, err := http.DefaultClient.Do(req)
check(err)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
check(err)
fmt.Println(string(body))
}
// checkAuth checks authentication (cf. https://stackoverflow.com/questions/21936332/idiomatic-way-of-requiring-http-basic-auth-in-go/21937924#21937924)
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 {
return false
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
return false
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
return false
}
return pair[0] == userName && pair[1] == password
}
func check(err error) {
if err != nil {
panic(err)
}
}
Обратите внимание, что SetBasicAuth
- это метод *http.Request
, поэтому, если я хочу сделать много запросов, мне придется вызывать этот метод для каждого запроса.
В Python вы можете определить requests.Session
, как в этом примере (из https://requests.readthedocs.io/en/master/user/advanced/#session -объектов ):
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# both 'x-test' and 'x-test2' are sent
s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})
Существует ли идиоматический c способ определения эквивалента requests.Session
в Go (предпочтительно с использованием стандартной библиотеки)? Все, что я могу придумать, - это определить собственную клиентскую структуру с помощью собственного метода Do()
:
type MyClient struct {
UserName, Password string
}
func (client *MyClient) Do(req *http.Request) (*http.Response, error) {
req.SetBasicAuth(client.UserName, client.Password)
return http.DefaultClient.Do(req)
}
и вызвать ее в приведенном выше сценарии, например
client := MyClient{UserName: userName, Password: password}
resp, err := client.Do(req)
Будет ли это идиоматизмом? c способ избежать нескольких звонков на SetBasicAuth()
?