У вас включен AdBlock или иной блокировщик рекламы.

Пожалуйста, отключите его, доход от рекламы помогает развитию сайта и появлению новых статей.

Спасибо за понимание.

В другой раз
DevGang блог о програмировании
Авторизоваться

Изучаем Go – Используем REST API в паре с шаблонами проектирования

Мы уже знаем, как отправить электронное письмо через API MailGun. В этот раз мы улучшим код, который написали пару недель назад, чтобы привлечь пользователей с сайта jsonplaceholder. Потом мы проверим новый код на работоспособность и, если вы читали предыдущий пост, вам не составит труда догадаться, что мы будем делать ...

В основном будем придерживаться того же подхода, что и ранее. Итак, от удаленного сервера мы получаем известный нами формат объекта JSON, поэтому начнем мы с создания структуры, а вернее структур. Ниже вы можете увидеть, как мы разобьем его на более мелкие части. В нем конечно есть все, что нам нужно, но я думаю, что это немного громоздкий вариант. Чтобы вам стало понятнее, объясняю: на самом деле нам не нужно заполнять всю структуру входящим JSON, мы могли бы просто получить ID, имя, имя пользователя и адрес электронной почты, и больше ни о чем не заботится. В листинге есть все варианты использования, хотя на GitHub, наверное, будут не все.

type Users []struct {
  ID       int    `json:"id"`
  Name     string `json:"name"`
  Username string `json:"username"`
  Email    string `json:"email"`
  Address  struct {
    Street  string `json:"street"`
    Suite   string `json:"suite"`
    City    string `json:"city"`
    Zipcode string `json:"zipcode"`
    Geo     struct {
      Lat string `json:"lat"`
      Lng string `json:"lng"`
    } `json:"geo"`
  } `json:"address"`
  Phone   string `json:"phone"`
  Website string `json:"website"`
  Company struct {
    Name        string `json:"name"`
    CatchPhrase string `json:"catchPhrase"`
    Bs          string `json:"bs"`
  } `json:"company"`
}

Пройдемся по коду сверху вниз, и сначала получим адрес Address

type Address struct {
  Street  string `json:"street"`
  Suite   string `json:"suite"`
  City    string `json:"city"`
  Zipcode string `json:"zipcode"`
  Geo     struct {
    Lat string `json:"lat"`
    Lng string `json:"lng"`
  } `json:"geo"`
}

Я уже было хотел вытащить Geo, но, в конце концов, решил, что Geo лучше оставаться в части структуры адреса. А теперь переместим Company.

type Company struct {
  Name        string `json:"name"`
  CatchPhrase string `json:"catchPhrase"`
  Bs          string `json:"bs"`
}

И закончим созданием блокировки пользователей с использованием вновь созданных структур Address и Company.

type Users []struct {
  ID       int    `json:"id"`
  Name     string `json:"name"`
  Username string `json:"username"`
  Email    string `json:"email"`
  Address  Address `json:"address"`
  Phone    string `json:"phone"`
  Website  string `json:"website"`
  Company  Company `json:"company"`
}

Здорово, что в новой итерации того же базового кода, с которого мы начали,  нам нужно внести всего лишь небольшие изменения. Переименуем нашу переменную r и назовем ее переменной u, которая будет содержать наших пользователей Users.

var u Users
json.Unmarshal(body, &u)

Хорошо, раз уж теперь у нас в памяти есть пользователи, что же мы можем с ними сделать? Давайте представим, что они были в списке адресатов и хотели получать обновления каждый раз, когда появлялось новое сообщение. А мы уже знаем, что отправка электронной почты через MailGun очень проста - фактически, у нас есть пакет, который мы можем просто импортировать. Как раз почти так, как я и хотел! Но прежде, разберем другое...

Используем шаблоны

Давайте кратко рассмотрим шаблоны Go. Если вы занимались веб-разработкой, то, наверное, 

знакомы с Handlebars и подобными системами шаблонов - ведь все они примерно похожи друг на друга. Мы можем взять struct и использовать этот компонент для замены текста внутри уже «размеченного» фрагмента. Здесь я привел основной пример, который можно встретить в Golang Playground. Давайте разберемся, что же тут происходит. 

