The following steps cover the writing and running of your application:
- From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter8/validation, and navigate to this directory.
- Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter8/validation
You should see a file called go.mod that contains the following:
module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter8/validation
- Copy the tests from ~/projects/go-programming-cookbook-original/chapter8/validation, or use this as an exercise to write some of your own code!
- Create a file called controller.go with the following contents:
package validation
// Controller holds our validation functions
type Controller struct {
ValidatePayload func(p *Payload) error
}
// New initializes a controller with our
// local validation, it can be overwritten
func New() *Controller {
return &Controller{
ValidatePayload: ValidatePayload,
}
}
- Create a file called validate.go with the following contents:
package validation
import "errors"
// Verror is an error that occurs
// during validation, we can
// return this to a user
type Verror struct {
error
}
// Payload is the value we
// process
type Payload struct {
Name string `json:"name"`
Age int `json:"age"`
}
// ValidatePayload is 1 implementation of
// the closure in our controller
func ValidatePayload(p *Payload) error {
if p.Name == "" {
return Verror{errors.New("name is required")}
}
if p.Age <= 0 || p.Age >= 120 {
return Verror{errors.New("age is required and must be a
value greater than 0 and less than 120")}
}
return nil
}
- Create a file called process.go with the following contents:
package validation
import (
"encoding/json"
"fmt"
"net/http"
)
// Process is a handler that validates a post payload
func (c *Controller) Process(w http.ResponseWriter, r
*http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
var p Payload
if err := decoder.Decode(&p); err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusBadRequest)
return
}
if err := c.ValidatePayload(&p); err != nil {
switch err.(type) {
case Verror:
w.WriteHeader(http.StatusBadRequest)
// pass the Verror along
w.Write([]byte(err.Error()))
return
default:
w.WriteHeader(http.StatusInternalServerError)
return
}
}
}
- Create a new directory named example and navigate to it.
- Create a file called main.go with the following contents:
package main
import (
"fmt"
"net/http"
"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter8/validation"
)
func main() {
c := validation.New()
http.HandleFunc("/", c.Process)
fmt.Println("Listening on port :3333")
err := http.ListenAndServe(":3333", nil)
panic(err)
}
- Run go run main.go.
- You could also run the following:
$ go build
$ ./example
You should see the following output:
$ go run main.go
Listening on port :3333
- In a separate Terminal, run the following commands:
$ curl "http://localhost:3333/" -X POST -d '{}'
$ curl "http://localhost:3333/" -X POST -d '{"name":"test"}'
$ curl "http://localhost:3333/" -X POST -d '{"name":"test",
"age": 5}' -v
You should see the following output:
$ curl "http://localhost:3333/" -X POST -d '{}'
name is required
$ curl "http://localhost:3333/" -X POST -d '{"name":"test"}'
age is required and must be a value greater than 0 and
less than 120
$ curl "http://localhost:3333/" -X POST -d '{"name":"test",
"age": 5}' -v
<lots of output, should contain a 200 OK status code>
- The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.
- If you copied or wrote your own tests, go up one directory and run go test. Ensure that all tests pass.