# 4.1 處理表單的輸入 先來看一個表單遞交的例子,我們有如下的表單內容,命名成檔案login.gtpl(放入當前新建專案的目錄裡面) ```html
使用者名稱: 密碼:
``` 上面遞交表單到伺服器的`/login`,當用戶輸入資訊點選登入之後,會跳轉到伺服器的路由`login`裡面,我們首先要判斷這個是什麼方式傳遞過來,POST還是GET呢? http套件裡面有一個很簡單的方式就可以取得,我們在前面web的例子的基礎上來看看怎麼處理login頁面的form資料 ```Go package main import ( "fmt" "html/template" "log" "net/http" "strings" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析url傳遞的引數,對於POST則解析響應套件的主體(request body) //注意:如果沒有呼叫ParseForm方法,下面無法取得表單的資料 fmt.Println(r.Form) //這些資訊是輸出到伺服器端的列印資訊 fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) fmt.Println(r.Form["url_long"]) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, "")) } fmt.Fprintf(w, "Hello astaxie!") //這個寫入到w的是輸出到客戶端的 } func login(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) //取得請求的方法 if r.Method == "GET" { t, _ := template.ParseFiles("login.gtpl") log.Println(t.Execute(w, nil)) } else { //請求的是登入資料,那麼執行登入的邏輯判斷 fmt.Println("username:", r.Form["username"]) fmt.Println("password:", r.Form["password"]) } } func main() { http.HandleFunc("/", sayhelloName) //設定訪問的路由 http.HandleFunc("/login", login) //設定訪問的路由 err := http.ListenAndServe(":9090", nil) //設定監聽的埠 if err != nil { log.Fatal("ListenAndServe: ", err) } } ``` 透過上面的程式碼我們可以看出取得請求方法是透過`r.Method`來完成的,這是個字串型別的變數,返回GET, POST, PUT等method資訊。 login函式中我們根據`r.Method`來判斷是顯示登入介面還是處理登入邏輯。當GET方式請求時顯示登入介面,其他方式請求時則處理登入邏輯,如查詢資料庫、驗證登入資訊等。 當我們在瀏覽器裡面開啟`http://127.0.0.1:9090/login`的時候,出現如下介面 ![](images/4.1.login.png?raw=true) 如果你看到一個空頁面,可能是你寫的 login.gtpl 檔案中有錯誤,請根據控制檯中的日誌進行修復。 圖4.1 使用者登入介面 我們輸入使用者名稱和密碼之後發現在伺服器端是不會打印出來任何輸出的,為什麼呢?預設情況下,Handler裡面是不會自動解析form的,必須顯式的呼叫`r.ParseForm()`後,你才能對這個表單資料進行操作。我們修改一下程式碼,在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新編譯,再次測試輸入遞交,現在是不是在伺服器端有輸出你的輸入的使用者名稱和密碼了。 `r.Form`裡面包含了所有請求的引數,比如URL中query-string、POST的資料、PUT的資料,所以當你在URL中的query-string欄位和POST衝突時,會儲存成一個slice,裡面儲存了多個值,Go官方文件中說在接下來的版本里面將會把POST、GET這些資料分離開來。 現在我們修改一下login.gtpl裡面form的action值`http://127.0.0.1:9090/login`修改為`http://127.0.0.1:9090/login?username=astaxie`,再次測試,伺服器的輸出username是不是一個slice。伺服器端的輸出如下: ![](images/4.1.slice.png?raw=true) 圖4.2 伺服器端列印接收到的資訊 `request.Form`是一個url.Values型別,裡面儲存的是對應的類似`key=value`的資訊,下面展示了可以對form資料進行的一些操作: ```Go v := url.Values{} v.Set("name", "Ava") v.Add("friend", "Jess") v.Add("friend", "Sarah") v.Add("friend", "Zoe") // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe" fmt.Println(v.Get("name")) fmt.Println(v.Get("friend")) fmt.Println(v["friend"]) ``` >**Tips**: >Request本身也提供了FormValue()函式來取得使用者提交的引數。如r.Form["username"]也可寫成r.FormValue("username")。呼叫r.FormValue時會自動呼叫r.ParseForm,所以不必提前呼叫。r.FormValue只會返回同名引數中的第一個,若引數不存在則返回空字串。 ## links * [目錄]() * 上一節: [表單](<04.0.md>) * 下一節: [驗證表單的輸入](<04.2.md>)