108 lines
6.3 KiB
Markdown
108 lines
6.3 KiB
Markdown
# 4.1 フォームの入力を処理する
|
||
|
||
まずフォームによる送信の例を見てみましょう。以下のようなフォームの内容があるとします。login.gtplというファイルを新規作成します。(新しくディレクトリを作ってその中に入れてください)
|
||
|
||
<html>
|
||
<head>
|
||
<title></title>
|
||
</head>
|
||
<body>
|
||
<form action="/login" method="post">
|
||
ユーザ名:<input type="text" name="username">
|
||
パスワード:<input type="password" name="password">
|
||
<input type="submit" value="ログイン">
|
||
</form>
|
||
</body>
|
||
</html>
|
||
|
||
上で送信されるフォームはサーバの`/login`に渡ります。ユーザが情報を入力し、ログインをクリックした後、サーバのルーティングの`login`にリダイレクトします。まずはこの送信が何のメソッドによるものか判断する必要があります。POSTでしょうかGETでしょうか?
|
||
|
||
httpパッケージではとても簡単な方法で取得することができます。前のwebの例を基礎にloginページのformデータをどのように処理するか見てみましょう。
|
||
|
||
|
||
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")
|
||
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`を開いた時に以下のような画面が現れます。
|
||
|
||

|
||
|
||
図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になっていませんか。サーバの出力は以下のようになります:
|
||
|
||

|
||
|
||
図4.1 サーバが受け取ったデータを表示
|
||
|
||
`request.Form`はurl.Values型です。この中には`key=value`のような対応するデータが保存されています。ここではformデータに対していくつかの操作をご紹介します:
|
||
|
||
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
|
||
* [目次](<preface.md>)
|
||
* 前へ: [フォーム](<04.0.md>)
|
||
* 次へ: [フォームに入力された内容の検証](<04.2.md>)
|