75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package apivalidate
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
|
|
"github.com/ultisuite/ulti-backend/internal/api/query"
|
|
)
|
|
|
|
// FieldDetail identifies a single invalid request field.
|
|
type FieldDetail struct {
|
|
Field string `json:"field"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// ValidationError reports invalid request bodies using field-level details.
|
|
type ValidationError struct {
|
|
Details []FieldDetail
|
|
}
|
|
|
|
func (e *ValidationError) Error() string {
|
|
return "invalid request body"
|
|
}
|
|
|
|
// NewValidationError builds a validation error from field details.
|
|
func NewValidationError(details ...FieldDetail) *ValidationError {
|
|
return &ValidationError{Details: details}
|
|
}
|
|
|
|
// DecodeJSON reads and decodes a JSON body with a size cap.
|
|
func DecodeJSON(w http.ResponseWriter, r *http.Request, limit int64, dest any) error {
|
|
r.Body = http.MaxBytesReader(w, r.Body, limit)
|
|
if err := json.NewDecoder(r.Body).Decode(dest); err != nil {
|
|
var maxBytesErr *http.MaxBytesError
|
|
if errors.As(err, &maxBytesErr) {
|
|
apiresponse.WriteError(w, r, http.StatusRequestEntityTooLarge, apiresponse.CodePayloadTooLarge, "request body too large", nil)
|
|
return err
|
|
}
|
|
apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid request body", nil)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WriteValidationError writes a standardized validation error response.
|
|
func WriteValidationError(w http.ResponseWriter, r *http.Request, err *ValidationError) {
|
|
apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid request body", err.Details)
|
|
}
|
|
|
|
// WriteQueryError maps query validation errors to API responses.
|
|
func WriteQueryError(w http.ResponseWriter, r *http.Request, err error) bool {
|
|
var qerr *query.ValidationError
|
|
if errors.As(err, &qerr) {
|
|
apiresponse.WriteError(w, r, http.StatusBadRequest, qerr.Code, qerr.Message, qerr.Details)
|
|
return true
|
|
}
|
|
apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidQueryParam, "invalid query parameters", nil)
|
|
return true
|
|
}
|
|
|
|
// WriteNotFound writes a standardized not-found response.
|
|
func WriteNotFound(w http.ResponseWriter, r *http.Request, message string) {
|
|
if message == "" {
|
|
message = "not found"
|
|
}
|
|
apiresponse.WriteError(w, r, http.StatusNotFound, apiresponse.CodeNotFound, message, nil)
|
|
}
|
|
|
|
// WriteInternal writes a standardized internal error response.
|
|
func WriteInternal(w http.ResponseWriter, r *http.Request) {
|
|
apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, "internal error", nil)
|
|
}
|