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