Golang: Работы с HTTP запросами часть 3
В предыдущих статьях мы разобрались как отправлять GET/POST запросы и рассмотрели как сделать настройку модуля Client для отправки запросов. В этой статье мы рассмотрим как предавать файлы средствами net/http.
Для загрузки файлов при отправке HTTP-запроса нам нужно использовать пакет mime/multipart с пакетом net/http. Сначала мы увидим пример кода, а затем поясним его, чтобы понять, что мы делаем.
func MakeRequest() {
// Открываем файл
file, err := os.Open("name.txt")
if err != nil {
log.Fatalln(err)
}
// Закрываем файл по завершению
defer file.Close()
// Буфер для хранения нашего тела запроса в виде байтов
var requestBody bytes.Buffer
// Создаем писателя
multiPartWriter := multipart.NewWriter(&requestBody)
// Инициализируем поле
fileWriter, err := multiPartWriter.CreateFormFile("file_field", "name.txt")
if err != nil {
log.Fatalln(err)
}
// Скопируйте содержимое файла в поле
_, err = io.Copy(fileWriter, file)
if err != nil {
log.Fatalln(err)
}
// Заполняем остальные поля
fieldWriter, err := multiPartWriter.CreateFormField("normal_field")
if err != nil {
log.Fatalln(err)
}
_, err = fieldWriter.Write([]byte("Value"))
if err != nil {
log.Fatalln(err)
}
// Закрываем запись данных
multiPartWriter.Close()
// Создаем объект реквеста
req, err := http.NewRequest("POST", "https://httpbin.org/post", &requestBody)
if err != nil {
log.Fatalln(err)
}
// Получаем и устанавливаем тип контента
req.Header.Set("Content-Type", multiPartWriter.FormDataContentType())
// Отправляем запрос
client := &http.Client{}
response, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}
var result map[string]interface{}
json.NewDecoder(response.Body).Decode(&result)
log.Println(result)
}
Итак, что мы здесь делаем?
- Сначала мы открываем файл, который хотим загрузить. В нашем случае я создал файл с именем «name.txt», который просто содержит мое имя.
- Мы создаем bytes.Buffer для хранения тела запроса, который мы передадим позже с нашим http.Request.
- Мы создаем объект multipart.Writer и передаем указатель на наш файл bytes.Buffer.
- Мы создаем поле файла и копируем его содержимое. Затем мы создаем нормальное поле и записываем в него «Value».
- После того, как мы добавили наши данные, мы вызываем метод Close для объекта multiPartWriter.
- Мы создаем новый POST запрос, как мы видели раньше. Мы передали bytes.Buffer, который мы создали как тело запроса. Тело теперь содержит данные формы нескольких полей, написанные с помощью пакета mime/multipart.
- Отправляем запрос как и ранее. Но мы устанавливаем тип контента, вызывая multiPartWriter.FormDataContentType() - который обеспечивает правильный тип контента.
- Мы декодируем ответ от httpbin и проверяем вывод.
Всегда закрывать тело ответа
Вот урок, который я усвоил. Когда мы делаем HTTP-запрос, мы получаем ответ и ошибку. Мы можем залениться и решать не проверять ошибки или не закрывать тело ответа (как в приведенных выше примерах). И это в последствие может вызвать много проблем. Если мы не закрываем тело ответа, соединение может оставаться открытым и вызывать утечку ресурсов. Но если ошибка не равна нулю, то в случае ошибки ответ может быть равен нулю. Таким образом, мы не можем просто сделать defer resp.Body.Close(). Мы должны проверить ошибку, а затем закрыть тело ответа.
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
Всегда использовать таймаут
Попробуйте использовать свой собственный клиент http и установите тайм-аут. Не настраивая тайм-аут вы можете блокировать соединение и goroutine и, следовательно, вызывать хаос. Итак, сделайте что-нибудь вроде этого:
timeout := time.Duration(5 * time.Second)
client := http.Client{
Timeout: timeout,
}
client.Get(url)
-
Golang: Работы с HTTP запросами часть 3