Skip to content

Commit 7556578

Browse files
authored
Add YAML support (#40)
1 parent f2ebb45 commit 7556578

6 files changed

Lines changed: 332 additions & 22 deletions

File tree

bind_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ func Test_Bind(t *testing.T) {
3535
}
3636
})
3737

38+
Convey("Bind YAML", func() {
39+
for _, testCase := range yamlTestCases {
40+
performYamlTest(t, Bind, testCase)
41+
}
42+
})
43+
3844
Convey("Bind multipart form", func() {
3945
for _, testCase := range multipartFormTestCases {
4046
performMultipartFormTest(t, Bind, testCase)

binding.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
"github.com/unknwon/com"
3333
"gopkg.in/macaron.v1"
34+
"gopkg.in/yaml.v3"
3435
)
3536

3637
func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
@@ -43,6 +44,8 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
4344
_, _ = ctx.Invoke(MultipartForm(obj, ifacePtr...))
4445
case strings.Contains(contentType, "json"):
4546
_, _ = ctx.Invoke(Json(obj, ifacePtr...))
47+
case strings.Contains(contentType, "yaml"):
48+
_, _ = ctx.Invoke(Yaml(obj, ifacePtr...))
4649
default:
4750
var errors Errors
4851
if contentType == "" {
@@ -60,6 +63,7 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
6063

6164
const (
6265
_JSON_CONTENT_TYPE = "application/json; charset=utf-8"
66+
_YAML_CONTENT_TYPE = "text/yaml; charset=utf-8"
6367
STATUS_UNPROCESSABLE_ENTITY = 422
6468
)
6569

@@ -207,10 +211,39 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
207211
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
208212
}
209213
}
214+
if errors != nil {
215+
ctx.Map(errors)
216+
return
217+
}
210218
validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
211219
}
212220
}
213221

222+
// Yaml is middleware to deserialize a YAML payload from the request
223+
// into the struct that is passed in. The resulting struct is then
224+
// validated, but no error handling is actually performed here.
225+
// An interface pointer can be added as a second argument in order
226+
// to map the struct to a specific interface.
227+
func Yaml(yamlStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
228+
return func(ctx *macaron.Context) {
229+
var errors Errors
230+
ensureNotPointer(yamlStruct)
231+
yamlStruct := reflect.New(reflect.TypeOf(yamlStruct))
232+
if ctx.Req.Request.Body != nil {
233+
defer ctx.Req.Request.Body.Close()
234+
err := yaml.NewDecoder(ctx.Req.Request.Body).Decode(yamlStruct.Interface())
235+
if err != nil && err != io.EOF {
236+
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
237+
}
238+
}
239+
if errors != nil {
240+
ctx.Map(errors)
241+
return
242+
}
243+
validateAndMap(yamlStruct, ctx, errors, ifacePtr...)
244+
}
245+
}
246+
214247
// URL is the middleware to parse URL parameters into struct fields.
215248
func URL(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
216249
return func(ctx *macaron.Context) {

common_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,26 @@ import (
2727
type (
2828
// For basic test cases with a required field
2929
Post struct {
30-
Title string `form:"title" json:"title" binding:"Required"`
31-
Content string `form:"content" json:"content"`
30+
Title string `form:"title" json:"title" yaml:"title" binding:"Required"`
31+
Content string `form:"content" json:"content" yaml:"content"`
3232
}
3333

3434
// To be used as a nested struct (with a required field)
3535
Person struct {
36-
Name string `form:"name" json:"name" binding:"Required"`
37-
Email string `form:"email" json:"email"`
36+
Name string `form:"name" json:"name" yaml:"name" binding:"Required"`
37+
Email string `form:"email" json:"email" yaml:"email"`
3838
}
3939

4040
// For advanced test cases: multiple values, embedded
4141
// and nested structs, an ignored field, and single
4242
// and multiple file uploads
4343
BlogPost struct {
4444
Post
45-
Id int `binding:"Required"` // JSON not specified here for test coverage
46-
Ignored string `form:"-" json:"-"`
47-
Ratings []int `form:"rating" json:"ratings"`
48-
Author Person `json:"author"`
49-
Coauthor *Person `json:"coauthor"`
45+
Id int `binding:"Required"` // JSON and YAML not specified here for test coverage
46+
Ignored string `form:"-" json:"-" yaml:"-"`
47+
Ratings []int `form:"rating" json:"ratings" yaml:"ratings"`
48+
Author Person `json:"author" yaml:"author"`
49+
Coauthor *Person `json:"coauthor" yaml:"coauthor"`
5050
HeaderImage *multipart.FileHeader
5151
Pictures []*multipart.FileHeader `form:"picture"`
5252
unexported string `form:"unexported"` //nolint
@@ -79,8 +79,8 @@ type (
7979
}
8080

8181
Group struct {
82-
Name string `json:"name" binding:"Required"`
83-
People []Person `json:"people" binding:"MinSize(1)"`
82+
Name string `json:"name" yaml:"name" binding:"Required"`
83+
People []Person `json:"people" yaml:"people" binding:"MinSize(1)"`
8484
}
8585

8686
UrlForm struct {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ go 1.12
55
require (
66
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
77
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e
8-
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a // indirect
98
gopkg.in/macaron.v1 v1.3.5
9+
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8
1010
)

go.sum

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,15 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49N
2121
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
2222
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
2323
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
24-
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
25-
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
26-
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
2724
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
2825
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
29-
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3026
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
31-
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
32-
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
3327
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
34-
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
35-
golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
28+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
29+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3630
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
3731
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
38-
gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs=
39-
gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI=
4032
gopkg.in/macaron.v1 v1.3.5 h1:FUA16VFBojxzfU75KqWrV/6BPv9O2R1GnybSGRie9QQ=
4133
gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
34+
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8 h1:tH9C0MON9YI3/KuD+u5+tQrQQ8px0MrcJ/avzeALw7o=
35+
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)