Изучаем Go - Отправка REST-запросов
Забудьте о GET, даешь POST
Мы с вами рассмотрели парочку небольших примеров того, как запрашивать данные с удаленного сервера по REST API. На этот раз, вы узнаете как их туда отправлять. Возможно, для этого вы могли бы использовать что-то вроде go-swagger, как предлагал @bgadrian и, вероятно, еще бы и применили это в продакшене. Однако в целях обучения мы просто сделаем, то, что сумеем стандартными средствами из коробки и используем небольшие самописные пакеты на основе стандартной библиотеки.
На этот раз мы рассмотрим отправку электронного письма с помощью MailGun API. Я выбрал MailGun, потому что он довольно простой и имеет хорошую документацию и, к тому же, у меня уже есть аккаунт. Для его использования необходима регистрация.
GOPATH
Но послушайте! Я надеялся, что никогда не попаду в GOPATH, потому что это может быть весьма странным, в зависимости от операционной системы, которую вы используете. Будем иметь это в виду, а пока я оставлю здесь вики, где вы сможете все узнать. В большинстве случаев я полагаю, что если вы читаете это, то у вас, наверное, уже установлен Go и он готов к работе. Я попытался собрать этот проект немного по-другому, так как хотел начать собирать пакеты многократного использования, которые я надеюсь буду использовать в будущих публикациях. Будем иметь это в виду во время написания довольно простого клиента MailGun "только для отправки". Первым делом я создал каталог в моей GOPATH (у меня путь выглядит так /Users/steve/Code/go) в моей директории github.com/shindakun. Возможно, мне следовало поместить его прямо в репозиторий ATLG, так как все равно он там окажется... Или преобразовать его в Go-модуль... Позже я подумаю об этом, а пока создадим эти каталоги.
mkdir $GOTPATH/github.com/shindakun/mailgunner mkdir $GOTPATH/github.com/shindakun/mailgunner/client
В первую очередь я собираюсь написать код для «клиента». Помните, что мы пытаемся выжить максимум из стандартной библиотеки, поэтому мы не импортируем существующий клиент MailGun для Go. В нашем клиентском каталоге создайте файл main.go и начните настройку пакета.
Небольшая заметка: я рекомендую использовать VSCode и плагин ms-vscode.go... а в общем то - используйте любой удобный вам редактор.
package client import ( "net/http" "net/url" "strings" )
У клиента не будет слишком много действий в функционале, поэтому нам понадобится всего несколько импортов. Строки и net/url подготовят наши данные для добавления в запрос, а net/http фактически соберет HTTP-запрос.
Отбросим вариант с импортированием, т.к есть иные способы, которые могут помочь нам реализовать нашу программу. Думаю, что вариант, представленный здесь, имеет смысл - по крайней мере, на мой взгляд. Сначала мы объявляем структуру для MgClient, эта структура будет содержать URL-адрес API, наш токен API и http.Client.
NewMgClient() примет URL API и токен и вернет нашу структуру. Эта настройка позволяет нам использовать пакет в нескольких проектах с разными учетными записями MailGun, мы можем просто передать его по мере необходимости.
type MgClient struct { MgAPIURL string MgAPIKey string Client *http.Client } func NewMgClient(apiurl, apikey string) MgClient { return MgClient{ apiurl, apikey, http.DefaultClient, } }
Дальше, становиться действительно тяжело поднять клиентский пакет методом с FormatEmailRequest(). Это метод MgClient, поэтому он передается вызывающей стороне с возвратом из NewMgClient() - позже мы это уточним. Здесь происходит несколько разных операций, поэтому я разделил функцию пополам, чтобы охватить каждую (полные исходники будут ниже, либо вы сможете найти их на GitHub). Объект данных использует url.Values {}, благодаря чему мы собираем наши пары ключ-значение для того элемента программы, который вскоре станет конечной формой отправки. Мы передаем все значения наших переменных, когда вызываем функцию.
func (mgc *MgClient) FormatEmailRequest(from, to, subject, body string) (r *http.Request, err error) { data := url.Values{} data.Add("from", from) data.Add("to", to) data.Add("subject", subject) data.Add("text", body)
Поскольку мы не используем данные электронной почты, нам нужно создать HTTP-запрос. Принцип похож на то, что мы делали до этого. Однако, обратите внимание, что мы используем http.MethodPost, а не http.MethodGet. Кроме того, мы используем strings.NewReader() для создания io.Reader, в который мы передаем data.Encode (). .Encode () просто URL кодирует пары ключ-значение, поэтому мы получим что-то вроде to=emailaddress&from=someotheraddress и так далее. Затем мы устанавливаем тег header как Basic Authorization и тег header как Content-Type. Формат в этом случае не JSON, а URL-кодированная форма.
r, err = http.NewRequest(http.MethodPost, mgc.MgAPIURL+"/messages", strings.NewReader(data.Encode())) if err != nil { return nil, err } r.SetBasicAuth("api", mgc.MgAPIKey) r.Header.Add("Content-Type", "application/x-www-form-urlencoded") return r, nil }
И это все для "клиентского" пакета! Все же было просто, да? Постараюсь вернуться к этому в следующем посте и добавить один или два теста. Можете заметить, что я уже начал задумываться об этом, убедившись, что мы возвращаемся из FormatEmailRequest () с ошибкой, если что-то идет не так. Смысл заключается в том, что мы хотим сделать ошибку в пакете, но передаем ее обратно, чтобы любой, кто использует пакет, мог обработать ее, как захочет.
Mailgunner
Теперь вернемся к нашему основному каталогу mailgunner и создадим новый файл main.go. В этот раз у нас довольно чистенько в импортах, всего лишь fmt и io / ioutil из стандартной библиотеки, плюс наш новый клиентский пакет и еще один. В своем посте Slackbot я кратко упомянул пакет envy: это очень маленький модуль, который просто получает переменную окружения или возвращает ошибку. Использование переменных ENV - это хороший способ сохранить ключи API в репозиториях Git. Код для «ENVy» может быть легко включен (всего навсего 5 строк), и я думаю, что так код становится чище. А еще пишите так потому, что он становится его собственным пакетом. Ниже вы можете увидеть envy.Get() который используется для получения ключа MailGun API из переменной среды MGKEY.
package main import ( "fmt" "io/ioutil" "github.com/shindakun/envy" "github.com/shindakun/mailgunner/client" ) func main() { mgKey, err := envy.Get("MGKEY") if err != nil { panic(err) }
Уберем базовые настройки, чтобы сделать что-нибудь действительно годное. Для начала мы создадим новый MgClient {} и сохраним его в mgc. Затем мы вызываем mgc.FormatEmailRequest() с нашим тестовым сообщением. При желании, также можно переместить URL-адрес API из кода в переменную ENV или const.
mgc := client.NewMgClient("https://api.mailgun.net/v3/youremaildomain.com", mgKey) req, err := mgc.FormatEmailRequest("<Name> some@email.domain", "other@email.domain", "Test email", "This is a test email!") if err != nil { panic(err) }
А с этого момента код по большей части будет уже напоминать то, что мы с вами делали в предыдущих постах. Мы выполняем актуальные запросы с помощью mgc.Client.Do (), считываем байтовые данные [] из res.Body, и, наконец, конвертируем в строку и выводим.
res, err := mgc.Client.Do(req) if err != nil { panic(err) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { panic(err) } fmt.Println(string(body)) }
И в конце-концов, результат должен выглядеть примерно так.
$ MGKEY=key-key go run main.go { "id": "<20181206152053.2.AEFB08ACDA47726E@youremaildomain.com>", "message": "Queued. Thank you." }
В будущем
Мы уже приблизились к тому, чтобы уже начать что-нибудь делать с нашими деталями, я бы не сказал, что мы уже совсем вплотную подошли к этому, но, все же мы где то рядом. В будущем, мы изменим наш код «getter» API из прошлых постов, чтобы вытащить список пользователей с удаленнного сервиса. Как только у нас появятся пользователи, мы быстро доберемся до кода, что уже отправить им письмо.
Другие статьи из цикла:
- Изучаем Go - создание загрузчика (часть 1)
- Изучаем Go - создание загрузчика (часть 2)
- Изучаем Go - создание загрузчика (часть 3)
- Изучаем Go - создание загрузчика (часть 4)
- Изучаем Go - создание загрузчика (часть 5)
- Изучаем Go - Использование REST API
- Изучаем Go - Продолжаем работать с REST API
- Изучаем Go - Отправка REST-запросов
- Изучаем Go - Используем REST API в паре с шаблонами проектирования
- Изучаем Go - Повторная отправка электронной почты через API
Изучаем Go - Давайте станем модульными!скороИзучаем Go - Давайте станем модульными снова!скороИзучаем Go - Сборка DevLog Часть 01скороИзучаем Go - Сборка DevLog Часть 02скоро