> Какие принципы построения REST API существуют? (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Моторика

Стек: Go

> Пример ответа

Короткий ответ

REST API строится на принципах архитектурного стиля REST: клиент-сервер, stateless, кэшируемость, единый интерфейс, слоистая система и опционально код по требованию. На уровне senior важно не просто перечислить их, а показать, как они влияют на дизайн ресурсов, HTTP-семантику, идемпотентность и масштабирование. В Go это выражается через чистые handler’ы, middleware и отсутствие серверного состояния между запросами.

Подробное объяснение

Классические принципы REST сформулированы Роем Филдингом в диссертации 2000 года. Клиент-сервер разделяет ответственность: клиент занимается UI и логикой представления, сервер - хранением и бизнес-логикой. Stateless означает, что каждый запрос содержит всю информацию, необходимую для его обработки, а сервер не хранит контекст сессии между запросами. Это упрощает горизонтальное масштабирование и отказоустойчивость, но переносит управление состоянием на клиента или во внешние хранилища.

Кэшируемость требует явных заголовков Cache-Control, ETag, Last-Modified, чтобы клиенты и промежуточные прокси могли переиспользовать ответы. Единый интерфейс - самый важный и самый нарушаемый принцип: идентификация ресурсов через URI, манипуляция через представления, самоописываемые сообщения и HATEOAS. Слоистая система допускает наличие прокси, балансировщиков и API gateway без изменения контракта. Код по требованию - опциональный принцип, редко применяемый на практике.

На уровне senior ожидается понимание trade-off’ов. Например, строгий stateless усложняет аутентификацию: JWT решает это, но добавляет проблемы отзыва токенов. HATEOAS почти не используется в реальных API из-за сложности и отсутствия зрелых клиентов. Идемпотентность методов GET, PUT, DELETE и неидемпотентность POST - критичны для ретраев и exactly-once семантики. Также важно различать REST и RPC-подходы, понимать, когда GraphQL или gRPC уместнее.

На практике

В Go REST API обычно строится на net/http или фреймворках вроде chi, gin, echo. Stateless достигается тем, что handler’ы не хранят состояние в глобальных переменных, а контекст запроса передаётся через context.Context. Аутентификация выносится в middleware, которое валидирует JWT или API-ключ и кладёт пользователя в контекст. Кэширование реализуется через заголовки и иногда через Redis на стороне сервера. Версионирование API делается через URL (/v1/...) или заголовок Accept. Идемпотентность PUT и DELETE обеспечивается на уровне бизнес-логики и базы данных. Для POST часто вводят Idempotency-Key.

Пример кода

GO
package main
import (
"context"
"encoding/json"
"net/http"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
type ctxKey string
const userKey ctxKey = "user"
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), userKey, token)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
user := User{ID: id, Name: "Alice"}
w.Header().Set("Cache-Control", "max-age=60")
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func main() {
mux := http.NewServeMux()
mux.Handle("GET /users/{id}", authMiddleware(http.HandlerFunc(getUser)))
http.ListenAndServe(":8080", mux)
}

Как отвечать на собеседовании

Начните с перечисления шести принципов Филдинга, затем углубитесь в те, которые чаще всего проверяют: stateless, единый интерфейс и кэшируемость. Покажите, что понимаете разницу между REST и RPC, и когда REST не подходит. Приведите примеры из Go: middleware для аутентификации, context для передачи данных, заголовки для кэша. Упомяните trade-off’ы: JWT против сессий, HATEOAS против простоты, версионирование через URL против заголовков. Если спросят про идемпотентность, объясните, как реализовать её для POST через Idempotency-Key и хранилище.

Что проверяет интервьюер

Проверяется глубина понимания архитектурных принципов, а не заученный список. Кандидат уровня senior должен уметь объяснить, почему stateless важен для масштабирования, как кэширование влияет на консистентность, и какие компромиссы возникают при строгом следовании REST. Также проверяется практический опыт: как в Go организованы handler’ы, middleware, версионирование и обработка ошибок. Ожидается знание HTTP-семантики: методов, статус-кодов, заголовков. Дополнительно оценивается способность критически оценивать REST и предлагать альтернативы.

Типичные ошибки

Кандидаты часто путают REST с JSON over HTTP и забывают про HATEOAS или кэшируемость. Многие называют stateless, но не могут объяснить, как хранить состояние аутентификации. Ошибкой является использование POST для всех операций или игнорирование идемпотентности. На уровне Go часто встречается хранение состояния в глобальных переменных или отсутствие context. Также типично неправильное использование статус-кодов: 200 вместо 201, 404 вместо 400. Некоторые пытаются применять REST там, где уместнее gRPC или GraphQL, не осознавая trade-off’ов.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью