Введение: почему Go идеален для REST API?
Go (Golang) — это современный компилируемый язык программирования, созданный в Google. Он сочетает в себе высокую производительность C++ и простоту Python. Если вам нужно быстро написать REST API, Go — один из лучших выборов. В этой статье мы создадим полноценный REST API на Go за 30 минут, используя только стандартную библиотеку net/http. Вы увидите, как просто можно обрабатывать HTTP-запросы, маршрутизировать их и возвращать JSON-ответы.
К концу статьи вы сможете написать собственный API, который можно развернуть на сервере. Мы не будем использовать сторонние фреймворки — только чистый Go, что делает код легковесным и быстрым.
Шаг 1: Установка Go и создание проекта
Установка Go
Если у вас ещё не установлен Go, скачайте последнюю версию с официального сайта. Следуйте инструкциям для вашей операционной системы. После установки проверьте версию в терминале:
go version
Вы должны увидеть что-то вроде go version go1.22.0 linux/amd64.
Создание проекта
Создайте новую директорию для проекта и инициализируйте модуль Go:
mkdir go-rest-api
cd go-rest-api
go mod init go-rest-api
Теперь структура проекта готова. Мы будем писать весь код в одном файле main.go, чтобы не усложнять пример.
Шаг 2: Простой HTTP-сервер на Go
Начнём с базового HTTP-сервера. Откройте файл main.go и добавьте следующий код:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, REST API on Go!")
})
log.Println("Сервер запущен на порту 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Запустите сервер командой go run main.go и откройте браузер по адресу http://localhost:8080. Вы увидите приветственное сообщение. Поздравляю — ваш первый HTTP-сервер на Go работает!
Разбор кода
http.HandleFunc— регистрирует обработчик для маршрута "/".http.ListenAndServe— запускает сервер на порту 8080.log.Fatal— выводит ошибку, если сервер не запустился.
Шаг 3: Добавляем маршрутизацию и обработку JSON
Теперь добавим поддержку REST-стиля: GET, POST, PUT, DELETE. Мы создадим простую "базу данных" в памяти (срез структур) и будем управлять списком задач (todo list).
Структура данных
Добавьте в main.go описание структуры Task и глобальный срез:
type Task struct {
ID int `json:"id"`
Name string `json:"name"`
}
var tasks []Task
var nextID int = 1
Обработчики маршрутов
Создадим главный обработчик, который будет анализировать метод HTTP и вызывать нужную функцию:
func tasksHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getTasks(w, r)
case http.MethodPost:
createTask(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func taskHandler(w http.ResponseWriter, r *http.Request) {
// Извлекаем ID из URL
idStr := strings.TrimPrefix(r.URL.Path, "/tasks/")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid task ID", http.StatusBadRequest)
return
}
switch r.Method {
case http.MethodGet:
getTask(w, r, id)
case http.MethodPut:
updateTask(w, r, id)
case http.MethodDelete:
deleteTask(w, r, id)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
Не забудьте добавить импорты: "encoding/json", "strconv", "strings".
Реализация CRUD-операций
Напишем функции для работы с задачами. Вот полный код обработчиков:
func getTasks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(tasks)
}
func createTask(w http.ResponseWriter, r *http.Request) {
var newTask Task
if err := json.NewDecoder(r.Body).Decode(&newTask); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
newTask.ID = nextID
nextID++
tasks = append(tasks, newTask)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(newTask)
}
func getTask(w http.ResponseWriter, r *http.Request, id int) {
for _, task := range tasks {
if task.ID == id {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(task)
return
}
}
http.Error(w, "Task not found", http.StatusNotFound)
}
func updateTask(w http.ResponseWriter, r *http.Request, id int) {
var updatedTask Task
if err := json.NewDecoder(r.Body).Decode(&updatedTask); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
for i, task := range tasks {
if task.ID == id {
tasks[i].Name = updatedTask.Name
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(tasks[i])
return
}
}
http.Error(w, "Task not found", http.StatusNotFound)
}
func deleteTask(w http.ResponseWriter, r *http.Request, id int) {
for i, task := range tasks {
if task.ID == id {
tasks = append(tasks[:i], tasks[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
http.Error(w, "Task not found", http.StatusNotFound)
}
Регистрация маршрутов в main()
Измените функцию main():
func main() {
http.HandleFunc("/tasks", tasksHandler)
http.HandleFunc("/tasks/", taskHandler)
log.Println("REST API запущен на