echo: "binding element must be a struct" introduced with path params binding
Issue Description
This is simillar to #988 and introduced by https://github.com/labstack/echo/commit/858270f6f5e4fd02d9e147e8a6b0d97df023f14b in a specific situation: taking the address of your struct twice and sending it to bind.
u := &user{}
c.Bind(&u)
Because of the newly introduced params binding, when the struct’s type is checked, it fails here:
func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
typ := reflect.TypeOf(ptr).Elem()
val := reflect.ValueOf(ptr).Elem()
if typ.Kind() != reflect.Struct {
return errors.New("binding element must be a struct")
}
Before the params binding was introduced, bindData
was not called for POST/PUT/PATCH, because for these methods you directly decode the body into the given struct.
And a mention: If you have id
both as path param and json property in the request body, the value from the body will overwrite the one from the path. This could lead to confusion and bugs in projects.
Checklist
- Dependencies installed
- No typos
- Searched existing issues and docs
Expected behaviour
If this is known and planned for next major version, than it’s OK, else compatibility should not be broken.
Actual behaviour
See Expected behaviour
Steps to reproduce
Take the address of the struct sent at bind twice and make a request to a server (see Working code to debug
).
curl -X PUT -H "Content-Type: application/json" -d '{"name":"John"}' localhost:8811/users/1
Working code to debug
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"
)
type user struct {
ID int `json:"id" param:"id"`
Name string `json:"name"`
}
func main() {
s := echo.New()
s.Use(middleware.Logger())
s.PUT("/users/:id", func(c echo.Context) error {
u := &user{}
if err := c.Bind(&u); err != nil {
log.Fatal(err)
}
log.Print(u)
return nil
})
s.Start(":8811")
}
Version/commit
https://github.com/labstack/echo/commit/858270f6f5e4fd02d9e147e8a6b0d97df023f14b
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 13
- Comments: 39 (6 by maintainers)
Commits related to this issue
- downgrade to echo v4.1.5 cause later versions are broken https://github.com/labstack/echo/issues/1356 — committed to elkrammer/kegger by elkrammer 4 years ago
- Fix: allow bound interface to be a slice of data Do not throw an error when binding to a slice, if binding occurs on path- or query-parameters => data is very likely to be found in the body, which wi... — committed to benoitmasson/echo by benoitmasson 4 years ago
- Fix: allow bound interface to be a slice of data Do not throw an error when binding to a slice, if binding occurs on path- or query-parameters => data is very likely to be found in the body, which wi... — committed to benoitmasson/echo by benoitmasson 4 years ago
Please don’t close this! I’m stuck back at version 4.1.6, which was the last release without this bug.
Also, binding to slices is returning the same error.
+1 On this issue. This is a bug that breaks existing functionality and has been an issue since early August. A patch release would be appreciated, so that I don’t have to pin at v4.1.6 indefinitely.
Thanks!
The description of bind() clearly states:
Why is it also parsing Param and QueryParam which are part of the header? Adding the Param and QueryParam parser into the binder was a breaking change and should not have happened. Developers like me expected this function only parsing the body as the description states.
I’m with @cdukes and his suggestion of adding BindParams() and BindQueryParams() but the Bind() should only parse the body like it always did (if added to 4.x).
I wouldn’t like to see this issue automatically close. It’s a revision version that broke compatibility.
Breakage on existing feature really hurts, if couldn’t be fixed quickly, maybe a temporary revert and a new version is a good idea?
Same - I upgraded to v4 yesterday and this broke (Bind on a json array now returning an error).
I’m confused about this ticket. The documentation doesn’t say that the binding element must be a struct.
On v4.1.6 and before, this worked fine. A change was introduced post-v4.1.6 that broke existing functionality. If this was intentional, then shouldn’t the next release have been 5.0.0, following semver? If it wasn’t intentional, then shouldn’t the change be reverted?
Beyond that, I’m having trouble figuring out why this change happened in the first place. Looking at the v4.1.7 release, I think issue #988 is the only one relevant here, but that ticket also has the binding element issue
Hi, an alternative to this problem would be this: https://www.buggger.com/en/rqt/5ecb9ceacb08b
Until, the new behaviour is fixed or reverted, I agree that this is the easiest workaround when you only need to read a JSON Body with a non-struct like an array. A more verbose code example:
I did some tricks to temporary solve the problem by adding
c.SetParamNames()
before bind.@vishr, param binding is a feature that I wanted, but I think it’s better if it gets reverted to fix breaking change it introduced. And then planned again for another release.
Hi,
is there a timeline to get the different solutions offered in different merge requests actually on master?
This is currently quite breaking for us, and many others.
Can this be picked up again?
Thanks!
This is almost a year and half old and it hasn’t been fixed? Why would anyone want to rely on a framework where these basic functionality type of issues aren’t resolved?
I also want to note that even using a struct with a PATCH method seems broken.
@benoitmasson yep, I saw it, thanks! At this point it seems as if it would be better to fork the repo and try to keep an up to date version based on this one.
Hi all,
My PR was closed due to inactivity, but if you really need this to work, apply this quick patch and things should work as expected.
This is also biting my team.
Is it possible that add a flag to enable or disable param binding feature and disable by default?
Thanks for verifying @jherman , @dahu33 . Closing!
Issues with binding should be resolved with echo v4.2 (see release notes) with various PRs merged that are referenced here.
So feedback is welcome, if there are open issues left… Please let us know.
I totally agree, and also think your suggestion of separate
BindBody()
, … functions is the most appropriate (along with a genericBind()
if needed, but then this one should allow onlystruct
ormap[string]string
bindings).This has already been suggested somewhere (another issue maybe?), but not implemented yet, although it would not be too hard to code… maybe the maintainer could tell us about it, as for me, I don’t know what the plans are for the next versions and I don’t want to waste time on something which has no chance of getting merged (my own PR is enough for me right now).
The bug comes from this commit (from this PR), to solve this proposal.
What is done is done, but I agree with you, breaking compatibility on such critical functions is not something we expect… unit tests were not complete enough.
PR #1574 includes a quick fix which works in most, common, cases (by ignoring the binding error on the parameter). Let’s hope it gets merged soon… If you have any idea or comments to improve it, feel free to tell me about it.
This workaround is causing interesting problems! If the c.bind() afterwards fails, other routes using params will suddenly fail with “Not found”. Only restarting the echo service resolves this.
I am now using ioutil.ReadAll(c.Request().Body) and Unmarshal it later into the struct.