response, err := http.Get("http://example.com/index.html")
if err != nil {
fmt.Println(err)
} http.Get(url string) возвращает указатель на структуру http.Response (ответ сервера) и error (ошибку, если что-то пошло не так). В URL-параметре указывают HTTP- или HTTPS-протокол.http.Response содержит заголовки ответа, тело и дополнительную информацию. В уроке «Создание HTTP-сервера» вы научились устанавливать кода статуса и заголовки ответа. Эти данные хранятся в полях StatusCode и Header соответственно. practicum.yandex.ru:package main
import (
"fmt"
"net/http"
)
func main() {
response, err := http.Get("https://practicum.yandex.ru")
if err != nil {
fmt.Println(err)
}
fmt.Printf("Status Code: %d\r\n", response.StatusCode)
for k, v := range response.Header {
// заголовок может иметь несколько значений,
// но для простоты запросим только первое
fmt.Printf("%s: %v\r\n", k, v[0])
}
} Status Code: 200
X-Request-Id: 1663854943634850-3617306141716402244
Date: Thu, 22 Sep 2022 13:55:45 GMT
Set-Cookie: _yasc=6xwp8F8yEKot/1Zso72AZeQrdloTNG4Cl/s+EnMNncw3og==; domain=.yandex.ru; path=/; expires=Sat, 22-Oct-2022 13:55:43 GMT; secure
Vary: Accept-Encoding
Etag: W/"742df-+H+2A9iCP7aCMRNI7y9HsHaKzfI"
Keep-Alive: timeout=5
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Powered-By: Express
Content-Security-Policy:
Content-Type: text/html; charset=utf-8
Report-To: {"group":"default-group","endpoints":[{"url":"https://csp.yandex.net/csp?from=wirth&project=practicum"}],"max_age":1800,"include_subdomains":true} Content-Type: text/html; charset=utf-8. (h Header) Values(key string) []string, а самое первое значение — методом (h Header) Get(key string) string.contentType := response.Header.Get("Content-Type")
// это может быть, например, "application/json; charset=UTF-8" Response.Body интерфейсом потокового чтения io.ReadCloser, который содержит два метода: Read(p []byte) (n int, err error) (интерфейс Reader) и Close() error (интерфейс Closer). Если клиент успешно получил ответ, то нужно закрыть Body независимо от того, прочитаете вы его или нет. []byte можно функцией io.ReadAll(r Reader) ([]byte, error):response, err := http.Get("http://example.com")
if err != nil {
fmt.Println(err)
return
}
body, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil {
fmt.Println(err)
return
} Content-Type. Если, например, Content-Type равен text/html; charset=utf-8, это значит, что тело ответа содержит текст в формате HTML и кодировке UTF-8. POST, можно использовать функцию Post(url, contentType string, body io.Reader) (resp *Response, err error).data := `{"name": "Иванов Иван", "email": "ivan@example.com"}`
resp, err := http.Post("https://example.com/adduser", "application/json", strings.NewReader(data))
if err != nil {
return err
} Content-Type.http.Client, а функции http.Get() и http.Post() — обёртки над вызовом этих методов у глобальной переменной http.DefaultClient.http.Client можно обратиться к серверу и получить http.Response:client := &http.Client{}
response, err := client.Get("https://golang.org") Timeout time.Duration — максимальная задержка ответа, после которой клиент отменяет запрос. Если это поле имеет нулевое значение, то лимит на ожидание не установлен. client := http.Client{
Timeout: time.Second * 1, // интервал ожидания: 1 секунда
} http://example.com → https://example.com → https://www.example.com. По умолчанию клиент переходит только по 10 адресам. Поле CheckRedirect func(req *Request, via []*Request) error позволяет контролировать редиректы. client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
fmt.Println(req.URL)
return nil
},
}
response, err := client.Get("http://ya.ru") http://ya.ru программа может вывести:https://ya.ru/
https://ya.ru/?nr=1 https://ya.ru/showcaptcha?cc=1&....RoundTripper. Стандартная библиотека предоставляет реализацию этого интерфейса структурой http.Transport, где можно указать тайм-ауты соединения и другие низкоуровневые настройки. http.DefaultTransport. При необходимости можно задать свой вариант реализации через поле http.Client.Transport. http.Transport позволяет переиспользовать уже открытые TCP-подключения (их называют HTTP keep-alive), поэтому http.Client обычно применяют повторно, а не создают заново для каждого запроса. К тому же один экземпляр клиента можно параллельно использовать из разных горутин.response.Body, даже если оно не нужно.// io.Discard выступает в качестве приёмника ненужных данных
_, err := io.Copy(io.Discard, response.Body)
response.Body.Close()
if err != nil {
fmt.Println(err)
} http.Get() переиспользовать один клиент?http.DefaultClient.http.Get() использует клиент по умолчанию http.DefaultClient.client.Get() или client.Post(), то можно сформировать нужный запрос *http.Request и отправить его с помощью метода (c *Client) Do(req *Request) (*Response, error).NewRequest(method, url string, body io.Reader) (*Request, error), где method — имя метода запроса, url — адрес. Третьим параметром указывается переменная интерфейсного типа io.Reader, из которой будет прочитано тело запроса. Если тело запроса отправлять не нужно (например, для метода GET), можно указывать значение nil.client.Do():request, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
if err != nil {
panic(err)
}
response, err := client.Do(request)
if err != nil {
panic(err)
}
io.Copy(os.Stdout, response.Body) // вывод ответа в консоль
response.Body.Close() *http.Request можно установить значения заголовков через поле Header типа http.Header. Чтобы задать содержимое заголовка, используют один из двух методов:(h Header) Set(key, value string) — установить значение заголовка;(h Header) Add(key, value string) — добавить значение заголовка к уже существующим.MyHeader c двумя значениями — Hello и Привет.req, err := http.NewRequest(http.MethodGet, `http://localhost:8080`, nil)
if err != nil {
panic(err)
}
req.Header.Set(`MyHeader`, "Hello")
req.Header.Add(`MyHeader`, "Привет")
response, err := client.Do(req) POST-запроса с часто встречающимися заголовками "Content-Type"."application/json"io.Reader используется bytes.Buffer, а "Content-Type" равен "application/json". Чаще всего этот подход можно встретить в современных REST API, где запросы и ответы передаются в JSON-формате.var body = []byte(`{"message":"Hello"}`)
request, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
if err != nil {
// обрабатываем ошибку
}
request.Header.Set("Content-Type", "application/json; charset=UTF-8")
response, err := client.Do(request) "multipart/form-data"file, _ := os.Open(filename) // открываем файл
defer file.Close() // не забываем закрыть
body := &bytes.Buffer{} // создаём буфер
// на основе буфера конструируем multipart.Writer из пакета mime/multipart
writer := multipart.NewWriter(body)
// готовим форму для отправки файла на сервер
part, err := writer.CreateFormFile("uploadfile", filename)
if err != nil {
// обрабатываем ошибку
}
// копируем файл в форму
// multipart.Writer отформатирует данные и запишет в предоставленный буфер
_, err = io.Copy(part, file)
if err != nil {
// обрабатываем ошибку
}
writer.Close()
// пишем запрос
request, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
// обрабатываем ошибку
}
// добавляем заголовок запроса
request.Header.Set("Content-Type", writer.FormDataContentType())
response, err := client.Do(request) "application/x-www-form-urlencoded"ключ=значение. Данные кодируются в виде строки key1=value1&key2=value2. При GET-запросе параметры указываются после адреса: example.com/endpoint?key1=value1&key2=value2. Этот формат кодировки по умолчанию используется в браузерах.// готовим контейнер для данных
// используем тип url.Values из пакета net/url
data := url.Values{}
// устанавливаем данные
data.Set("key1", "value1")
data.Set("key2", "value2")
// пишем запрос
request, err := http.NewRequest(http.MethodPost, url, strings.NewReader(data.Encode()))
if err != nil {
// обрабатываем ошибку
}
// устанавливаем заголовки
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("Content-Length", strconv.Itoa(len(data.Encode())))
response, err := client.Do(request) http.Cookie. Вот её обязательные поля:Name string — имя куки.Value string — значение куки.Path string — путь URL, начиная с которого распространяется действие куки.Domain string — хост. Если он не задан, то берётся доменная часть адреса.Expires time.Time — дата и время, когда истекает срок действия куки. После этого кука удаляется.MaxAge int — время жизни куки в секундах. Если значение равно 0, то атрибут не указан. Это альтернатива полю Expires.Secure bool — если true, то кука будет доступна только по протоколу HTTPS.HttpOnly bool — если true, то кука будет недоступна для чтения из JavaScript, при этом браузер всё равно будет отправлять её на сервер.(r *Request) Cookie(name string) (*Cookie, error) — получить структуру куки с указанным именем.(r *Request) Cookies() []*Cookie — получить список всех кук.http.SetCookie(w ResponseWriter, cookie *Cookie) — установить куку в ответ сервера.(r *Request) AddCookie(c *Cookie) — добавить в запрос нужные куки.(r *Response) Cookies() []*Cookie — получить куки из ответа сервера.client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, `http://localhost:8080`, nil)
if err != nil {
panic(err)
}
req.AddCookie(&http.Cookie{
Name: "ID",
Value: "3675",
})
req.AddCookie(&http.Cookie{
Name: "Token",
Value: "TEST_TOKEN",
MaxAge: 360,
})
response, err := client.Do(req) net/http/cookiejar. Он позволяет автоматически обновлять куки, полученные от сервера, и отправлять их при каждом запросе. Jar в переменной типа http.Client:jar, err := cookiejar.New(nil)
if err != nil {
panic(err)
}
client := &http.Client{
Jar: jar,
}
req, err := http.NewRequest(http.MethodGet, `http://localhost:8080`, nil)
if err != nil {
panic(err)
}
cookie := &http.Cookie{
Name: "Token",
Value: "TEST_TOKEN",
MaxAge: 300,
}
req.AddCookie(cookie)
response, err := client.Do(req) Jar клиент после запроса запомнит куки, полученные от сервера, и добавит их в следующий запрос к этому доменному имени.POST «Сервису сокращения URL» и получить ответ — короткий псевдоним.main.go поддиректории cmd/client вашего проекта. Тогда у вас всегда будет под рукой программа для проверки сервиса.package main
import (
"bufio"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
)
func main() {
endpoint := "http://localhost:8080/"
// контейнер данных для запроса
data := url.Values{}
// приглашение в консоли
fmt.Println("Введите длинный URL")
// открываем потоковое чтение из консоли
reader := bufio.NewReader(os.Stdin)
// читаем строку из консоли
long, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
long = strings.TrimSuffix(long, "\n")
// заполняем контейнер данными
data.Set("url", long)
// добавляем HTTP-клиент
client := &http.Client{}
// пишем запрос
// запрос методом POST должен, помимо заголовков, содержать тело
// тело должно быть источником потокового чтения io.Reader
request, err := http.NewRequest(http.MethodPost, endpoint, strings.NewReader(data.Encode()))
if err != nil {
panic(err)
}
// в заголовках запроса указываем кодировку
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// отправляем запрос и получаем ответ
response, err := client.Do(request)
if err != nil {
panic(err)
}
// выводим код ответа
fmt.Println("Статус-код ", response.Status)
defer response.Body.Close()
// читаем поток из тела ответа
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
// и печатаем его
fmt.Println(string(body))
} net/http я тебя познакомил. Но кроме него, есть множество сторонних библиотек, которые ускоряют разработку сервера и клиента, — им будет посвящён следующий урок.cookiejar.