// internal/adminhttp/http.go 提供 Admin API 处理器共用的 JSON、路径片段和来源 IP 辅助方法。 package adminhttp import ( "encoding/json" "errors" "net" "net/http" "net/url" "strings" ) func DecodeJSONRequest(w http.ResponseWriter, r *http.Request, dst any) bool { defer r.Body.Close() decoder := json.NewDecoder(r.Body) decoder.UseNumber() if err := decoder.Decode(dst); err != nil { WriteAPIError(w, http.StatusBadRequest, "invalid JSON: "+err.Error()) return false } return true } func WriteJSON(w http.ResponseWriter, status int, value any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(value) } func WriteAPIError(w http.ResponseWriter, status int, message string) { WriteJSON(w, status, map[string]any{ "error": message, }) } func PathSegment(raw string) (string, error) { if raw == "" || strings.Contains(raw, "/") { return "", errors.New("invalid path segment") } value, err := url.PathUnescape(raw) if err != nil { return "", err } if value == "" || strings.Contains(value, "/") { return "", errors.New("invalid path segment") } return value, nil } func RequestSourceIP(r *http.Request) string { host, _, err := net.SplitHostPort(r.RemoteAddr) if err == nil { return host } if r.RemoteAddr != "" { return r.RemoteAddr } return "unknown" }