В этом посте представлены основы написания API RESTful веб-сервиса с помощью Go и Gin Web Framework (Gin).
Вы получите максимальную отдачу от этого руководства, если у вас будут базовые знания Go и его инструментов. Если это ваше первое знакомство с Go, см. Начало работы с Go для быстрого ознакомления.
Gin упрощает многие задачи кодирования, связанные с созданием веб-приложений, включая веб-сервисы. В этом руководстве вы будете использовать Gin для маршрутизации запросов, получения сведений о запросах и маршалинга JSON для ответов.
В этом руководстве вы создадите RESTful API сервер с двумя конечными точками. Ваш примерный проект будет репозиторием данных о винтажных джазовых записях.
Предпосылки
1. Установка Go 1.16 или новее. Инструкции по установке см. Установка Go.
2. Инструмент для редактирования вашего кода. Любой текстовый редактор, который у вас есть, будет работать нормально.
3. Командный терминал. Go хорошо работает с любым терминалом в Linux и Mac, а также с PowerShell или cmd в Windows.
4. Инструмент curl. В Linux и Mac это уже должно быть установлено. В Windows он входит в состав Windows 10 . Для более ранних версий Windows может потребоваться его установка.
Проектирование конечных точек API
Вы создадите API, открывающий доступ к магазину, где продаются винтажные записи на виниле. Поэтому вам нужно будет предоставить конечные точки, через которые клиент может получать и добавлять альбомы для пользователей.
При разработке API вы обычно начинаете с проектирования конечных точек. Пользователи вашего API добьются большего успеха, если конечные точки будут простыми для понимания.
Вот конечные точки, которые вы создадите в этом руководстве.
/albums
- GET - получить список всех альбомов в формате JSON.
- POST - добавить новый альбом из данных запроса, отправленных в формате JSON.
/albums/:id
- GET - получить альбом по его идентификатору, вернув данные альбома в формате JSON.
Далее вы создадите папку для своего кода.
Создайте папку для вашего кода
Для начала создайте проект для кода, который вы напишете.
1. Откройте командную строку и перейдите в свой домашний каталог.
В Linux или Mac:
$ cd
В Windows:
C:\> cd %HOMEPATH%
2. Используя командную строку, создайте каталог для вашего кода с именем web-service-gin.
$ mkdir web-service-gin
$ cd web-service-gin
3. Создайте модуль, в котором вы сможете управлять зависимостями.
Запустите команду go mod init, указав ей путь к модулю, в котором будет находиться ваш код.
$ go mod init example/web-service-gin
go: creating new go.mod: module example/web-service-gin
Эта команда создает файл go.mod, в котором будут перечислены добавленные вами зависимости для отслеживания. Дополнительные сведения об именовании модуля с помощью пути к модулю см. в посте Управление зависимостями.
Далее вы создадите структуры данных для обработки данных.
Создайте данные
Чтобы упростить работу с руководством, вы будете хранить данные в памяти. Более типичный API будет взаимодействовать с базой данных.
Обратите внимание, что хранение данных в памяти означает, что набор альбомов будет утерян каждый раз при остановке сервера, а затем воссоздан при его запуске.
Напишите код
Используя текстовый редактор, создайте файл с именем main.go в каталоге web-service-gin. Вы напишете свой код Go в этом файле.
В main.go в верхней части файла вставьте следующее объявление пакета.
package main
Автономная программа (в отличие от библиотеки) всегда находится в пакете main.
Под объявлением пакета вставьте следующее объявление структуры album. Вы будете использовать ее для хранения данных альбома в памяти.
Теги структуры, такие как json:"artist", определяют, каким должно быть имя поля, когда содержимое структуры сериализуется в JSON. Без них JSON использовал бы имена полей структуры с заглавной буквы - стиль, который не так распространен в JSON.
// album представляет данные об альбоме.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
Под только что добавленным объявлением структуры вставьте следующий срез album структур, содержащий данные, которые вы будете использовать для начала.
// Срез albums для заполнения данных об альбомах.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
Далее вы напишете код для реализации вашей первой конечной точки.
Напишите обработчик для возврата всех товаров
Когда клиент делает запрос в GET /albums, вы хотите вернуть все альбомы в формате JSON.
Для этого вам нужно написать следующее:
- Логика для подготовки ответа
- Код для сопоставления пути запроса с вашей логикой
Обратите внимание, что это противоположно тому, как они будут выполняться во время выполнения, но вы сначала добавляете зависимости, а затем код, который от них зависит.
Напишите код
Под кодом структур, который вы добавили в предыдущем разделе, вставьте следующий код, чтобы получить список альбомов.
Эта функция getAlbums создает JSON из среза album структур, записывая JSON в ответ.
// getAlbums возвращает список всех альбомов в формате JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
В этом коде вы:
- Напишете функцию getAlbums, которая принимает параметр gin.Context. Обратите внимание, что вы могли дать этой функции любое имя - ни Gin, ни Go не требуют определенного формата имени функции.
gin.Context - самая важная часть Gin. Он передает детали запроса, проверяет и сериализует JSON и многое другое. (Несмотря на похожее название, он отличается от встроенного в Go пакета context.) - Вызовете Context.IndentedJSON, чтобы сериализовать структуру в JSON и добавить ее в ответ.
Первый аргумент функции - это код состояния HTTP, который вы хотите отправить клиенту. Здесь вы передаете константу StatusOK из пакета net/http, чтобы указать 200 OK.
Обратите внимание, что вы можете заменить Context.IndentedJSON вызовом Context.JSON для отправки более компактного JSON. На практике с формой с отступом намного легче работать при отладке, и разница в размере обычно небольшая.
В верхней части main.go, сразу под объявлением среза albums, вставьте приведенный ниже код, чтобы назначить функцию-обработчик пути к конечной точке.
Это устанавливает связь, в которой getAlbums обрабатывает запросы к пути конечной точки /albums.
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:8080")
}
В этом коде вы:
- Инициализируете роутер Gin, используя Default.
- Используете функцию GET, чтобы связать метод GET HTTP и путь /albums с функцией обработчика.
Обратите внимание, что вы передаете имя функции getAlbums. Это отличается от передачи результата функции, которую вы бы сделали, передав getAlbums() (обратите внимание на круглые скобки). - Используйте функцию Run, чтобы подключить маршрутизатор к http.Server и запустить сервер.
В верхней части main.go, сразу под объявлением пакета, импортируйте пакеты, которые вам понадобятся для поддержки кода, который вы только что написали.
Первые строки кода должны выглядеть так:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
Сохраните main.go.
Запустите код
Начните отслеживать модуль Gin как зависимость.
В командной строке используйте go get, чтобы добавить модуль github.com/gin-gonic/gin в качестве зависимости для вашего модуля. Используйте аргумент с точкой для обозначения "получить зависимости для кода в текущем каталоге".
$ go get .
go get: added github.com/gin-gonic/gin v1.7.2
Go разрешил и загрузил эту зависимость, чтобы удовлетворить декларацию импорта, которую вы добавили на предыдущем шаге.
Из командной строки в каталоге, содержащем main.go, запустите код. Используйте аргумент с точкой для обозначения "запустить код в текущем каталоге".
$ go run .
После запуска кода у вас есть работающий HTTP-сервер, на который вы можете отправлять запросы.
В новом окне командной строки используйте curl, чтобы сделать запрос к работающему веб-сервису.
$ curl http://localhost:8080/albums
Команда должна отображать данные, которыми вы заполнили сервис.
[
{
"id": "1",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 56.99
},
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
},
{
"id": "3",
"title": "Sarah Vaughan and Clifford Brown",
"artist": "Sarah Vaughan",
"price": 39.99
}
]
Вы запустили API! В следующем разделе вы создадите еще одну конечную точку с кодом для обработки POST запроса для добавления элемента.
Напишите обработчик для добавления нового элемента
Когда клиент отправляет запрос POST в /albums, вы хотите добавить альбом, описанный в теле запроса, к существующим данным альбомов.
Для этого вам нужно написать следующее:
- Логика добавления нового альбома в существующий список.
- Немного кода для маршрутизации запроса POST к вашей логике.
Напишите код
Добавьте код для добавления данных альбомов в список альбомов.
Где-нибудь после операторов импорта вставьте следующий код. (Конец файла - хорошее место для этого кода, но Go не обеспечивает соблюдение порядка, в котором вы объявляете функции.)
// postAlbums добавляет альбом из JSON,
// полученного в теле запроса.
func postAlbums(c *gin.Context) {
var newAlbum album
// Вызов BindJSON для привязки
// полученного JSON к newAlbum
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Добавляем в срез новый альбом.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
В этом коде вы:
- Используйте Context.BindJSON для привязки тела запроса к newAlbum.
- Добавляете структуру album, инициализированную из JSON, в срез albums.
- Добавляете в ответ код состояния 201 вместе с JSON, представляющим добавленный вами альбом.
Измените main функцию так, чтобы она включала функцию router.POST, как показано ниже.
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
В этом коде вы:
- Связываете метод POST по пути /albums с функцией postAlbums.
С помощью Gin вы можете связать обработчик с комбинацией HTTP-метода и пути. Таким образом, вы можете отдельно маршрутизировать запросы, отправленные по одному пути, в зависимости от метода, который использует клиент.
Запустите код
Если сервер все еще работает с последнего раздела, остановите его.
Из командной строки в каталоге, содержащем main.go, запустите код.
$ go run .
Из другого окна командной строки используйте curl, чтобы сделать запрос к работающему веб-сервису.
$ curl http://localhost:8080/albums \
--include \
--header "Content-Type: application/json" \
--request "POST" \
--data '{"id": "4","title": "The Modern Sound of Betty Carter","artist": "Betty Carter","price": 49.99}'
Команда должна отображать заголовки и JSON для добавленного альбома.
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Date: Wed, 02 Jun 2021 00:34:12 GMT
Content-Length: 116
{
"id": "4",
"title": "The Modern Sound of Betty Carter",
"artist": "Betty Carter",
"price": 49.99
}
Как и в предыдущем разделе, используйте curl для получения полного списка альбомов, который вы можете использовать для подтверждения того, что новый альбом был добавлен.
$ curl http://localhost:8080/albums \
--header "Content-Type: application/json" \
--request "GET"
Команда должна отобразить список альбомов.
[
{
"id": "1",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 56.99
},
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
},
{
"id": "3",
"title": "Sarah Vaughan and Clifford Brown",
"artist": "Sarah Vaughan",
"price": 39.99
},
{
"id": "4",
"title": "The Modern Sound of Betty Carter",
"artist": "Betty Carter",
"price": 49.99
}
]
В следующем разделе вы добавите код для обработки GET для определенного элемента.
Напишите обработчик для получения определенного элемента
Когда клиент делает запрос к GET /album/[id], вы хотите вернуть альбом, идентификатор которого совпадает с параметром пути id.
Для этого вы:
- Добавите логику для получения запрошенного альбома.
- Укажите путь к логике.
Напишите код
Под функцией postAlbums, которую вы добавили в предыдущем разделе, вставьте следующий код для получения определенного альбома.
Эта функция getAlbumByID извлечет идентификатор из пути запроса, а затем найдет соответствующий альбом.
// getAlbumByID находит альбом,
// значение идентификатора которого совпадает с
// параметром id, отправленным клиентом,
// затем возвращает этот альбом в качестве ответа
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Перебираем список альбомов в поисках альбома,
// значение идентификатора которого соответствует параметру.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
В этом коде вы:
- Используете Context.Param, чтобы получить параметр пути id из URL-адреса. Когда вы сопоставляете этот обработчик с путем, вы включаете в путь заполнитель для параметра.
- Проходите в цикле по структурам album в срезе, ища ту, значение поля ID которой совпадает со значением параметра id. Если он найден, вы сериализуете эту album структуру в JSON и возвращаете ее в качестве ответа с HTTP-кодом 200 OK.
Как упоминалось выше, реальный сервис, скорее всего, будет использовать запрос к базе данных для выполнения этого поиска. - Вернуть ошибку HTTP 404 с http.StatusNotFound, если альбом не найден.
Наконец, измените свой main так, чтобы он включал новый вызов router.GET, где теперь путь - /albums/:id, как показано в следующем примере.
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
В этом коде вы:
- Связываете путь /albums/:id с функцией getAlbumByID. В Gin двоеточие перед элементом в пути означает, что этот элемент является параметром пути.
Запустите код
Если сервер все еще работает с последнего раздела, остановите его.
Из командной строки в каталоге, содержащем main.go, запустите код, чтобы запустить сервер.
$ go run .
Из другого окна командной строки используйте curl, чтобы сделать запрос к работающему веб-сервису.
$ curl http://localhost:8080/albums/2
Команда должна отображать JSON для альбома, идентификатор которого вы использовали. Если альбом не найден, вы получите JSON с сообщением об ошибке.
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
}
Заключение
Вы только что использовали Go и Gin для написания простого RESTful веб-сервиса.
Итоговый код
В этом разделе содержится код приложения, которое вы создаете с помощью этого руководства.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// album представляет данные об альбоме.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// Срез albums для заполнения данных об альбомах.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
// getAlbums возвращает список всех альбомов в формате JSON.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums добавляет альбом из JSON, полученный в теле запроса.
func postAlbums(c *gin.Context) {
var newAlbum album
// Вызов BindJSON для привязки
// полученного JSON к newAlbum
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// Добавляем в срез новый альбом.
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID находит альбом,
// значение идентификатора которого совпадает с
// параметром id, отправленным клиентом,
// затем возвращает этот альбом в качестве ответа
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// Перебираем список альбомов в поисках альбома,
// значение идентификатора которого соответствует параметру.
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
Читайте также:
- Веб-приложение на Go: введение в пакет net/http, пример веб-сервера на Go
- Пакет net/http, краткий обзор
- Эффективный Go: веб-сервер
- Веб-приложение на Go: готовое приложение
Комментариев нет:
Отправить комментарий