Creating validator.go allowing us to validate received data from our endpoints. Updating errors.go adding failedValidationResponse method allowing us to deliver a StatusUnprocessableEntity error. Updating createMovieHandler with the use of our new validator package.
All checks were successful
Deploy Greenlight API / deploy (push) Successful in 53s

This commit is contained in:
Maxime Delporte
2025-10-28 11:04:18 +01:00
parent 040ace30ad
commit e5ddfa120f
3 changed files with 98 additions and 1 deletions

View File

@@ -51,3 +51,8 @@ func (app *application) methodNotAllowedResponse(w http.ResponseWriter, r *http.
func (app *application) badRequestResponse(w http.ResponseWriter, r *http.Request, err error) {
app.errorResponse(w, r, http.StatusBadRequest, err.Error())
}
// The errors parameter here has the type map[string]string, which is exactly the same as the errors map contained in our Validator type.
func (app *application) failedValidationResponse(w http.ResponseWriter, r *http.Request, errors map[string]string) {
app.errorResponse(w, r, http.StatusUnprocessableEntity, errors)
}

View File

@@ -3,6 +3,7 @@ package main
import (
"fmt"
"greenlight.craftr.fr/internal/data"
"greenlight.craftr.fr/internal/validator"
"net/http"
"time"
)
@@ -19,12 +20,37 @@ func (app *application) createMovieHandler(w http.ResponseWriter, r *http.Reques
// Use the new readJSON() helper to decode the request body into the input struct. If this returns an error, we send the client the error message along with a 400 Bad Request status code, just like before.
err := app.readJSON(w, r, &input)
if err != nil {
app.badRequestResponse(w, r, err)
return
}
// Initialize a new Validator instance
v := validator.New()
// Use the Check() method to execute our validation checks. This will add the provided key and error message to the errors maps if the check does not evaluate to true. For example, in the first line we "check that the title is not equal to an empty string".
v.Check(input.Title != "", "title", "must be provided")
v.Check(len(input.Title) <= 500, "title", "must not be more than 500 bytes long")
v.Check(input.Year != 0, "year", "must be provided")
v.Check(input.Year >= 1888, "year", "must be greater than 1888")
v.Check(input.Year <= int32(time.Now().Year()), "year", "must not be in the future")
v.Check(input.Runtime != 0, "runtime", "must be provided")
v.Check(input.Runtime > 0, "runtime", "must be a positive integer")
v.Check(input.Genres != nil, "genres", "must be provided")
v.Check(len(input.Genres) >= 1, "genres", "must contain at least 1 genre")
v.Check(len(input.Genres) <= 5, "genres", "must not contain more than 5 genres")
// Not that we're using the Unique helper in the line below to check that all values in the input.Genres slice are unique
v.Check(validator.Unique(input.Genres), "genres", "must not contain duplicate values")
// Use the Valid() method to see if any of the checks failed. If they did, then use the failedValidationResponse() helper to send a response to the client, passing in the v.Errors map.
if !v.Valid() {
app.failedValidationResponse(w, r, v.Errors)
return
}
// Dump the contents of the input struct in a HTTP response.
fmt.Fprintf(w, "%+v\n", input)
}