web-dev-qa-db-fra.com

Comment récupérer des données de formulaire (sous forme de tableau) dans Golang?

Je suis un PHP Dev. Mais je suis actuellement en train de migrer vers Golang ... J'essaie de récupérer les données d'un formulaire (méthode Post):

<!-- 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>

Dans PHP je voudrais simplement utiliser ceci pour obtenir les données:

<?php 
   print_r($_POST["Contact"])
?>
// Output would be something like this:
Array
(
    [Name] => Something
    [Email] => Else
    [Message] => For this message
)

MAIS in go ... ou je reçois un par un ou le tout, mais pas le tableau Contact [] uniquement comme PHP

J'ai pensé à 2 solutions:

1) Obtenez un par un:

// 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]

Notez que les clés de la carte sont le tout: "Contact [Nom]" ...

2) Rangez la mappe entière r.Form et "parse | obtenez" ces valeurs avec le préfixe "Contact ["], puis remplacez "Contact [" et "]" par une chaîne vide afin que je puisse obtenir la clé de tableau de formulaire uniquement telle que PHP Exemple

Je suis allé chercher ce travail par moi-même mais ... couvrant l'ensemble du formulaire peut ne pas être une bonne idée (?)

// 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]

Les deux solutions me donnent le même résultat ... Mais dans le deuxième exemple, je n'ai pas nécessairement besoin de connaître les clés de "Contact []"

Je sais ... je peux simplement oublier cette "matrice de formulaires" et utiliser name="Email" sur mes entrées et extraire une à une mais ... je suis passé par quelques scénarios dans lesquels j'utilise UN formulaire qui contient plus de 2 matrices de données et faire des choses différentes avec chacun, comme les ORM

Question 1 : Existe-t-il un moyen plus simple d’obtenir mon Form Array sous forme de carte réelle dans Golang, comme le fait PHP?

Question 2 : Devrais-je récupérer les données une par une (fastidieux et je peux changer les données du formulaire à un moment donné et recompiler ...) ou tout réitérer comme je l'ai fait dans le 2ème exemple.

Désolé pour mon mauvais anglais ... Merci d'avance!

7
David Lavieri

Existe-t-il un moyen plus simple d’obtenir mon Form Array sous forme de carte réelle dans Golang, comme le fait PHP?

Vous pouvez utiliser le membre PostForm du type http.Request. Il est de type url.Values - qui est en réalité (ta-da) un map[string][]string et que vous pouvez traiter en tant que tel. Cependant, vous devrez toujours appeler req.ParseForm() en premier.

if err := req.ParseForm(); err != nil {
    // handle error
}

for key, values := range req.PostForm {
    // [...]
}

Notez que PostForm est une carte de listes de chaînes. En effet, en théorie, chaque champ pourrait être présent plusieurs fois dans le corps POST. La méthode PostFormValue() gère cela en renvoyant implicitement le premier de plusieurs valeurs (ce qui signifie que lorsque votre corps POST est &foo=bar&foo=baz, alors req.PostFormValue("foo") renverra toujours "bar").

Notez également que PostForm ne contiendra jamais structures imbriquéescomme vous êtes habitué de PHP. Comme Go est typé de manière statique, une valeur de formulaire POST _ sera toujours un mappage de string (nom) à []string (valeur/s).

Personnellement, je n’utiliserais pas la syntaxe entre crochets (contact[email]) pour les noms de champs POST dans les applications Go; c’est une construction spécifique à PHP, et comme vous l’avez déjà remarqué, Go ne la supporte pas très bien.

Devrais-je récupérer les données une par une (fastidieux et je peux changer les données de formulaire à un moment donné et recompiler ...) ou réitérer le tout comme je l'ai fait dans le 2ème exemple.

Il n'y a probablement pas de réponse correcte à cela. Si vous mappez vos champs POST vers une structure avec des champs statiques, vous devrez les mapper explicitement à un moment donné (ou utilisez reflect pour implémenter un mappage automatique magique).

16
helmbert

J'ai eu un problème similaire, alors j'ai écrit cette fonction

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
}

Il transforme une collection de paires clé-valeur de formulaire en une liste de mappages de chaînes. Par exemple, si j'ai des données de formulaire comme ceci:

Contacts[0][Name] = Alice
Contacts[0][City] = Seattle
Contacts[1][Name] = Bob
Contacts[1][City] = Boston

Je peux appeler ma fonction en passant le typeName de "Contacts":

for _, contact := range ParseFormCollection(r, "Contacts") {
    // ...
}

Et il retournera une liste de deux objets de carte, chaque carte contenant les clés pour "Nom" et "Ville". En notation JSON, cela ressemblerait à ceci:

[
  {
    "Name": "Alice",
    "City": "Seattle"
  },
  {
    "Name": "Bob",
    "City": "Boston"
  }
]

Ce qui incidemment, est exactement la façon dont je poste les données sur le serveur dans une requête ajax:

$.ajax({
  method: "PUT",
  url: "/api/example/",
  dataType: "json",
  data: {
    Contacts: [
      {
        "Name": "Alice",
        "City": "Seattle"
      },
      {
        "Name": "Bob",
        "City": "Boston"
      }
    ]
  }
})

Si la structure de votre clé de données de formulaire ne correspond pas tout à fait à la mienne, vous pourrez probablement adapter le regex que j'utilise à vos besoins.

2
d4nt

J'ai eu la même question. La soumission de paramètres de forme de tableau est également idiomatique dans le monde Ruby/Rails d'où je viens. Mais, après quelques recherches, il semble que ce n’est pas vraiment le "Go-way". 

J'utilise la convention de préfixe de point: 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()
}

Lancer ce serveur puis le boucler:

$ curl -id "contact.name=Jeffrey%20Lebowski&[email protected]&contact.message=I%20hate%20the%20Eagles,%20man." http://localhost:8088

Résulte en:

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."}

Utilisation de la boîte à outils Gorilla

Vous pouvez également utiliser le paquet de schéma de Gorilla Toolkit pour analyser les paramètres de formulaire dans une structure, comme suit:

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)
}

Lancer ce serveur puis le boucler:

$ curl -id "Contact.Name=Jeffrey%20Lebowski&[email protected]&Contact.Message=I%20hate%20the%20Eagles,%20man." http://localhost:8088

Résulte en:

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."}}
2
Elliot Larson