Как получить данные формы (в виде массива) в Golang?

Я разработчик PHP. Но в настоящее время движется в Golang... Я пытаюсь получить данные из формы (метод 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>

в PHP я бы просто использовал это, чтобы получить данные:

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

но в go... либо я получаю один за другим, либо все, но не массив Contact[] только такой, как PHP

Я думал о 2 решения:

1) получить один за другим:

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

обратите внимание, что ключи карты все: "имя контакта."]..

2) Диапазон всей карты r.Form и "анализировать / получать" эти значения с префиксом "Контакт [" , а затем заменить "контакт ["и"] " пустой строкой поэтому я могу получить ключ массива формы только такой пример PHP

Я пошел на эту работу самостоятельно, но... ранжирование по всей форме может быть не очень хорошей идеей (?)

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

оба решения дают мне тот же результат... Но во 2-м примере мне не обязательно знать ключи "Контакт []"

Я знаю... Я могу просто забыть об этом "массиве форм" и использовать name="Email" на моих входах и получить один за другим, но... Я прошел через некоторые сценарии, где я использую одну форму, содержащую более 2 массивов данных, и делаю разные вещи с каждым из них, например ORMs

Вопрос 1: есть ли более простой способ получить мой массив формы как фактическую карту в Golang, как это делает PHP?

Вопрос 2: должен ли я получить данные один по одному (утомительно, и я могу изменить данные формы в какой-то момент и перекомпилировать...) или повторите все это, как я сделал во втором примере.

извините за мой плохой английский... Заранее спасибо!

3 ответов


есть ли более простой способ получить мой массив формы как фактическую карту в Golang, как это делает PHP?

вы можете использовать PostForm член http.Request тип. Это тип url.Values -- что на самом деле (та-да) а map[string][]string, и вы можете рассматривать это как таковое. Вам все равно нужно позвонить req.ParseForm() во-первых, хотя.

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

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

отметим, что PostForm карта списки строк. Это потому, что в теории, каждый поле может присутствовать несколько раз в теле POST. The PostFormValue() метод обрабатывает это, безоговорочно возвращая первый нескольких значений (что означает, когда ваше тело сообщения &foo=bar&foo=baz, потом req.PostFormValue("foo") всегда будет возвращать "bar").

также обратите внимание, что PostForm никогда не будет содержать вложенные структуры как вы используете PHP. Поскольку Go статически типизирован, значение формы POST будет всегда быть отображение string (имя) к []string (ключ/s).

лично я бы не использовал синтаксис скобки (contact[email]) для имен полей POST в приложениях Go; это конкретная конструкция PHP, во всяком случае, и, как вы уже заметили, Go не поддерживает ее очень хорошо.

должен ли я извлекать данные один за другим (утомительно, и я могу изменить данные формы в какой-то момент и перекомпилировать...) или повторите все это, как я сделал во 2-м образец.

вероятно, для этого нет правильного ответа. Если вы сопоставляете поля POST со структурой со статическими полями, вам придется явно сопоставить их в какой-то момент (или использовать reflect для реализации каких-то волшебных авто-карт).


у меня был тот же вопрос. Представление параметров формы массива также идиоматично в мире Ruby/Rails, откуда я прихожу. Но, после некоторых исследований, похоже, что это не совсем "путь".

я использую соглашение префикса точки: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()
}

запуск этого сервера, а затем его керлинг:

$ curl -id "contact.name=Jeffrey%20Lebowski&contact.email=thedude@example.com&contact.message=I%20hate%20the%20Eagles,%20man." http://localhost:8088

результаты:

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":"thedude@example.com", "message":"I hate the Eagles, man."}

использование инструментария гориллы

вы также можете использовать пакет схем Gorilla Toolkit для разбора парамов формы в структуру, например:

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

запуск этого сервера, а затем его керлинг:

$ curl -id "Contact.Name=Jeffrey%20Lebowski&Contact.Email=thedude@example.com&Contact.Message=I%20hate%20the%20Eagles,%20man." http://localhost:8088

результаты:

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:"thedude@example.com", Message:"I hate the Eagles, man."}}

у меня была похожая проблема, поэтому я написал эту функцию

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
}

он превращает коллекцию пар значений ключа формы в список строковых карт. Например, если у меня есть такие данные формы:

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

Я могу вызвать свою функцию, передавая имя типа "контакты":

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

и он вернет список из двух объектов карты, каждая карта содержит ключи для "имя"и " город". В нотации JSON это выглядит так:

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

что кстати, именно так я отправляю данные на сервер в запросе ajax:

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

если ваша структура ключа данных формы не совсем соответствует моей, то я вы могли бы, вероятно, адаптировать регулярное выражение, которое я использую, чтобы удовлетворить ваши потребности.