martini-contrib / binding Goto Github PK
View Code? Open in Web Editor NEWMartini handler for mapping and validating a raw request into a structure.
License: MIT License
Martini handler for mapping and validating a raw request into a structure.
License: MIT License
I've just had a problem while trying to bind a struct with some dates on it like this
{"birthDate":"2015-02-19T00:00:00", "name": "name", "place": "Málaga"}
It evens doesn't show me any error... It just doesn't bind any data.
Anyone on this?
Am trying to use the README's example of a simple form with a file in it.
type UploadForm struct {
Title string `form:"title"`
Description string `form:"description"`
FileUpload *multipart.FileHeader `form:"fileUpload"`
}
and then the service routine...
m.Post("/upload", binding.MultipartForm(UploadForm{}), uploadHandler(uf UploadForm, r render.Render) {
log.Println("Upload Form: ", uf)
s := fmt.Sprintf("Posted an Upload: %v / %v \n", uf.Title, uf.Description)
r.HTML(200, "upload_success", s)
})
but uf, the form struct is always Nil.
Any ideas? Any ideas on how to fix?
func mapForm(formStruct reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors)
the last param is not a pointer,so if has errors,it don't get work
I have the following route:
m.Post("/users", binding.Bind(models.User{}), func(user models.User, r render.Render)
And I receive the following error message when I try to do a Post request:
"PANIC: reflect.Value.Interface: cannot return value obtained from unexported field or method"
My model is:
type User struct {
id int
UUID string `json:"uuid"`
Username string `json:"userName" form:"userName" binding:"required"`
Firstname string `json:"firstName" form:"Firstname" binding:"required`
Lastname string `json:"lastName" form:"Lastname" binding:"required`
Email string `json:"email" form:"Email" binding:"required`
IsActive bool `json:"isActive"`
DateJoined time.Time `json:"dateJoined"`
}
Sorry. The problem was the attribute "id". If I set this as "Id" works perfectly or if I set the tag form:"-"
too.
Thank you and sorry for the question. You can delete it.
allow preprocessing of params (before validation)
via e.g. Preprocess (binding.Preprocess)
use cases:
remove leading zeros
remove spaces
before putting data into a struct
before validating data
Preprocess might receive
req.Form
use "Set(key, value string)" to modify value
not sure whether to make a copy of req.Form or just modify it
type Embed struct {
Test string `form:"test" binding:"required"`
}
type Outer struct {
*Embed
}
Currently, if I bind to Outer{}, *Embed is nil after binding even if I pass the "test" parameter in the request.
I'm using martini and the binding package with Angular.
So if a requests fails the validation Angular gets back a binding.Errors() object which is all good.
What I'd like to do is to reuse the same error structure for other errors that might arise in the app basically so that I can have a consistent error checking experience with Angular. Say if the if I need to say a DB error occurred or something.
I'm currently doing this as Errors is exported, I just wanted to check my approach and wonder if we could formalise it or something so I don't feel so hacky.
Any thoughts?
Thanks
Currently the POST/PUT size is limited by the parseForm function in the http package (see https://golang.org/src/net/http/request.go#L834). However, the mulitform parser has a configurable limit (with MaxMemory), so why not apply this to the form-urlencoded requests too?
I'd really like to configure my own limit (we use Mandrill incoming webhooks, and sometimes the incoming data is larger than 10MB, Mandrill only supports application/x-www-form-urlencoded.
This possible resutlts in some code copy/pasting from the http package.
Was thinking it would be cool if binding could bind to martini params. If this is something you guys think would be cool I will try and find some time to hack it up.
example:
router.Post(
"/user/stream",
binding.Json(StreamRequest{}),
binding.Form(APICredentials{}),
AuthenticationMiddleware,
postUserStreamHandler,
)
there is no errors in postUserStreamHandler
but if i change the order, its fine.
Validation errors should chain or smth.
I think having type safe route values at least would really be an improvement
I hope I haven't overlooked anything though...
It could work with the struct tag "route:" or in the case of URL values "url:"
If you have a basic type that's a pointer, deserialization doesn't happen correctly. For instance:
type Post struct {
Id uint64
Content *string
}
causes a panic during the ValidateStruct step. The validation incorrectly assumes if it's a pointer, it's a pointer to a struct.
A full, stand alone test would be the following
package main
import (
"log"
"github.com/go-martini/martini"
"github.com/martini-contrib/binding"
)
type Post struct {
Id uint64
Content *string
}
func main() {
c := martini.Classic()
c.Post("/", binding.Json(Post{}), func(p Post) {
log.Println(p)
})
c.Run()
}
With a curl test of
curl -XPOST --data '{"content": "fubar"}' http://localhost:3000/
$GOROOT/src/pkg/reflect/type.go:654 (0x48a9cd)
(*rtype).NumField: panic("reflect: NumField of non-struct type")
$GOPATH/src/github.com/martini-contrib/binding/binding.go:191 (0x432512)
validateStruct: for i := 0; i < typ.NumField(); i++ {
$GOPATH/src/github.com/martini-contrib/binding/binding.go:171 (0x43372a)
func.005: errors = validateStruct(errors, obj)
I've also done a quick JSON test case with the following commit: losinggeneration@58a8515
Thanks for the great work you've done with martini and its companions like binding! :)
Now to the issue...It seems that it isn't possible to have a nested array of some own structs (yes, I saw no tests for it either) like so:
type Foo struct {
Bars []Bar
}
type Bar struct {
A string `form:"a"`
B string `form:"b"`
}
Binding Foo
with the above will not work and it ignores the form fields a
and b
even if they are passed in. Removing the []
and just having a single copy works fine (as you also have in the tests).
Before I start sending a PR I'd like to see what makes sense to do here.
Does it make sense to have an array of structs? I think so. Imagine having a form with a dynamic part where one can add zero or more of some set of fields. It would make sense to have a struct to represent those, and bang!, we have this very problem.
How would that look like on the form side to start with? I see that an array of strings for instance just takes all fields with the name and sticks them into the array. The implication of this is that it's not possible to have different fields with the same name. Would the same principle be a good approach now as well?
I'd like to hear what you guys who have worked on this a longer time have to say. What do you think is a good way to support what I ask for or is it even something you think makes sense to support?
Thanks!
As I re-write the tests, I'm finding a few more edge cases and issues. For instance, if you have a simple struct:
type Post struct {
Title string `form:"title" binding:"required"`
Content string `form:"content"`
}
Notice the Title
field is required. However, suppose your struct is also a Validator
, and its Validate()
method looked like:
func (p Post) Validate(errs *Errors, req *http.Request) {
if len(p.Title) < 10 {
errs.Fields["title"] = "Title is too short"
}
}
Now suppose a request comes in and the Title
field is left blank. Well, the obvious error to get back is simply "Required" -- who cares that it's too short, it didn't even get submitted!
Turns out that, right now, this implementation will overwrite the "Required" error which is added for you.
So should the Fields
and Overall
fields of the Errors
struct be made un-exported, and used with getters and setters instead? (The setter could prevent overwriting an existing error for that field.)
And should we allow multiple errors to the same key (i.e. instead of map[string]string
, we have map[string][]string
)? That would allow us to show both errors for the field... or however many... the downside is that it's slightly more complex.
See issue #3 for a similar discussion (though I'm not sure I want to include original input value, as that is even more complex).
Thoughts?
For Json Posts I have the following Type:
type Host struct {
Id string `json:"id,omitempty" gorethink:"id,omitempty"`
Name string `json:"name" gorethink:"name" binding:"required"`
Errors bool `json:"errors" gorethink:"errors" binding:"required"`
Updated time.Time `json:"updated" gorethink:"updated"`
}
The binding works perfectly. But when I set for the time.Time
struct the required
field tag with binding:"required
I a get a panic:
type Host struct {
Id string `json:"id,omitempty" gorethink:"id,omitempty"`
Name string `json:"name" gorethink:"name" binding:"required"`
Errors bool `json:"errors" gorethink:"errors" binding:"required"`
Updated time.Time `json:"updated" gorethink:"updated" binding:"required"`
}
This line is the problem:
if strings.Index(field.Tag.Get("binding"), "required") > -1 {
if field.Type.Kind() == reflect.Struct {
validateStruct(errors, fieldValue) //<- THIS LINE PANICS
} else if reflect.DeepEqual(zero, fieldValue) {
errors.Fields[field.Name] = RequireError
}
}
I know the what the problem is, but not really a good, general way to solve this. The problem is that the time.Time
struct does have no exported fields, just methods:
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
//
// It is declared as uintptr instead of int32 or uint32
// to avoid garbage collector aliasing in the case where
// on a 64-bit system the int32 or uint32 field is written
// over the low half of a pointer, creating another pointer.
// TODO(rsc): When the garbage collector is completely
// precise, change back to int32.
nsec uintptr
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// Only the zero Time has a nil Location.
// In that case it is interpreted to mean UTC.
loc *Location
}
And then the following line of code does not work:
fieldValue := val.Field(i).Interface()
Maybe someone has a good idea how to fix this.
tl;dr If a request comes in without a Content-Type, what do we do?
When the Bind() method is looking at the request to see which deserialization method to use, there is, I think, a bug if the "else" block is entered... suppose JSON was POSTed without a Content-Type. That "else" block is entered and, first, a JSON deserialization is attempted. If there are any errors, it uses form deserialization.
Two problems:
I'm willing to submit a fix for these, but would like some feedback first. How do we guess the Content-Type correctly and elegantly? Peek at the first character of the request body for a [
or {
character to detect JSON? That seems kind of unreliable.
Thoughts?
when struct has a struct field, it will miss values
Martini has moved to the go-martini github org. Make sure this package uses the new martini import path.
See this discussion for details.
Basically, implementing the Validate
method with a pointer receiver allows the validation to be used as a "prepare" function, e.g. trimming whitespace or lower-casing email addresses.
The tests are getting a bit unwieldy and should probably be separated into more files. Also, each test should be reviewed for effectiveness; for example, tests for empty payloads might need some revising to actually test what we think they're testing.
I've started splicing the tests out into separate files and cleaning them up. Many are being rewritten as I verify that they test what we think they're testing, and more test cases will be added. (Thank you, GoConvey, for the in-editor notifications of test results!)
Current progress is in the tests branch of this repository.
If a form field is bool, and it's submit value is false, then it will fail to validate "required"
package main
import "github.com/go-martini/martini"
import "github.com/martini-contrib/binding"
type TestStruct struct {
Test string `form:"test"`
}
func test(t TestStruct) {
}
func main() {
m := martini.Classic()
// need to bind with binding.Form instead to work
m.Get("/", binding.Bind(TestStruct{}), test)
m.Run()
}
The case where the request is a GET with query parameters without specifying Content-Type header, the binding will fail in the form of PANIC: Value not found for type TestStruct
.
Need to call binding.Form explicitly in this case.
I want to map a pointer of a struct to a specific interface, but I can't. I read codegangsta/martini-contrib#34 (comment) and I see the current limitation is introduced to avoid race conditions.
But I think this limitation is not necessary, for example, after changing line 402 of binding.go
context.MapTo(obj.Elem().Interface(), ifacePtr[0])
to
context.MapTo(obj.Interface(), ifacePtr[0])
it still works (although it requires a struct to implement the interface methods with a pointer receiver). Because that obj
is created by reflect.New
each time a request comes in, there are no race conditions.
rt.
After coming from .NET MVC, I am accustom to model binding. However, there were many more validation rules you could set on the model fields.
For example, now you have
type BlogPost struct {
Title string `form:"title" json:"title" binding:"required"`
}
And you have the "Binding:required", which will add an error if its not there. Is there any chance you'd consider adding some other validation tags, like for example, marking a field as an email? There are some other really common ones too, like credit card, date, minlength, maxlength etc. Sure you could just add your own validator, but it would be nice to be able to add these to the model and have that taken care of for you:
type BlogPost struct {
Title string `form:"title" json:"title" binding:"required" minlength:"10"`
Email string `form:"title" json:"title" binding:"required" email:"true"`
}
The syntax above is just an example, not necessarily how I would do it. Is this something that might belong here?
Just a thought, but to make Form validation more useful, the errors should probably include the original value as well. It seems like it'd be simpler to model forms with all of the information coming from one place. I can implement, but I'm curious on everyone's thoughts first.
type ThisObj struct {
ID string `json:"id"`
Name string `json:"name" binding:"required"`
Online bool `json:"online" binding:"required"`
}
In the above example, bind gives me a RequiredError
when I POST the following JSON string:
{
"name": "test",
"online": false
}
However, when I set online
to 'true' in the above JSON string, binding works fine. I assume this is a bug as a false
value should still fulfill the required
tag?
from @abh
With the code below I get [martini] PANIC: Value not found for type *main.checkForm
if I run curl -XPOST -dqueue=1 http://localhost:1234/check/127.0.0.1
and if I run the same without any form parameters I get a 400 bad request response and this JSON in the response {"overall":{"DeserializationError":"mime: no media type"},"fields":{}}
.
Neither is what I expected. I was using roughly the same code with the now removed form middleware.
type checkForm struct {
Queue string `form:"queue"`
}
func runWeb() {
m := martini.Classic()
m.Use(render.Renderer())
m.Post("/check/:ip",
binding.Bind(checkForm{}),
func(res http.ResponseWriter, params martini.Params, options *checkForm, rndr render.Render) {
....
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.