Введение
Это полное руководство по созданию веб-сервисов на Go. Мы разберём всё: от базовой структуры до продвинутых тем продакшена.
Посмотри на картинку выше! Она показывает всю архитектуру веб-сервисов в фантастическом стиле: программист (ты!) управляет магическими потоками данных между различными сервисами — API Gateway, Database Realm и Auth Service. Каждая башня — это отдельный микросервис, а светящиеся сферы — JSON-ответы, летящие по сети!
📦 Базовая структура Разобрано
Минимальный веб-сервис
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
responseData := map[string]string{
"message": "Привет от Go веб-сервиса!",
"status": "success",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(responseData)
}
func main() {
http.HandleFunc("/hello", helloHandler)
fmt.Println("Сервис запущен на порту 8080...")
http.ListenAndServe(":8080", nil)
}
📥 Импорт пакетов Разобрано
Работа с JSON: кодирование и декодирование данных
Форматированный ввод-вывод (Println, Printf)
HTTP клиент и сервер, все типы для работы с веб
🎮 Handler и параметры Разобрано
Сигнатура обработчика
func helloHandler(w http.ResponseWriter, r *http.Request) {
// w - ResponseWriter (отправка ответа)
// r - Request (получение запроса)
}
Это стандартная сигнатура обработчика в Go. Вы не создаёте w и r сами — Go передаёт их автоматически при получении запроса.
🚀 Функция main() Разобрано
Точка входа
Функция main — это точка входа в программу. Go начинает выполнение с неё.
Сервер работает в бесконечном цикле. Программа завершится только при нажатии Ctrl+C или ошибке.
🔀 http.HandleFunc Разобрано
Регистрация роутов
// Регистрируем маршрут
http.HandleFunc("/hello", helloHandler)
// Можно зарегистрировать много роутов
http.HandleFunc("/users", usersHandler)
http.HandleFunc("/products", productsHandler)
Можно обойтись без неё, обрабатывая все запросы в одном handler и проверяя
r.URL.Path, но это неудобно.
👂 ListenAndServe Разобрано
Два этапа работы
Открывает порт и начинает слушать входящие соединения
Принимает соединения и обрабатывает запросы
Для graceful shutdown можно разделить на net.Listen() и
server.Serve()
🛣️ Роуты Разобрано
Роут — это путь (URL), по которому клиент обращается к серверу.
Примеры роутов
/api/users— список пользователей/api/products— список товаров/health— проверка здоровья сервиса
🔖 Префикс /api/ Разобрано
- Разделение API и веб-интерфейса
- Группировка и организация
- Версионирование (/api/v1/, /api/v2/)
- Настройка прав доступа
Можно работать и без него, если у вас только API
Слэш в конце роута Разобрано
| Роут | Поведение |
|---|---|
/api/users |
Точное совпадение. Не ловит подпути |
/api/users/ |
Ловит все подпути + редирект с /api/users |
Роут со слэшем /api/users/ автоматически редиректит
/api/users → /api/users/ (HTTP 301)
🔄 Несколько сервисов Разобрано
4 варианта запуска
Каждый сервис на своём порту (8080, 8081, 8082)
// Сервис A
http.ListenAndServe(":8080", nil)
// Сервис B
http.ListenAndServe(":8081", nil)
Все сервисы в одной программе с разными роутами
http.HandleFunc("/api/users", usersHandler)
http.HandleFunc("/api/products", productsHandler)
http.ListenAndServe(":80", nil)
Nginx на порту 80 перенаправляет запросы на разные порты
Разные сервисы на разных поддоменах (users.site.com, products.site.com)
📊 HTTP статусы Следующее
Как правильно сообщать клиенту о результате запроса:
200 OK— успех201 Created— ресурс создан400 Bad Request— ошибка клиента404 Not Found— ресурс не найден500 Internal Server Error— ошибка сервера
func getUser(w http.ResponseWriter, r *http.Request) {
user, err := findUser(r.URL.Query().Get("id"))
if err != nil {
w.WriteHeader(http.StatusNotFound) // 404
json.NewEncoder(w).Encode(map[string]string{
"error": "User not found",
})
return
}
w.WriteHeader(http.StatusOK) // 200
json.NewEncoder(w).Encode(user)
}
⚠️ Обработка ошибок Следующее
Как обрабатывать ошибки и возвращать понятные сообщения клиенту:
- Невалидный JSON
- База данных недоступна
- Отсутствуют обязательные поля
- Внутренние ошибки сервера
🔗 Параметры URL (GET) Следующее
Чтение query-параметров из URL:
// Запрос: /search?name=Алиса&age=25
func searchHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name") // "Алиса"
age := r.URL.Query().Get("age") // "25"
// Или все параметры
params := r.URL.Query()
}
Прием JSON (POST) Следующее
Чтение JSON из тела запроса:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func createUser(w http.ResponseWriter, r *http.Request) {
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
// Обработка ошибки
return
}
// Сохраняем пользователя
}
🛡️ Middleware Следующее
Промежуточные обработчики для:
- Логирования всех запросов
- Аутентификации (проверка токенов)
- CORS (разрешить запросы с других доменов)
- Rate limiting (ограничение частоты)
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL.Path)
next(w, r) // Вызываем следующий handler
}
}
// Применение
http.HandleFunc("/api/users", loggingMiddleware(usersHandler))
✅ Валидация данных Следующее
Проверка входных данных:
- Обязательные поля
- Типы данных (число, строка, email)
- Длина строк
- Диапазоны чисел
📁 Структура проекта Следующее
Как организовать файлы при росте проекта:
myapp/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── handlers/
│ │ └── users.go
│ ├── models/
│ │ └── user.go
│ ── middleware/
│ └── auth.go
├── pkg/
│ └── database/
│ └── db.go
└── go.mod
💾 База данных Следующее
Работа с PostgreSQL/MySQL:
- Подключение к БД
- CRUD операции
- Миграции схемы
- Connection pooling
Аутентификация Следующее
JWT токены и авторизация:
- Генерация токенов
- Проверка токенов
- Защита роутов
- Refresh tokens
⚙️ Конфигурация Следующее
Переменные окружения:
- Хранение паролей отдельно от кода
- Разные конфиги для dev/staging/prod
- .env файлы
🛑 Graceful Shutdown Следующее
Плавная остановка сервера:
- Обработка сигнала Ctrl+C
- Дождаться завершения текущих запросов
- Закрытие соединений с БД
Тестирование Следующее
Unit и интеграционные тесты:
- Тестирование handlers
- Mock базы данных
- Test coverage