How to do it...

The following steps cover the writing and running of your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter8/validationand navigate to this directory.
  1. 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    
  1. Copy the tests from ~/projects/go-programming-cookbook-original/chapter8/validation, or use this as an exercise to write some of your own code!
  2. 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,
}
}
  1. 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
}
  1. 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
}
}
}
  1. Create a new directory named example and navigate to it.
  2. 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)
}
  1. Run go run main.go.
  2. You could also run the following:
$ go build
$ ./example

You should see the following output:

$ go run main.go
Listening on port :3333
  1. 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>
  1. The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.
  2. If you copied or wrote your own tests, go up one directory and run go test. Ensure that all tests pass.