94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package internalhttp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"galaxy/authsession/internal/service/shared"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const internalErrorCodeContextKey = "internal_error_code"
|
|
|
|
type malformedJSONRequestError struct {
|
|
message string
|
|
}
|
|
|
|
func (e *malformedJSONRequestError) Error() string {
|
|
if e == nil {
|
|
return ""
|
|
}
|
|
|
|
return e.message
|
|
}
|
|
|
|
func decodeJSONRequest(request *http.Request, target any) error {
|
|
if request == nil || request.Body == nil {
|
|
return &malformedJSONRequestError{message: "request body must not be empty"}
|
|
}
|
|
|
|
return decodeJSONReader(request.Body, target)
|
|
}
|
|
|
|
func decodeJSONReader(reader io.Reader, target any) error {
|
|
decoder := json.NewDecoder(reader)
|
|
decoder.DisallowUnknownFields()
|
|
|
|
if err := decoder.Decode(target); err != nil {
|
|
return describeJSONDecodeError(err)
|
|
}
|
|
|
|
if err := decoder.Decode(&struct{}{}); err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return nil
|
|
}
|
|
|
|
return &malformedJSONRequestError{message: "request body must contain a single JSON object"}
|
|
}
|
|
|
|
return &malformedJSONRequestError{message: "request body must contain a single JSON object"}
|
|
}
|
|
|
|
func describeJSONDecodeError(err error) error {
|
|
var syntaxErr *json.SyntaxError
|
|
var typeErr *json.UnmarshalTypeError
|
|
|
|
switch {
|
|
case errors.Is(err, io.EOF):
|
|
return &malformedJSONRequestError{message: "request body must not be empty"}
|
|
case errors.As(err, &syntaxErr):
|
|
return &malformedJSONRequestError{message: "request body contains malformed JSON"}
|
|
case errors.Is(err, io.ErrUnexpectedEOF):
|
|
return &malformedJSONRequestError{message: "request body contains malformed JSON"}
|
|
case errors.As(err, &typeErr):
|
|
if strings.TrimSpace(typeErr.Field) != "" {
|
|
return &malformedJSONRequestError{
|
|
message: fmt.Sprintf("request body contains an invalid value for %q", typeErr.Field),
|
|
}
|
|
}
|
|
|
|
return &malformedJSONRequestError{message: "request body contains an invalid JSON value"}
|
|
case strings.HasPrefix(err.Error(), "json: unknown field "):
|
|
return &malformedJSONRequestError{
|
|
message: fmt.Sprintf("request body contains unknown field %s", strings.TrimPrefix(err.Error(), "json: unknown field ")),
|
|
}
|
|
default:
|
|
return &malformedJSONRequestError{message: "request body contains invalid JSON"}
|
|
}
|
|
}
|
|
|
|
func abortWithProjection(c *gin.Context, projection shared.InternalErrorProjection) {
|
|
c.Set(internalErrorCodeContextKey, projection.Code)
|
|
c.AbortWithStatusJSON(projection.StatusCode, errorResponse{
|
|
Error: errorBody{
|
|
Code: projection.Code,
|
|
Message: projection.Message,
|
|
},
|
|
})
|
|
}
|