DevGang
Авторизоваться

Изучаем 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 из прошлых постов, чтобы вытащить список пользователей с удаленнного сервиса. Как только у нас появятся пользователи, мы быстро доберемся до кода, что уже отправить им письмо. 

Другие статьи из цикла:

#Golang
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу