69 lines
2.1 KiB
Go
69 lines
2.1 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
reasonMalformedRequest = "MALFORMED_REQUEST"
|
|
reasonMethodNotAllowed = "METHOD_NOT_ALLOWED"
|
|
reasonForbidden = "FORBIDDEN"
|
|
reasonInternal = "INTERNAL"
|
|
)
|
|
|
|
type apiError struct {
|
|
Code int `json:"code"`
|
|
Reason string `json:"reason"`
|
|
Message string `json:"message"`
|
|
Details map[string]any `json:"details,omitempty"`
|
|
RequestID string `json:"request_id,omitempty"`
|
|
ServerTime string `json:"server_time"`
|
|
}
|
|
|
|
func writeSuccess(w http.ResponseWriter, r *http.Request, data any) {
|
|
writeJSON(w, r, http.StatusOK, map[string]any{
|
|
"code": 0, "data": data, "request_id": requestID(r),
|
|
"server_time": time.Now().UTC().Format(time.RFC3339Nano),
|
|
})
|
|
}
|
|
|
|
func writeError(w http.ResponseWriter, r *http.Request, status, code int, reason, message string, details map[string]any) {
|
|
writeJSON(w, r, status, apiError{Code: code, Reason: reason, Message: message, Details: details,
|
|
RequestID: requestID(r), ServerTime: time.Now().UTC().Format(time.RFC3339Nano)})
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, r *http.Request, status int, value any) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
if requestID := requestID(r); requestID != "" {
|
|
w.Header().Set("X-Request-ID", requestID)
|
|
}
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(value)
|
|
}
|
|
|
|
func requestID(r *http.Request) string {
|
|
return r.Header.Get("X-Request-ID")
|
|
}
|
|
|
|
func decodeBody(w http.ResponseWriter, r *http.Request, dst any) bool {
|
|
defer r.Body.Close()
|
|
decoder := json.NewDecoder(r.Body)
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(dst); err != nil {
|
|
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", nil)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func methodNotAllowed(w http.ResponseWriter, r *http.Request, allowed ...string) {
|
|
if len(allowed) == 0 {
|
|
allowed = []string{http.MethodPost}
|
|
}
|
|
w.Header().Set("Allow", strings.Join(allowed, ", "))
|
|
writeError(w, r, http.StatusMethodNotAllowed, 30005, reasonMethodNotAllowed, "method not allowed", nil)
|
|
}
|