kataras / iris

The fastest HTTP/2 Go Web Framework. New, modern and easy to learn. Fast development with Code you control. Unbeatable cost-performance ratio :rocket:
https://www.iris-go.com
BSD 3-Clause "New" or "Revised" License
25.23k stars 2.47k forks source link

[v12.1.8] How to `Context` support a array of form value request or ajax. #1503

Closed conero closed 4 years ago

conero commented 4 years ago

I try get some simple K-V form use Context.FormValue or Context.FormValues that no probem, but when I really need array form there is no way to handler. And I search issuses found a same 701, maybe it's possible use https://github.com/monoculum/formam. It's better if the framework support than the third library. So I ask for help if there is better path to resoult it, will be geart. 20200503140514

A test/debug code example:

func (c *Router) PostSaveAuth() {
    //iris.Context
    ctx := c.Ctx
    type PostDataForm struct {
        RouterId int `json:"router_id"`
        RoleList []int `json:"role_list"`
    }
    var postData PostDataForm

    body, err := ctx.GetBody()
    fmt.Println(string(body), err)
    fmt.Println(ctx.ReadJSON(&postData), postData, ":postData")
    fmt.Println(ctx.FormValues(), ":2")
    fmt.Println(url.QueryUnescape(string(body)))

    var dataTest map[string]interface{}
    fmt.Println(ctx.ReadForm(&dataTest), dataTest, ":postData2")
        //.... and more
}

Output:

        //router_id=1&role_list%5B0%5D=1&role_list%5B1%5D=2 <nil>
        //unexpected end of JSON input {0 []} :postData
        //map[] :2
        //router_id=1&role_list[0]=1&role_list[1]=2 <nil>
        //<nil> map[] :postData2
kataras commented 4 years ago

Hello @conero, First of all, formam is no longer used, that issue is not up-to-dated any more (2017).

Secondly, I tested it and arrays are supported:

https://github.com/kataras/iris/blob/350f72a1434d3f90f4b315361dd30e5ab644126f/_examples/http_request/read-form/main.go#L11

I think your PostDataForm is missing the "form" tag? Try:

type PostDataForm struct {
        RouterId int `json:"router_id" form:"router_id"`
        RoleList []int `json:"role_list" form:"role_list"`
    }

It's better if the framework support than the third library

Iris supports everything, if formam works with net/http (which it does) then it works with Iris too, we have nothing more to do to make it compatible, it's already iris-compatible :) However you do not need this, you probably missed the struct tags on the PostDataForm fields. Try my solution and tell me if that helped you.

Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go

kataras commented 4 years ago

Also, at the upcoming release(v12.2.0) you may want to use the new ctx.ReadBody which will bind any available request to a struct value. Example at: https://github.com/kataras/iris/blob/350f72a1434d3f90f4b315361dd30e5ab644126f/_examples/http_request/read-body/main.go#L33-L42

conero commented 4 years ago

@kataras Thank you, agan. That disturb u from ur busy time with nice patience. I found out litte problem from usage of npm qs qs.stringify({ a: ['b', 'c', 'd'] }) => a[0]=b&a[1]=c&a[2]=d

iris.Context.ReadForm cannot parse the format, so I change to whart I learn from ur geart tips.

kataras commented 4 years ago

You are welcome @conero, I see!

conero commented 4 years ago

First, codes don't work like I want, it's weird when I change order iris.Context.ReadForm with iris.Context.GetBody(). Anyway Iit's Solved. Thanks, and looking forward to a new version release(v12.2.0).

body, err := ctx.GetBody()

fmt.Println(ctx.ReadForm(&postData), postData, ":postData")
fmt.Println(ctx.ReadJSON(&postData), postData, ":postData-ctx.ReadJSON")

fmt.Println(string(body), err)
fmt.Println(ctx.FormValues(), ":2")
fmt.Println(url.QueryUnescape(string(body)))

// <nil> { []} :postData
// unexpected end of JSON input { []} :postData-ctx.ReadJSON
// router_id=1&role_list=1&role_list=2 <nil>
// map[] :2
// router_id=1&role_list=1&role_list=2 <nil>
// <nil> map[] :postData2

Changed codes then work out.

var postData = PostDataForm{}

fmt.Println(ctx.ReadForm(&postData), postData, ":postData")
fmt.Println(ctx.ReadJSON(&postData), postData, ":postData-ctx.ReadJSON")

body, err := ctx.GetBody()

//fmt.Println(ctx.ReadForm(&postData), postData, ":postData")
//fmt.Println(ctx.ReadJSON(&postData), postData, ":postData-ctx.ReadJSON")

fmt.Println(string(body), err)
fmt.Println(ctx.FormValues(), ":2")
fmt.Println(url.QueryUnescape(string(body)))

//<nil> {1 [1 2]} :postData
//unexpected end of JSON input {1 [1 2]} :postData-ctx.ReadJSON
// <nil>
//map[role_list:[1 2] router_id:[1]] :2
// <nil>
//schema: interface must be a pointer to struct map[] :postData2
//[1 2] true map[role_list:[1 2] router_id:[1]]
kataras commented 4 years ago

Hello @conero, if you call ReadBody then the whole body is consumed, it's the default behavior of an io.Reader of an HTTP request, to change that behavior in Iris you have to pass the iris.WithoutBodyConsumptionOnUnmarshal option, we have an example of that too: _examples/http_request/read-many