Ответим на вопрос — как обмениваться данными в Интернете. Посмотрим на протокол HTTP. Узнаем как написать клиент для внешнего сервиса погоды.
Продолжаем рассматривать тему микросервисов. В предыдущем уроке, мы написали небольшую программу для извлечения температуры в определенном городе из структуры, закодированной в формате JSON.
Пример данных мы взяли из открытого источника. В этом нам помог сайт open-meteo.com, предоставляющий открытый API - программный интерфейс.
Теперь пришло время написать решение для автоматизированного сбора погоды. Сделаем мы это в серии из нескольких статей.
Как всегда начнем с небольшой теории. Дадим самую минимально необходимую информацию и ссылки для более подробного изучения. Чтобы написать свое приложение, информации из этой статьи должно хватить.
Клиент — Сервер
В основе современного интернета лежит клиент — серверная архитектура.
В процессе обмена информацией участвуют как минимум два основных компонента — Клиент и Сервер. Чтобы передать информацию между ними компьютер или устройство должно установить Соединение.
Примером соединения может быть телефонный звонок. Два абонента устанавливают голосовое соединение. В рамках соединения передается звуковая информация.
Получателя информации часто называют Клиентом (Client).
Клиент — сторона, создающая запрос на получение информации
Клиент отправляет запрос (request) в котором содержатся определенные данные — Параметры запроса.
Параметры помогают принимающей стороне понять, что именно нужно клиенту.
Важной частью запроса является Адрес ресурса.
Клиентом может выступать домашний компьютер, мобильный телефон, браузер, настольное устройство (умные колонки) и т.п. Все они подключены к сети Интернет и способны передавать запросы.
Таким образом клиент должен инициировать передачу данных и отправить запрос на другой конец.
Приемником запроса называют Сервером (Server).
Сервер — это программно-аппаратный комплекс для обеспечения приема, обработки и распространения данных.
Задача сервера принять входящий запрос. Запрос проходит через несколько процессов. Одним из основных является процесс декодирования запроса. После этого сервер должен выполнить действия, соответствующие запросу.
Например, извлечь данные по погоде в городе.
Эти данные также будут закодированы и переданы обратно клиенту.
Напомню, что основная единица передачи информации это байт. Любая текстовая информация может быть закодирована в поток байтов.
Для кодирования данных, которые идут по сети используются различные протоколы.
Самый популярный протокол передачи текстовой информации на данный момент — протокол HTTP.
HTTP
HyperText Transfer Protocol
Протокол передачи данных прикладного уровня.
Базовый протокол при работе с сайтами и ресурсами в Интернете.
Часто используется для передачи информации между различными системами.
В основе протокола лежит понятие адреса ресурса.
URI
Uniform Resource Identifier
Это строка, которая указывает на то, где может находиться информация в Интернете. URI описывается стандартом rfc3986.
https://datatracker.ietf.org/doc/html/rfc3986
Это строгий документ, описывающий различные особенности формата строки URI. Читать полностью его вероятно не нужно, но пробежаться определенно стоит.
Вначале пути backend разработчика важно ухватить общую картину, прежде чем вдаваться в глубокие детали.
URI - это адресная строка.
Например, это адрес в браузере который вы набираете, чтобы получить данные с сайта.
Или тот адрес, по которому можно получить данные о погоде.
Обратите внимание на префикс http - он означает тот протокол, который используется в запросе.
Адрес указывает на определенный Ресурс. Ресурс может быть представлен текстовой информацией, например веб — страницей или файлом. Фактически в качестве ресурса может выступать любая информация в определенном формате.
Возьмем полный адрес для получения погоды и разберем его на составляющие:
Строка адреса состоит из следующих составных частей:
Scheme - http, https
Протокол передачи данных (схема). В нашем примере https - зашифрованный вариант протокола http.
Host - api.open-meteo.com
Адрес сервера, который предоставляет данные
Path /v1/forecast
Путь до запрашиваемого ресурса
Query
Параметры запроса
Go предоставляет несколько важных пакетов для работы с протоколом http. Давайте посмотрим на пакет net/url.
В пакете net/url есть функции, которые позволяет получить из адреса, каждую составляющую запроса.
В приведенном примере мы воспользовались функцией Parse из пакета net/url для парсинга адресной строки в структуру url.URL.
Структура имеет множество полей и в частности, те которые нас интересуют. Для наглядности мы выводим их на экран.
Параметр Query имеет тип url.Values - это обертка над типом map[string][]string.
Он предоставляет метод Get, с помощью которого можно получить данные по параметрам запроса. Ниже приведена ссылка на песочницу, где можно попробовать код:
Также в протоколе HTTP определяются, так называемые методы (methods).
К основным и часто используемым методам относят GET и POST. Сегодня коснемся только запросов типа GET.
GET
Метод запроса, с помощью которого предоставляют содержимое, запрашиваемого ресурса. Например, для получения информации с Википедии, браузер делает запрос GET.
В Chrome и Firefox есть специальные инструменты, которые позволяют посмотреть различные параметры запроса.
В Chrome можно нажать F12, чтобы открылось окно DeveloperTools.
Там есть вкладка Сеть, которая показывает все запросы, уходящие с выбранной страницы.
Например, по такому адресу находится картинка с логотипом Dzen.
https://dzen.ru/icon-144.png
Каждый запрос имеет так называемые заголовки (Headers):
GET /icon-144.png HTTP/1.1
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate, br, zstd Accept-Language: en,ru;q=0.9,ru-RU;q=0.8,en-US;q=0.7
- Cache-Control: no-cache
- Connection: keep-alive
- Cookie: yandexuid=123;
- Host: dzen.ru
- Pragma: no-cache
Заголовки запроса дают серверу информация о пожеланиях клиента. В качестве эксперимента вы можете скопировать адрес картинки, далее открыть DeveloperTools. Вставьте адрес в строку браузера и посмотрите на отправляемый запрос:
Так выглядит окно Сеть из DeveloperTools.
Подробно заголовки описывать мы не будем. С полным перечнем заголовков можно ознакомится на следующей странице:
Итак, пришло время написать клиент для отправки запроса.
Клиент на Go
Сформулируем цель.
Написать программу для получения погоды по указанным параметрам широты и долготы.
Разобьем задачу на несколько этапов:
- Написание структуры клиента
- Написание метода Forecast для отправки запроса к сервису open-meteo и обработки ответа
Структура клиента
Клиент должен отравлять http запрос по адресу и принимать результат.
Определим структуру Client в следующем виде:
Структура Client будет отправлять запрос в open-meteo. Далее создаем функцию конструктора New. В ней инициализируем начальные значения.
На строке 23 создается http клиент.
Важно указать таймаут для клиента.
Ваше приложение не должно бесконечно ожидать ответа от сервера.
Всегда указывайте таймаут для клиента
Вторым этапом определяем метод для запроса погоды:
Метод Forecast отправляет запрос по адресу api.open-meteo.com и возвращает температуру в виде числа с типом float64.
В параметрах метода передаются широта и долгота, а также временная зона.
Разберем по шагам, что происходит в данном методе:
Вначале мы формируем адрес для запроса с учетом всех параметров: широта, долгота и временная зона.
Далее с помощью метода http.NewRequestWithContext, создаем новый объект http.Request. Запрос имеет тип Get. Третьим параметром передается http.NoBody. Так как запросы типа Get не имеют тела, необходимо передавать nil вместо него.
На 12 строке происходит непосредственная отправка запроса. После получения ответа нужно в обязательном порядке обработать ошибку:
Важным моментом при обработке ответа от сервера, является освобождение ресурса соединения с помощью resp.Body.Close().
Если не вызвать resp.Body.Close() соединение не сможет быть переиспользовано.
Последним шагом остается обработать ответ от сервера.
Создаем новую переменную response, в которой будем хранить ответ от сервера. Сервер передает данные внутри тела (Body).
Ответ от сервера представляет собой структуру http.Response. Данная структура имеет поле Body.
Body реализует интерфейс io.Reader.
Поэтому мы можем воспользоваться пакетом io для получения информации из тела.
С помощью функции io.ReadAll читаем все данные из тела ответа. Данные представляют собой срез байт ([]byte).
Мы помним, что сервер open-meteo предоставляет информацию в формате JSON.
Для преобразования среза байт в структурированное представление можно применить функции json.Unmarshal из пакета encoding/json.
Для наглядности ниже приведен код объявления структуры ForecastResponse, которая будет хранить данные, полученные от сервиса open-meteo.
В последней строке метода Forecast извлекается текущая погода из объекта Current.Temperature.
Ознакомьтесь с полным кодом приложения ниже.
Хорошей практикой будет создание похожего клиента для других внешних сервисов. Например, для получения курса криптовалют.
Полный код приложения
https://github.com/golangtime/openmeteo
В следующем занятии напишем свой небольшой веб-сервер, который слегка меняет формат предоставления погодных данных.