adds custom errors and validation on input
This commit is contained in:
parent
f407cf8909
commit
13535506f3
|
@ -35,3 +35,7 @@ func (app *application) methodNotAllowedResponse(w http.ResponseWriter, r *http.
|
|||
message := fmt.Sprintf("the %s method is not supported for this resource", r.Method)
|
||||
app.errorResponse(w, r, http.StatusMethodNotAllowed, message)
|
||||
}
|
||||
|
||||
func (app *application) badRequestResponse(w http.ResponseWriter, r *http.Request, err error) {
|
||||
app.errorResponse(w, r, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@ func (app *application) healthCheckHandler(w http.ResponseWriter, r *http.Reques
|
|||
|
||||
err := app.writeJSON(w, http.StatusOK, envelopedHealth, nil)
|
||||
if err != nil {
|
||||
app.logger.Print(err)
|
||||
http.Error(w, "the server encountered an error and could process your request", http.StatusInternalServerError)
|
||||
app.serverErrorResponse(w, r, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,70 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
type envelope map[string]any
|
||||
|
||||
func (app *application) readJSON(w http.ResponseWriter, r *http.Request, dest any) error {
|
||||
maxBytes := 1_048_576
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
|
||||
err := decoder.Decode(dest)
|
||||
|
||||
if err != nil {
|
||||
var syntaxError *json.SyntaxError
|
||||
var unmarshallError *json.UnmarshalTypeError
|
||||
var invalidUnmarshall *json.InvalidUnmarshalError
|
||||
var maxBytesError *http.MaxBytesError
|
||||
|
||||
switch {
|
||||
case errors.As(err, &syntaxError):
|
||||
return fmt.Errorf("body contains badly-formed JSON (at character %d)", syntaxError.Offset)
|
||||
|
||||
case errors.Is(err, io.ErrUnexpectedEOF):
|
||||
return errors.New("body contains badly-formed JSON")
|
||||
|
||||
case errors.As(err, &unmarshallError):
|
||||
if unmarshallError.Field != "" {
|
||||
return fmt.Errorf("the body contains incorrect json type for field %q", unmarshallError.Field)
|
||||
}
|
||||
return fmt.Errorf("body contains incorrect JSON type (at character %d)", unmarshallError.Offset)
|
||||
|
||||
case errors.Is(err, io.EOF):
|
||||
return errors.New("body must not be empty")
|
||||
|
||||
case strings.HasPrefix(err.Error(), "json: unknown field "):
|
||||
fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ")
|
||||
return fmt.Errorf("body contains uknown key %s", fieldName)
|
||||
|
||||
case errors.As(err, &maxBytesError):
|
||||
return fmt.Errorf("body must not be larger than %d bytes", maxBytesError.Limit)
|
||||
|
||||
case errors.As(err, &invalidUnmarshall):
|
||||
panic(err)
|
||||
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = decoder.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
return errors.New("body must only contain a single JSON value")
|
||||
}
|
||||
|
||||
return nil // no errors
|
||||
}
|
||||
|
||||
func (app *application) readIDParam(r *http.Request) (int64, error) {
|
||||
params := httprouter.ParamsFromContext(r.Context())
|
||||
|
||||
|
|
|
@ -9,13 +9,26 @@ import (
|
|||
)
|
||||
|
||||
func (app *application) createMovieHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "create a new movie")
|
||||
var input struct {
|
||||
Title string `json:"title"`
|
||||
Year int32 `json:"year"`
|
||||
Runtime int32 `json:"runtime"`
|
||||
Genres []string `json:"genres"`
|
||||
}
|
||||
|
||||
err := app.readJSON(w, r, &input)
|
||||
if err != nil {
|
||||
app.badRequestResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%v+\n", input)
|
||||
}
|
||||
|
||||
func (app *application) showMovieHandler(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := app.readIDParam(r)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
app.notFoundResponse(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,8 +44,7 @@ func (app *application) showMovieHandler(w http.ResponseWriter, r *http.Request)
|
|||
envelopedMovie := envelope{"movie": movie}
|
||||
err = app.writeJSON(w, http.StatusOK, envelopedMovie, nil)
|
||||
if err != nil {
|
||||
app.logger.Print(err)
|
||||
http.Error(w, "the server encountered an error and could process your request", http.StatusInternalServerError)
|
||||
app.serverErrorResponse(w, r, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
|
||||
func (app *application) routes() *httprouter.Router {
|
||||
router := httprouter.New()
|
||||
router.NotFound = http.HandlerFunc(app.notFoundResponse)
|
||||
router.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowedResponse)
|
||||
|
||||
router.HandlerFunc(http.MethodGet, "/v1/healthcheck", app.healthCheckHandler)
|
||||
router.HandlerFunc(http.MethodPost, "/v1/movies", app.createMovieHandler)
|
||||
|
|
Loading…
Reference in New Issue