adds custom errors and validation on input

This commit is contained in:
ergz 2023-06-25 01:10:01 -07:00
parent f407cf8909
commit 13535506f3
5 changed files with 79 additions and 6 deletions

View File

@ -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())
}

View File

@ -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)
}
}

View File

@ -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())

View File

@ -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)
}
}

View File

@ -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)