В этой статье мы рассмотрим, как создать REST API приложение на языке Go: лучшая пошаговая инструкция с использованием популярной архитектуры.
Причина популярности Go заключается в его простоте, мощных функциях и производительности. В качестве первого контакта с этим языком предлагаем написать небольшое REST API приложение, в котором мы сможем просматривать список книг или одну книгу, удалять, обновлять и добавлять книги в список.
Итак, у нас будет пять HTTP запросов следующего вида:
/books — GET
/books/{id} — GET
/books — POST
/books/{id} — PUT
/books/{id} — DELETE
12345 | /books — GET/books/{id} — GET/books — POST/books/{id} — PUT/books/{id} — DELETE |
Метод GET предполагает, что мы будем просматривать или список всех книг, или информацию одной книги по её id. В POST методе мы будем добавлять новую книгу, PUT – обновлять, DELETE – удалять.
Программы на Go начинаются с определения пакета.
package main
1 | package main |
Дальше мы импортируем нужные нам пакеты с помощью функции import:
import (
«log»
«net/http»
«math/rand»
«strconv»
«encoding/json»
«github.com/gorilla/mux»
)
12345678 | import ( «log» «net/http» «math/rand» «strconv» «encoding/json» «github.com/gorilla/mux») |
В конце можно заметить библиотеку gorilla/mux, которая не входит в стандартный пакет библиотек, поставляемых Go. Библиотека gorilla/mux используется для определения маршрутов HTTP запросов. Для начала нам нужно скачать её на проект с помощью команды в терминале go get github.com/gorilla/mux, а потом импортировать.
Дальше мы создадим две небольшие структуры (напоминаем, что в Go нет привычных нам классов и объектов):
type Book struct {
ID string `json:»id»`
Title string `json:»title»`
Author *Author `json:»author»`
}
type Author struct {
Firstname string `json:»firstname»`
Lastname string `json:»lastname»`
}
12345678910 | type Book struct { ID string `json:»id»` Title string `json:»title»` Author *Author `json:»author»`} type Author struct { Firstname string `json:»firstname»` Lastname string `json:»lastname»`} |
В структуре Book у нас содержится id книги, название и указатель на структуру Author, которая, в то же время, содержит имя и фамилию автора, написавшего книгу.
Далее мы создаём объект структуры Book:
var books []Book
1 | var books []Book |
Теперь, когда мы инициализировали всё, что нам понадобится для хранения данных, загрузили пакеты, можно приступать к написанию методов, обрабатывающих HTTP запросы. Первой функцией будет getBooks(), в результате работы которой мы будем видеть список всех книг. В качестве параметров передаём объект ResponseWriter и указатель на объект Request.
Также мы должны установить заголовок Content-Type, определяющий, с каким типом данных мы будет работать, а именно – json. Дальше мы формируем ответ и отправляем его пользователю.
func getBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set(«Content-Type», «application/json»)
json.NewEncoder(w).Encode(books)
}
1234 | func getBooks(w http.ResponseWriter, r *http.Request) { w.Header().Set(«Content-Type», «application/json») json.NewEncoder(w).Encode(books)} |
Функция getBook() будет показывать книгу по определённому id, который мы передаём через URL. Для начала передаём знакомые нам параметры запроса и ответа и устанавливаем заголовок. Теперь самое главное: нам нужно как-то получить id, передаваемый пользователем, с id книги, которая у нас реально существует. Для этого посмотрим на адресную строку:
127.0.0.1:8000/books/1
1 | 127.0.0.1:8000/books/1 |
Чтобы достать GET-параметр 1, воспользуемся функцией библиотеки gorilla/mux Vars(), куда передаём наш запрос, а полученные данные сохраняем в переменную params. Далее с помощью цикла перебираем весь массив и сравниваем id конкретной книги с GET-параметром из адресной строки. Если они равны, формируем ответ в json и возвращаем пользователю.
func getBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set(«Content-Type», «application/json»)
params := mux.Vars(r)
for _, item := range books {
if item.ID == params[«id»] {
json.NewEncoder(w).Encode(item)
return
}
}
json.NewEncoder(w).Encode(&Book{})
}
1234567891011 | func getBook(w http.ResponseWriter, r *http.Request) { w.Header().Set(«Content-Type», «application/json») params := mux.Vars(r) for _, item := range books { if item.ID == params[«id»] { json.NewEncoder(w).Encode(item) return } } json.NewEncoder(w).Encode(&Book{})} |
Функция createBook() будет отвечать за создание книги. Для этого мы инициализируем объект структуры Book, в четвёртой строке мы парсим тело запроса и связываем её с объектом book, передаваемым по ссылке. Дальше мы формируем случайный ID и включаем новую книгу в массив books с помощью встроенной функции append.
func createBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set(«Content-Type», «application/json»)
var book Book
_ = json.NewDecoder(r.Body).Decode(&book)
book.ID = strconv.Itoa(rand.Intn(1000000))
books = append(books, book)
json.NewEncoder(w).Encode(book)
}
12345678 | func createBook(w http.ResponseWriter, r *http.Request) { w.Header().Set(«Content-Type», «application/json») var book Book _ = json.NewDecoder(r.Body).Decode(&book) book.ID = strconv.Itoa(rand.Intn(1000000)) books = append(books, book) json.NewEncoder(w).Encode(book)} |
Функции updateBook() и deleteBook() внешне очень похожи: мы также должны получить переменные запроса с помощью метода Vars(), необходимые для сравнения значений на изменение и удаление книги. Комбинируя функцию append и оператора среза мы добиваемся того, что в первом случае индексируем новый срез с уже обновлённой книгой, который включаем в массив с книгами, а во втором – удаляем срез, в котором находится книга.
func updateBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set(«Content-Type», «application/json»)
params := mux.Vars(r)
for index, item := range books {
if item.ID == params[«id»] {
books = append(books[:index], books[index+1:]…)
var book Book
_ = json.NewDecoder(r.Body).Decode(&book)
book.ID = params[«id»]
books = append(books, book)
json.NewEncoder(w).Encode(book)
return
}
}
json.NewEncoder(w).Encode(books)
}
func deleteBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set(«Content-Type», «application/json»)
params := mux.Vars(r)
for index, item := range books {
if item.ID == params[«id»] {
books = append(books[:index], books[index+1:]…)
break
}
}
json.NewEncoder(w).Encode(books)
}
12345678910111213141516171819202122232425262728 | func updateBook(w http.ResponseWriter, r *http.Request) { w.Header().Set(«Content-Type», «application/json») params := mux.Vars(r) for index, item := range books { if item.ID == params[«id»] { books = append(books[:index], books[index+1:]…) var book Book _ = json.NewDecoder(r.Body).Decode(&book) book.ID = params[«id»] books = append(books, book) json.NewEncoder(w).Encode(book) return } } json.NewEncoder(w).Encode(books)} func deleteBook(w http.ResponseWriter, r *http.Request) { w.Header().Set(«Content-Type», «application/json») params := mux.Vars(r) for index, item := range books { if item.ID == params[«id»] { books = append(books[:index], books[index+1:]…) break } } json.NewEncoder(w).Encode(books)} |
И, наконец, мы должны объявить функцию main – точку входа в наше приложение. Для начала создадим объект NewRouter() пакета gorilla/mux и добавим две книги в массив. Теперь осталось прописать пять маршрутов нашего приложения, определить HTTP методы и заставить приложение слушать порт 8000.
func main() {
r := mux.NewRouter()
books = append(books, Book{ID: «1», Title: «Война и Мир», Author: &Author{Firstname: «Лев», Lastname: «Толстой»}})
books = append(books, Book{ID: «2», Title: «Преступление и наказание», Author: &Author{Firstname: «Фёдор», Lastname: «Достоевский»}})
r.HandleFunc(«/books», getBooks).Methods(«GET»)
r.HandleFunc(«/books/{id}», getBook).Methods(«GET»)
r.HandleFunc(«/books», createBook).Methods(«POST»)
r.HandleFunc(«/books/{id}», updateBook).Methods(«PUT»)
r.HandleFunc(«/books/{id}», deleteBook).Methods(«DELETE»)
log.Fatal(http.ListenAndServe(«:8000», r))
}
1234567891011 | func main() { r := mux.NewRouter() books = append(books, Book{ID: «1», Title: «Война и Мир», Author: &Author{Firstname: «Лев», Lastname: «Толстой»}}) books = append(books, Book{ID: «2», Title: «Преступление и наказание», Author: &Author{Firstname: «Фёдор», Lastname: «Достоевский»}}) r.HandleFunc(«/books», getBooks).Methods(«GET») r.HandleFunc(«/books/{id}», getBook).Methods(«GET») r.HandleFunc(«/books», createBook).Methods(«POST») r.HandleFunc(«/books/{id}», updateBook).Methods(«PUT») r.HandleFunc(«/books/{id}», deleteBook).Methods(«DELETE») log.Fatal(http.ListenAndServe(«:8000», r))} |
Чтобы убедиться, что REST API приложение работает, пропишем в терминале команду go run main.go.
Если введём в адресной строке 127.0.0.1:8000/books, то увидим следующее:
Все книги
На запрос 127.0.0.1:8000/books/1 мы должны получить данные о первой книге:
Одна книга
Чтобы проверить, что запросы PUT, DELETE и POST работают, воспользуемся Postman. Пишем json, который хотим отправить на добавление:
Смотрим, что получилось:
Видим, что функция сработала и в нашем списке появилась новая книга. Теперь попробуем обновить одну из книг. Для этого в Postman выставляем метод PUT, в строке запроса пишем http://127.0.0.1:8000/books/1, где 1 – id обновляемой книги.
Отправляем запрос и идём в браузер за результатами:
Как видно, всё работает. Осталось проверить последний метод – DELETE. Для этого в Postman выставляем DELETE, а всё остальное оставляем от метода PUT, включая id удаляемой книги.
Таким образом, мы научились писать на Go простое REST API приложение, посмотрели на язык в боевых условиях и убедились, что Go не только прост в освоении, но и быстрее многих других за счёт компилируемости и прочих особенностей.
Больше материалов по теме:
- Программирование на Go с нуля: 9 полезных видеоуроков
- Разбираем по косточкам компьютерные сети: HTTP, TCP, REST