Skip to content

Commit 031ea05

Browse files
committed
Automatic JSON binding to struct in parameters, also changed validation to not store validation state via a cookie, since with a json request/response no redirect is required and therefor no persistent data is required.
Also renamed local variable error to err so no package conflict error will occur
1 parent 23418a6 commit 031ea05

File tree

4 files changed

+82
-38
lines changed

4 files changed

+82
-38
lines changed

binder.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strconv"
1515
"strings"
1616
"time"
17+
"encoding/json"
1718
)
1819

1920
// A Binder translates between string parameters and Go data structures.
@@ -290,7 +291,15 @@ func unbindSlice(output map[string]string, name string, val interface{}) {
290291
}
291292

292293
func bindStruct(params *Params, name string, typ reflect.Type) reflect.Value {
293-
result := reflect.New(typ).Elem()
294+
resultPointer := reflect.New(typ)
295+
result := resultPointer.Elem()
296+
if params.JsonRequest {
297+
// Try to inject the response as a json into the created result
298+
if err := json.Unmarshal(params.Json, resultPointer.Interface()); err != nil {
299+
WARN.Println("W: bindStruct: Unable to unmarshal request:", name, err)
300+
}
301+
return result
302+
}
294303
fieldValues := make(map[string]reflect.Value)
295304
for key := range params.Values {
296305
if !strings.HasPrefix(key, name+".") {

binder_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package revel
66

77
import (
8+
"encoding/json"
89
"fmt"
910
"io"
1011
"io/ioutil"
@@ -165,6 +166,16 @@ var fileBindings = []struct{ val, arrval, f interface{} }{
165166
{(*io.ReadSeeker)(nil), []io.ReadSeeker{}, ioutil.ReadAll},
166167
}
167168

169+
func TestJsonBinder(t *testing.T) {
170+
// create a structure to be populated
171+
foo := struct {A string}{}
172+
d, _ := json.Marshal(map[string]string{"a": "b"})
173+
params := &Params{JsonRequest: true, Json: d}
174+
ParseParams(params, NewRequest(getMultipartRequest()))
175+
actual := Bind(params, "test", reflect.TypeOf(foo))
176+
valEq(t, "TestJsonBinder", reflect.ValueOf(actual.Interface().(struct {A string}).A), reflect.ValueOf("b"))
177+
}
178+
168179
func TestBinder(t *testing.T) {
169180
// Reuse the mvc_test.go multipart request to test the binder.
170181
params := &Params{}

params.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/url"
1010
"os"
1111
"reflect"
12+
"io/ioutil"
1213
)
1314

1415
// Params provides a unified view of the request params.
@@ -31,6 +32,8 @@ type Params struct {
3132

3233
Files map[string][]*multipart.FileHeader // Files uploaded in a multipart form
3334
tmpFiles []*os.File // Temp files used during the request.
35+
Json []byte // JSON data from request body
36+
JsonRequest bool // True if request was JSON
3437
}
3538

3639
// ParseParams parses the `http.Request` params into `revel.Controller.Params`
@@ -56,6 +59,20 @@ func ParseParams(params *Params, req *Request) {
5659
params.Form = req.MultipartForm.Value
5760
params.Files = req.MultipartForm.File
5861
}
62+
case "application/json":
63+
fallthrough
64+
case "text/json":
65+
if req.Body!=nil {
66+
if content, err := ioutil.ReadAll(req.Body);err==nil{
67+
// We wont bind it until we determine what we are binding too
68+
params.Json = content
69+
params.JsonRequest = true
70+
} else {
71+
ERROR.Println("Failed to ready request body bytes",err)
72+
}
73+
} else {
74+
INFO.Println("Json post received with empty body")
75+
}
5976
}
6077

6178
params.Values = params.calcValues()

validation.go

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -185,49 +185,56 @@ func (v *Validation) Check(obj interface{}, checks ...Validator) *ValidationResu
185185

186186
// ValidationFilter revel Filter function to be hooked into the filter chain.
187187
func ValidationFilter(c *Controller, fc []Filter) {
188-
errors, err := restoreValidationErrors(c.Request.Request)
189-
c.Validation = &Validation{
190-
Errors: errors,
191-
keep: false,
192-
}
193-
hasCookie := (err != http.ErrNoCookie)
188+
// If json request, we shall assume json response is intended,
189+
// as such no validation cookies should be tied response
190+
if c.Params != nil && c.Params.JsonRequest {
191+
c.Validation = &Validation{}
192+
fc[0](c, fc[1:])
193+
} else {
194+
errors, err := restoreValidationErrors(c.Request.Request)
195+
c.Validation = &Validation{
196+
Errors: errors,
197+
keep: false,
198+
}
199+
hasCookie := (err != http.ErrNoCookie)
194200

195-
fc[0](c, fc[1:])
201+
fc[0](c, fc[1:])
196202

197-
// Add Validation errors to ViewArgs.
198-
c.ViewArgs["errors"] = c.Validation.ErrorMap()
203+
// Add Validation errors to ViewArgs.
204+
c.ViewArgs["errors"] = c.Validation.ErrorMap()
199205

200-
// Store the Validation errors
201-
var errorsValue string
202-
if c.Validation.keep {
203-
for _, error := range c.Validation.Errors {
204-
if error.Message != "" {
205-
errorsValue += "\x00" + error.Key + ":" + error.Message + "\x00"
206+
// Store the Validation errors
207+
var errorsValue string
208+
if c.Validation.keep {
209+
for _, err := range c.Validation.Errors {
210+
if err.Message != "" {
211+
errorsValue += "\x00" + err.Key + ":" + err.Message + "\x00"
212+
}
206213
}
207214
}
208-
}
209215

210-
// When there are errors from Validation and Keep() has been called, store the
211-
// values in a cookie. If there previously was a cookie but no errors, remove
212-
// the cookie.
213-
if errorsValue != "" {
214-
c.SetCookie(&http.Cookie{
215-
Name: CookiePrefix + "_ERRORS",
216-
Value: url.QueryEscape(errorsValue),
217-
Domain: CookieDomain,
218-
Path: "/",
219-
HttpOnly: true,
220-
Secure: CookieSecure,
221-
})
222-
} else if hasCookie {
223-
c.SetCookie(&http.Cookie{
224-
Name: CookiePrefix + "_ERRORS",
225-
MaxAge: -1,
226-
Domain: CookieDomain,
227-
Path: "/",
228-
HttpOnly: true,
229-
Secure: CookieSecure,
230-
})
216+
// When there are errors from Validation and Keep() has been called, store the
217+
// values in a cookie. If there previously was a cookie but no errors, remove
218+
// the cookie.
219+
if errorsValue != "" {
220+
c.SetCookie(&http.Cookie{
221+
Name: CookiePrefix + "_ERRORS",
222+
Value: url.QueryEscape(errorsValue),
223+
Domain: CookieDomain,
224+
Path: "/",
225+
HttpOnly: true,
226+
Secure: CookieSecure,
227+
})
228+
} else if hasCookie {
229+
c.SetCookie(&http.Cookie{
230+
Name: CookiePrefix + "_ERRORS",
231+
MaxAge: -1,
232+
Domain: CookieDomain,
233+
Path: "/",
234+
HttpOnly: true,
235+
Secure: CookieSecure,
236+
})
237+
}
231238
}
232239
}
233240

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy