I'm a PHP Dev. But currently moving to Golang... I'm trying to retrieve data from a Form (Post method):
<!-- A really SIMPLE form -->
<form class="" action="/Contact" method="post">
<input type="text" name="Contact[Name]" value="Something">
<input type="text" name="Contact[Email]" value="Else">
<textarea name="Contact[Message]">For this message</textarea>
<button type="submit">Submit</button>
</form>
In PHP I would simple use this to get the data:
<?php
print_r($_POST["Contact"])
?>
// Output would be something like this:
Array
(
[Name] => Something
[Email] => Else
[Message] => For this message
)
BUT in go... either I get one by one or the whole thing but not the Contact[] Array only such as PHP
I thought about 2 solutions:
1) Get one by one:
// r := *http.Request
err := r.ParseForm()
if err != nil {
w.Write([]byte(err.Error()))
return
}
contact := make(map[string]string)
contact["Name"] = r.PostFormValue("Contact[Name]")
contact["Email"] = r.PostFormValue("Contact[Email]")
contact["Message"] = r.PostFormValue("Contact[Message]")
fmt.Println(contact)
// Output
map[Name:Something Email:Else Message:For this Message]
Note that the map keys are the whole: "Contact[Name]"...
2) Range whole map r.Form
and "parse|obtain" those values with Prefix
"Contact[" and then replacing "Contact[" and "]" with empty string
so I can get the Form array Key only such the PHP Example
I went for this work around by my own but... ranging over the whole form may not be a good idea (?)
// ContactPost process the form sent by the user
func ContactPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
err := r.ParseForm()
if err != nil {
w.Write([]byte(err.Error()))
return
}
contact := make(map[string]string)
for i := range r.Form {
if strings.HasPrefix(i, "Contact[") {
rp := strings.NewReplacer("Contact[", "", "]", "")
contact[rp.Replace(i)] = r.Form.Get(i)
}
}
w.Write([]byte(fmt.Sprint(contact)))
}
//Output
map[Name:Something Email:Else Message:For this Message]
Both solutions give me the same output... But in the 2nd example I don't necessarily need to know the keys of "Contact[]"
I know... I may just forget about that "Form Array" and use name="Email"
on my inputs and retrieve one by one but... I've passing through some scenarios where I use ONE form that contain more than 2 arrays of data and do different things with each one, like ORMs
Question 1: Is there a easier way to get my Form Array as an actual map in Golang like PHP does?
Question 2: Should I retrieve the data one by one (Tedious as much and I may change the Form data at some point and recompile...) or iterate the whole thing as I've done in the 2nd example.
Sorry for my bad English... Thanks in advance!
I had a similar problem, so I wrote this function
func ParseFormCollection(r *http.Request, typeName string) []map[string]string {
var result []map[string]string
r.ParseForm()
for key, values := range r.Form {
re := regexp.MustCompile(typeName + "\\[([0-9]+)\\]\\[([a-zA-Z]+)\\]")
matches := re.FindStringSubmatch(key)
if len(matches) >= 3 {
index, _ := strconv.Atoi(matches[1])
for ; index >= len(result); {
result = append(result, map[string]string{})
}
result[index][matches[2]] = values[0]
}
}
return result
}
It turns a collection of form key value pairs into a list of string maps. For example, if I have form data like this:
Contacts[0][Name] = Alice
Contacts[0][City] = Seattle
Contacts[1][Name] = Bob
Contacts[1][City] = Boston
I can call my function passing the typeName of "Contacts":
for _, contact := range ParseFormCollection(r, "Contacts") {
// ...
}
And it will return a list of two map objects, each map containing keys for "Name" and "City". In JSON notation, it would look like this:
[
{
"Name": "Alice",
"City": "Seattle"
},
{
"Name": "Bob",
"City": "Boston"
}
]
Which incidentally, is exactly how I'm posting the data up to the server in an ajax request:
$.ajax({
method: "PUT",
url: "/api/example/",
dataType: "json",
data: {
Contacts: [
{
"Name": "Alice",
"City": "Seattle"
},
{
"Name": "Bob",
"City": "Boston"
}
]
}
})
If your form data key structure doesn't quite match mine, then I you could probably adapt the Regex that I'm using to suit your needs.
I had the same question. The submission of array form params is also idiomatic in the Ruby/Rails world where I'm coming from. But, after some research, it looks like this is not really the "Go-way".
I've been using the dot prefix convention: contact.name
, contact.email
, etc.
func parseFormHandler(writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
userParams := make(map[string]string)
for key, _ := range request.Form {
if strings.HasPrefix(key, "contact.") {
userParams[string(key[8:])] = request.Form.Get(key)
}
}
fmt.Fprintf(writer, "%#v\n", userParams)
}
func main() {
server := http.Server{Addr: ":8088"}
http.HandleFunc("/", parseFormHandler)
server.ListenAndServe()
}
Running this server and then curling it:
$ curl -id "contact.name=Jeffrey%20Lebowski&[email protected]&contact.message=I%20hate%20the%20Eagles,%20man." http://localhost:8088
Results in:
HTTP/1.1 200 OK
Date: Thu, 12 May 2016 16:41:44 GMT
Content-Length: 113
Content-Type: text/plain; charset=utf-8
map[string]string{"name":"Jeffrey Lebowski", "email":"[email protected]", "message":"I hate the Eagles, man."}
You can also use the Gorilla Toolkit's Schema Package to parse the form params into a struct, like so:
type Submission struct {
Contact Contact
}
type Contact struct {
Name string
Email string
Message string
}
func parseFormHandler(writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
decoder := schema.NewDecoder()
submission := new(Submission)
err := decoder.Decode(submission, request.Form)
if err != nil {
log.Fatal(err)
}
fmt.Fprintf(writer, "%#v\n", submission)
}
Running this server and then curling it:
$ curl -id "Contact.Name=Jeffrey%20Lebowski&[email protected]&Contact.Message=I%20hate%20the%20Eagles,%20man." http://localhost:8088
Results in:
HTTP/1.1 200 OK
Date: Thu, 12 May 2016 17:03:38 GMT
Content-Length: 128
Content-Type: text/plain; charset=utf-8
&main.Submission{Contact:main.Contact{Name:"Jeffrey Lebowski", Email:"[email protected]", Message:"I hate the Eagles, man."}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With