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
All checks were successful
Deploy Greenlight API / deploy (push) Successful in 53s
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
66
internal/validator/validator.go
Normal file
66
internal/validator/validator.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Declare a regular expression for sanity checking the format of email addresses. This regular expression pattern is taken from https://html.spec.whatwg.org/#valid-e-mail-address.
|
||||
/*
|
||||
Regex pour l'e-mail à tester :
|
||||
1. "^[a-zA-Z0-9.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
|
||||
2. `^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`
|
||||
*/
|
||||
var (
|
||||
EmailTX, _ = regexp.Compile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
|
||||
)
|
||||
|
||||
// Validator : Contains a map of validation errors
|
||||
type Validator struct {
|
||||
Errors map[string]string
|
||||
}
|
||||
|
||||
// New is a helper which creates a new Validator instance with an empty errors map
|
||||
func New() *Validator {
|
||||
return &Validator{Errors: make(map[string]string)}
|
||||
}
|
||||
|
||||
// Valid returns true if the errors map doesn't contain any entries
|
||||
func (v *Validator) Valid() bool {
|
||||
return len(v.Errors) == 0
|
||||
}
|
||||
|
||||
// AddError adds an error message to the map (so long as no entry already exists for the given key)
|
||||
func (v *Validator) AddError(key, message string) {
|
||||
if _, exists := v.Errors[key]; !exists {
|
||||
v.Errors[key] = message
|
||||
}
|
||||
}
|
||||
|
||||
// Check adds an error message to the map only if a validation check is not 'ok'
|
||||
func (v *Validator) Check(ok bool, key, message string) {
|
||||
if !ok {
|
||||
v.AddError(key, message)
|
||||
}
|
||||
}
|
||||
|
||||
// PermittedValue is a generic function which returns true if a specific value is in a list of permitted values
|
||||
func PermittedValue[T comparable](value T, permittedValues ...T) bool {
|
||||
return slices.Contains(permittedValues, value)
|
||||
}
|
||||
|
||||
// Matches returns true if a string value matches a specific regexp pattern
|
||||
func Matches(value string, rx *regexp.Regexp) bool {
|
||||
return rx.MatchString(value)
|
||||
}
|
||||
|
||||
// Unique is a generic function which returns true if all values in a slice are unique
|
||||
func Unique[T comparable](values []T) bool {
|
||||
uniqueValues := make(map[T]bool)
|
||||
|
||||
for _, value := range values {
|
||||
uniqueValues[value] = true
|
||||
}
|
||||
|
||||
return len(values) == len(uniqueValues)
|
||||
}
|
||||
Reference in New Issue
Block a user