9

I'm trying to capture an array of Post values from HTML form using Go / Gin Gonic -- in PHP I would use something like:

<form method="POST" enctype="multipart/form-data" action="mygo">
 <input type=hidden name="emails[]" value="email1@email.com">
 <input type=hidden name="emails[]" value="email2@email.com">
 <input type=hidden name="emails[]" value="email3@email.com">
</form>

However this doesn't seem to work with Gin Gonic (or Go for that matter).

I've also tried:

<form method="POST" enctype="multipart/form-data" action="mygo">
 <input type=hidden name="emails" value="email1@email.com">
 <input type=hidden name="emails" value="email2@email.com">
 <input type=hidden name="emails" value="email3@email.com">
</form>

As elsewhere it is suggested that doing this would cause c.PostForm("emails") to return a slice. However in practice it seems that this instead returns the last value as a string instead :(

Interestingly, c.Request.PostForm returns an empty map, even if c.Request.ParseForm() is called first. What am I doing wrong?

Go Form:

func main() {
// ...
    router.POST("mygo",parseFunc)
}

func mygo(c *gin.Context) {
  c.Request.ParseForm()
  log.Printf("%v",c.Request.PostForm["emails"]) // ""
  log.Printf("%v",c.PostForm("emails") // "email3@email.com"
}
Zoyd
  • 3,449
  • 1
  • 18
  • 27
BadPirate
  • 25,802
  • 10
  • 92
  • 123
  • 1
    To get multiple values, you need to use the `Request.Form` or `Request.PostForm` maps directly. If those aren't there please show a [mcve]. For instance, the form you show will submit via a _GET_ to the same url that generated the form. – JimB Oct 11 '16 at 20:26
  • @JimB - Edited to show MCV example :) – BadPirate Oct 11 '16 at 21:05
  • Sorry, you're going to have to create a more complete example, because what you've shown should still work.. – JimB Oct 11 '16 at 21:57
  • @JimB - Ah, I think my issues were two fold. First c.Request.PostForm is likely not working at all because of something in my form that is not shown in the example `enctype="multipart/form-data"` .. Technically I don't think I need my form to be multipart and if I did I think it would just mean calling `ParseMultipartForm` rather than `ParseForm` -- `ParseMultipartForm` is however called when I call `c.PostForm` using gonic, but it appears the gonic method manually returns [only the first value](https://github.com/gin-gonic/gin/blob/develop/context.go#L267-L269) - Will check and update. – BadPirate Oct 11 '16 at 22:04
  • The methods only returning the first value is expected, because that's how the std lib `http.Request` methods works. – JimB Oct 11 '16 at 22:06

4 Answers4

8

In order to make it works you have two ways here

<form method="POST" enctype="multipart/form-data" action="mygo">
 <input type=hidden name="emails" value="email1@email.com">
 <input type=hidden name="emails" value="email2@email.com">
 <input type=hidden name="emails" value="email3@email.com">
</form>

r.POST("/", func(c *gin.Context) {
        c.Request.ParseMultipartForm(1000)
        for key, value := range c.Request.PostForm {
            fmt.Println(key,value)
        }
    })

either

    <form method="POST" action="mygo">
     <input type=hidden name="emails" value="email1@email.com">
     <input type=hidden name="emails" value="email2@email.com">
     <input type=hidden name="emails" value="email3@email.com">
    </form>

 r.POST("/", func(c *gin.Context) {
            c.Request.ParseForm()
            for key, value := range c.Request.PostForm {
                fmt.Println(key,value)
            }
        })

Both gives the same result

emails [email1@email.com email2@email.com email3@email.com]
Marsel Novy
  • 1,777
  • 16
  • 23
  • Kudos for a general-purpose answer that will work in many other situations! However, I guess that the OP would prefer one of the other solutions that target specifically their own particular issue. Nevertheless, I found your trick quite useful for extracting _all_ form values, in those cases when I don't know beforehand how many there will be! – Gwyneth Llewelyn Apr 04 '22 at 11:23
6

With the latest version of gin-gonic (v1.2) you can just:

emails := c.PostFormArray("emails")

Then emails will be a []string.

Parse and iterate isn't more necessary.

algorix
  • 301
  • 3
  • 7
2

Issue with this code is two fold. Posting my answer in case someone else has either of these problems.

  1. c.Request.PostForm returns empty in this case because the form is multipart and ParseForm only parses non-multipart forms. In order to get the data here either change the form type to non-multipart or call ParseMultipartForm before accessing post values.
  2. c.PostForm() only returns the first value because thats what the wrapper in Gin Gonic does. In order to access all the values, it is necessary to access the original c.Request.PostForm data, and then AFTER you parse it properly.

Thanks @JimB for the assist.

BadPirate
  • 25,802
  • 10
  • 92
  • 123
2

I know its late about 5 years, but since I was searching for this also, I think it will still be useful for someone if I share my answer.

One of the simplest ways would be this:

in HTML

<form method="POST" enctype="multipart/form-data" action="mygo">
 <input type=hidden name="emails[]" value="email1@email.com">
 <input type=hidden name="emails[]" value="email2@email.com">
 <input type=hidden name="emails[]" value="email3@email.com">
</form>

in GO:

emails, ok := c.Request.PostForm["emails[]"]
if ok {
    fmt.Println("this are your emails: ", emails)
}
Jacky Supit
  • 469
  • 3
  • 15