// Package httpx — общие HTTP-утилиты: ответы об ошибках (RFC 9457), логирование, middleware. package httpx import ( "encoding/json" "log/slog" "net/http" ) // Problem — тело ответа application/problem+json. Поле Code переводится клиентом через i18n. type Problem struct { Type string `json:"type"` Status int `json:"status"` Code string `json:"code"` Detail string `json:"detail,omitempty"` Params map[string]any `json:"params,omitempty"` } // WriteProblem пишет ответ об ошибке. func WriteProblem(w http.ResponseWriter, status int, code, detail string) { w.Header().Set("Content-Type", "application/problem+json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(Problem{Type: "about:blank", Status: status, Code: code, Detail: detail}) } // NotFound — обработчик неизвестных путей. func NotFound(w http.ResponseWriter, _ *http.Request) { WriteProblem(w, http.StatusNotFound, "common.not_found", "route not found") } // MethodNotAllowed — обработчик неподдерживаемых методов. func MethodNotAllowed(w http.ResponseWriter, _ *http.Request) { WriteProblem(w, http.StatusMethodNotAllowed, "common.method_not_allowed", "method not allowed") } // RequestError — ошибка разбора запроса (strict-server). func RequestError(w http.ResponseWriter, _ *http.Request, err error) { WriteProblem(w, http.StatusBadRequest, "common.bad_request", err.Error()) } // ResponseError — непредвиденная ошибка обработчика: детали только в лог. func ResponseError(log *slog.Logger) func(http.ResponseWriter, *http.Request, error) { return func(w http.ResponseWriter, r *http.Request, err error) { log.ErrorContext(r.Context(), "handler failed", "path", r.URL.Path, "err", err) WriteProblem(w, http.StatusInternalServerError, "common.internal", "internal error") } }