74 lines
1.8 KiB
Go
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
|
|
}
|