Files
XQKqueue/server/internal/httpapi/response.go
2026-07-12 15:53:24 +08:00

74 lines
1.8 KiB
Go

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
}