package httpapi import ( "crypto/sha256" "encoding/hex" "encoding/json" "errors" "io" "net/http" ) const maxJSONBody = 1 << 20 type apiError struct { Status int Code string Message string Details any } func (e *apiError) Error() string { return e.Code + ": " + e.Message } 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 writeError(w http.ResponseWriter, err error) { var target *apiError if !errors.As(err, &target) { target = &apiError{Status: http.StatusInternalServerError, Code: "INTERNAL_ERROR", Message: "服务暂时不可用"} } body := map[string]any{ "error": map[string]any{ "code": target.Code, "message": target.Message, }, } if target.Details != nil { body["error"].(map[string]any)["details"] = target.Details } writeJSON(w, target.Status, body) } func decodeJSON(r *http.Request, dst any) error { decoder := json.NewDecoder(io.LimitReader(r.Body, maxJSONBody)) decoder.DisallowUnknownFields() if err := decoder.Decode(dst); err != nil { return &apiError{Status: http.StatusBadRequest, Code: "INVALID_JSON", Message: "请求内容格式不正确"} } if err := decoder.Decode(&struct{}{}); !errors.Is(err, io.EOF) { return &apiError{Status: http.StatusBadRequest, Code: "INVALID_JSON", Message: "请求只能包含一个 JSON 对象"} } return nil } func marshalResponse(value any) ([]byte, error) { body, err := json.Marshal(value) if err != nil { return nil, err } return body, nil } func requestHash(value any) (string, error) { body, err := json.Marshal(value) if err != nil { return "", err } sum := sha256.Sum256(body) return hex.EncodeToString(sum[:]), nil }