106 lines
3.9 KiB
Go
106 lines
3.9 KiB
Go
package api
|
||
|
||
import (
|
||
"fmt"
|
||
"log/slog"
|
||
"net/http"
|
||
"time"
|
||
)
|
||
|
||
// UserService — интерфейс сервиса пользователей для хендлера.
|
||
type UserService interface {
|
||
GetProfile(token string) string
|
||
}
|
||
|
||
// AuthService — интерфейс сервиса авторизации для хендлера.
|
||
type AuthService interface {
|
||
// методы, которые хендлер использует из сервиса авторизации
|
||
}
|
||
|
||
// NotificationService — интерфейс сервиса уведомлений для хендлера.
|
||
type NotificationService interface {
|
||
// методы, которые хендлер использует из сервиса уведомлений
|
||
}
|
||
|
||
// Handler — интерфейс обработчика HTTP-запросов.
|
||
type Handler interface {
|
||
Routes() http.Handler
|
||
}
|
||
|
||
// handler — конкретная реализация, скрыта от внешних пакетов.
|
||
// Содержит только хендлеры и роутинг.
|
||
// Ничего не знает про http.Server, порт или lifecycle —
|
||
// это ответственность app-слоя.
|
||
type handler struct {
|
||
userService UserService
|
||
authService AuthService
|
||
notificationService NotificationService
|
||
}
|
||
|
||
// NewHandler создаёт обработчик HTTP-запросов.
|
||
func NewHandler(
|
||
userService UserService,
|
||
authService AuthService,
|
||
notificationService NotificationService,
|
||
) Handler {
|
||
return &handler{
|
||
userService: userService,
|
||
authService: authService,
|
||
notificationService: notificationService,
|
||
}
|
||
}
|
||
|
||
// Routes возвращает маршрутизатор со всеми зарегистрированными хендлерами.
|
||
// App-слой использует этот http.Handler при создании http.Server.
|
||
func (h *handler) Routes() http.Handler {
|
||
mux := http.NewServeMux()
|
||
mux.HandleFunc("GET /health", h.healthHandler)
|
||
mux.HandleFunc("GET /users/me", h.getUserProfile)
|
||
|
||
// /slow — эндпоинт для демонстрации graceful shutdown.
|
||
// Имитирует долгий запрос: обращение к БД, вызов внешнего API и т.д.
|
||
// Пять секунд — чтобы мы успели нажать Ctrl+C, пока запрос выполняется.
|
||
mux.HandleFunc("GET /slow", h.slowHandler)
|
||
|
||
return mux
|
||
}
|
||
|
||
func (h *handler) healthHandler(w http.ResponseWriter, _ *http.Request) {
|
||
w.WriteHeader(http.StatusOK)
|
||
|
||
if _, err := fmt.Fprintln(w, "ok"); err != nil {
|
||
slog.Error("ошибка записи ответа", "err", err)
|
||
}
|
||
}
|
||
|
||
func (h *handler) getUserProfile(w http.ResponseWriter, r *http.Request) {
|
||
token := r.Header.Get("Authorization")
|
||
profile := h.userService.GetProfile(token)
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
|
||
if _, err := fmt.Fprintln(w, profile); err != nil {
|
||
slog.Error("ошибка записи ответа", "err", err)
|
||
}
|
||
}
|
||
|
||
// slowHandler — медленный эндпоинт для демонстрации graceful shutdown.
|
||
// Имитирует реальную работу: запрос в базу, вызов внешнего сервиса.
|
||
//
|
||
// Попробуйте:
|
||
// 1. curl localhost:8080/slow
|
||
// 2. Пока запрос висит — нажмите Ctrl+C в терминале сервера
|
||
// 3. Без graceful shutdown: curl получит "connection reset by peer"
|
||
// 4. С graceful shutdown: curl дождётся и получит "готово!" — 200 OK
|
||
func (h *handler) slowHandler(w http.ResponseWriter, _ *http.Request) {
|
||
slog.Info("обрабатываю медленный запрос...")
|
||
|
||
time.Sleep(5 * time.Second) // имитация: запрос в БД, внешний API
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
|
||
if _, err := fmt.Fprintln(w, "готово!"); err != nil {
|
||
slog.Error("ошибка записи ответа", "err", err)
|
||
}
|
||
}
|