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)
|
message := fmt.Sprintf("the %s method is not supported for this resource", r.Method)
|
||||||
app.errorResponse(w, r, http.StatusMethodNotAllowed, message)
|
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)
|
err := app.writeJSON(w, http.StatusOK, envelopedHealth, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.logger.Print(err)
|
app.serverErrorResponse(w, r, err)
|
||||||
http.Error(w, "the server encountered an error and could process your request", http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,70 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type envelope map[string]any
|
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) {
|
func (app *application) readIDParam(r *http.Request) (int64, error) {
|
||||||
params := httprouter.ParamsFromContext(r.Context())
|
params := httprouter.ParamsFromContext(r.Context())
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *application) createMovieHandler(w http.ResponseWriter, r *http.Request) {
|
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) {
|
func (app *application) showMovieHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
id, err := app.readIDParam(r)
|
id, err := app.readIDParam(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.NotFound(w, r)
|
app.notFoundResponse(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +44,7 @@ func (app *application) showMovieHandler(w http.ResponseWriter, r *http.Request)
|
||||||
envelopedMovie := envelope{"movie": movie}
|
envelopedMovie := envelope{"movie": movie}
|
||||||
err = app.writeJSON(w, http.StatusOK, envelopedMovie, nil)
|
err = app.writeJSON(w, http.StatusOK, envelopedMovie, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.logger.Print(err)
|
app.serverErrorResponse(w, r, err)
|
||||||
http.Error(w, "the server encountered an error and could process your request", http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
|
|
||||||
func (app *application) routes() *httprouter.Router {
|
func (app *application) routes() *httprouter.Router {
|
||||||
router := httprouter.New()
|
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.MethodGet, "/v1/healthcheck", app.healthCheckHandler)
|
||||||
router.HandlerFunc(http.MethodPost, "/v1/movies", app.createMovieHandler)
|
router.HandlerFunc(http.MethodPost, "/v1/movies", app.createMovieHandler)
|
||||||
|
|
Loading…
Reference in New Issue