Проверяет знание HTTP.
Определите доменные ошибки (sentinel или типовые), возвращайте их из сервиса и на уровне HTTP используйте errors.Is/As для маппинга на статусы (например, ErrNotFound → 404, ErrValidation → 400). Не полагайтесь на сравнение строк.
Подход:
В сервисе — доменные ошибки:
sentinel (var ErrNoSlots = errors.New("…"));
или типы (type ErrNoSlots struct{ … }) с методами.
В хендлере — сопоставление ошибки статусу.
Пример:
var (
ErrNotFound = errors.New("order not found")
ErrValidation = errors.New("validation failed")
ErrNoSlots = errors.New("no slots available")
)
func (s *Service) CreateOrder(ctx context.Context, in CreateCmd) (Order, error) {
if err := validate(in); err != nil {
return Order{}, fmt.Errorf("%w: %v", ErrValidation, err)
}
// ...
return Order{}, ErrNoSlots
}
// HTTP
if err != nil {
switch {
case errors.Is(err, ErrValidation):
http.Error(w, err.Error(), http.StatusBadRequest)
case errors.Is(err, ErrNotFound):
http.Error(w, err.Error(), http.StatusNotFound)
case errors.Is(err, ErrNoSlots):
http.Error(w, err.Error(), http.StatusConflict)
default:
http.Error(w, "internal error", http.StatusInternalServerError)
}
return
}Советы:
заворачивайте fmt.Errorf("%w", err) для сохранения цепочки;
используйте errors.As для извлечения полей из типовых ошибок.
Вывод: чёткий контракт ошибок на уровне сервиса упрощает правильные HTTP-ответы.