form
form is a directive executor who decodes/encodes a field from/to HTTP form data, i.e. http.Request.Form
.
Signature
Name: "form"
Args: KEY1 [,KEY2 [,KEY3, ...]]
Decoding
form will examine values of the keys one by one (KEY1 -> KEY2 -> ...) from the form data, the first non-empty value will be used to set the corresponding field of the input struct.
In most cases, the form
directive just works like query
. Because it also visits data from the URL querystring.
But if the form data sent by the client was of application/x-www-form-urlencoded
content type, form
will visit the form data first,
and then visit the URL querystring.
For more details, please refer to GH-5 add a query directive to pull values from querystring params #6 and the following quotes.
From the Go documentation:
Form contains the parsed form data, including both the URL field's query parameters and the PATCH, POST, or PUT form data. This field is only available after ParseForm is called.
And for http.Request.ParseForm
, it is:
ParseForm
populatesr.Form
andr.PostForm
. For all requests,ParseForm
parses the raw query from the URL and updatesr.Form
. For POST, PUT, and PATCH requests, it also reads the request body, parses it as a form and puts the results into bothr.PostForm
andr.Form
. Request body parameters take precedence over URL query string values inr.Form
.
Usage
type Profile struct {
Role string `in:"form=role"`
Hireable bool `in:"form=hireable"`
}
Request (Mixed, Body + URL query) | Profile |
---|---|
|
|
|
|
|
|
|
|
Runable Example
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"github.com/ggicci/httpin"
"github.com/go-chi/chi/v5"
)
type UpdateAccountInput struct {
AccessToken string `in:"form=access_token"`
Bio string `in:"form=bio"`
}
func UpdateAccount(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*UpdateAccountInput)
fmt.Printf("input: %#v\n", input)
}
func main() {
router := chi.NewRouter()
router.With(
httpin.NewInput(UpdateAccountInput{}),
).Patch("/me", UpdateAccount)
// NOTE: form directive also visits querystring
r, _ := http.NewRequest("PATCH", "/me?access_token=rainbow", nil)
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
form := make(url.Values)
form.Add("bio", "ありがどう")
r.Body = io.NopCloser(strings.NewReader(form.Encode()))
rw := httptest.NewRecorder()
router.ServeHTTP(rw, r)
}
Output:
input: &main.UpdateAccountInput{AccessToken:"rainbow", Bio:"ありがどう"}
Encoding
form will encode the field value to the form data with the key specified in the directive argument, the first key will be used, i.e. KEY1
, results in http.Request.Form[KEY1] = VALUE
.
Runable Example
package main
import (
"fmt"
"net/http/httputil"
"github.com/ggicci/httpin"
)
type Questionare struct {
Name string `in:"form=name;nonzero"`
Gender int `in:"form=gender"`
Position string `in:"form=position"`
Height float32 `in:"form=height"`
Weight float32 `in:"form=weight"`
}
func main() {
payload := &Questionare{
Name: "Ggicci",
Gender: 0,
Position: "Stand up",
Height: 182.3,
Weight: 73.5,
}
r, _ := httpin.NewRequest("POST", "/forms/weight-and-height", payload)
data, _ := httputil.DumpRequest(r, true)
fmt.Printf("%s\n", data)
}
Output:
POST /forms/weight-and-height HTTP/1.1
Content-Type: application/x-www-form-urlencoded
gender=0&height=182.3&name=Ggicci&position=Stand+up&weight=73.5