/
validate.go
132 lines (121 loc) · 3.5 KB
/
validate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// This file is part of graze/golang-service
//
// Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
// license: https://github.com/graze/golang-service/blob/master/LICENSE
// link: https://github.com/graze/golang-service
/*
Package validate provides a simple interface for validating JSON user input
Example:
type Item struct {
Name string `json:"name"`
Description string `json:"description"`
}
func (i *Item) Validate(ctx context.Context) error {
if RuneCountInString(i.Name) == 0 {
return fmt.Errorf("the field: name must be provided and not empty")
}
return nil
}
func CreateItem(w http.ResponseWriter, r *http.Request) {
item := &Item{}
if err := validate.JSONRequest(ctx, r, item); err != nil {
w.WriteHeader(400)
return
}
}
*/
package validate
import (
"context"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"net/http"
)
type (
// IOError for when we fail to read the stream
IOError struct{ error }
)
// Validatable items can self validate
type Validatable interface {
Validate(ctx context.Context) error
}
// JSONRequest takes an http request, decodes the json and validates the input against a Validatable output
// the Validatable variable will get populated with the contents of the body provided by *http.Request
//
// The base set of error types returned from this method are:
// validate.IOError
// *json.SyntaxError
// *json.UnmarshalFieldError
// *json.UnmarshalTypeError
//
// Usage:
// type Item struct {
// Name string `json:"name"`
// Description string `json:"description"`
// }
//
// func (i *Item) Validate(ctx context.Context) error {
// if RuneCountInString(i.Name) == 0 {
// return fmt.Errorf("the field: name must be provided and not empty")
// }
// return nil
// }
//
// func CreateItem(w http.ResponseWriter, r *http.Request) {
// item := &Item{}
// if err := validate.JSONRequest(ctx, r, item); err != nil {
// w.WriteHeader(400)
// return
// }
// }
func JSONRequest(ctx context.Context, r *http.Request, v Validatable) error {
return Reader(ctx, r.Body, json.Unmarshal, v)
}
// XMLRequest takes an http request docodes the xml into an item and validates the provided item
//
// The base set of error types returned from this method are:
// validate.IOError
// *xml.SyntaxError
// *xml.TagPathError
// *xml.UnmarshalError
func XMLRequest(ctx context.Context, r *http.Request, v Validatable) error {
return Reader(ctx, r.Body, xml.Unmarshal, v)
}
// Reader takes a generic io.Reader, an unmarshaller and validates the input against a Validatable item
// the Validatable variable will get populated with the contents of the body provided by *http.Request
//
// Usage:
// type ApiInput struct {
// Name string `json:"name"`
// Description string `json:"description"`
// }
//
// func (i *ApiInput) Validate(ctx context.Context) error {
// if utf8.RuneCountInString(i.Name) == 0 {
// return fmt.Errorf("the field: name must be provided and not empty")
// }
// return nil
// }
//
// func main() {
// input := &ApiInput{}
// if err := validate.Reader(ctx, reader, json.Unmarshal, input); err != nil {
// log.Panic(err)
// }
// }
func Reader(ctx context.Context, r io.Reader, unmarshaller func(data []byte, v interface{}) error, v Validatable) error {
str, err := ioutil.ReadAll(r)
if err != nil {
return &IOError{err}
}
if err = unmarshaller(str, v); err != nil {
return err
}
return v.Validate(ctx)
}