package main

import (
  "os"
  "text/template"
)

type Data struct {
  Name string
  City string
}

func main() {

  o := Data{"Steve", "Portland"}
  msgText := "It's {{.Name}} from {{.City}}!"

  t := template.Must(template.New("msg").Parse(msgText))

  err := t.Execute(os.Stdout, o)
  if err != nil {
    panic(err)
  }
}

Текст шаблона в этом примере будет выглядеть так:  It's {{.Name}} from {{.City}}!. Затем мы оборачиваем template.New().Parse() с template.Must().  .Must() будет выкидывать варнинги, если .New() или .Parse() завершится неудачей. t.Execute () в данном случае заменяет токены {{}} и записывает результат непосредственно для последующего вывода пользователю. Вы по-прежнему можете поиграться с этим на игровой площадке Golang. Итак, спойлер:  в выводе вы увидите такой текст:  It's Steve from Portland!

Используйте пользователей Users и шаблоны, и вы сможете очень быстро производить замены и получать приятную персонализированную электронную почту.

Поработаем со списком

Вернемся к нашему исходному коду и добавим первую часть шаблонного кода прямо после теперь уже обновленной переменной Users и вызовом Unmarshal().

msgText := "To: {{.Email}}\nHi {{.Username}}! There is a new post!\n\n\n"
t := template.Must(template.New("msg").Parse(msgText))

Можете увидеть, что мы используем настройку схожую с примером из шаблона. Мы заменяем электронную почту и имя пользователя тем, что содержится в переданном объекте. Но как мы можем использовать это с нашими пользователями? В этом случае мы используем  for / range для того, чтобы пробежаться по данным в цикле.

for _, v := range u {
    err := t.Execute(os.Stdout, v)
    if err != nil {
      panic(err)
    }
  }

v будет каждый раз в цикле for хранить значение одного из наших пользователей. В этом случае нам не нужен индекс, поэтому мы используем _, чтобы указать Go, что нам не нужна эта переменная. Как и в предыдущем примере, мы вызываем t.Execute () и даем ему возможность выводить напрямую в standard out. Ну что-ж, давайте уже посмотрим как это все выглядит в коде.

Полный листинг данного поста 

package main

import (
  "encoding/json"
  "io/ioutil"
  "net/http"
  "os"
  "text/template"
)

type Address struct {
  Street  string `json:"street"`
  Suite   string `json:"suite"`
  City    string `json:"city"`
  Zipcode string `json:"zipcode"`
  Geo     struct {
    Lat string `json:"lat"`
    Lng string `json:"lng"`
  } `json:"geo"`
}

type Company struct {
  Name        string `json:"name"`
  CatchPhrase string `json:"catchPhrase"`
  Bs          string `json:"bs"`
}

type Users []struct {
  ID       int     `json:"id"`
  Name     string  `json:"name"`
  Username string  `json:"username"`
  Email    string  `json:"email"`
  Address  Address `json:"address"`
  Phone    string  `json:"phone"`
  Website  string  `json:"website"`
  Company  Company `json:"company"`
}

func main() {
  APIURL := "https://jsonplaceholder.typicode.com/users"
  req, err := http.NewRequest(http.MethodGet, APIURL, nil)
  if err != nil {
    panic(err)
  }
  client := http.DefaultClient
  resp, err := client.Do(req)
  if err != nil {
    panic(err)
  }
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    panic(err)
  }

  var u Users
  json.Unmarshal(body, &u)

  msgText := "To: {{.Email}}\nHi {{.Username}}! There is a new post!\n\n\n"
  t := template.Must(template.New("msg").Parse(msgText))

  for _, v := range u {
    err := t.Execute(os.Stdout, v)
    if err != nil {
      panic(err)
    }
  }
}

Тем временем мы разобрались, как мы вкладывать структуры в программе и использовать базовые шаблоны. Все таки этот пост получился довольно длинным… В следующий раз, я немного изменю его структуру и добавлю реальную отправку почты. А после я пока еще не уверен, что же я вам расскажу!

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

#Golang