GoでWebサーバーを構築
Goで、フレームワークを使わなくてもサーバーを構築してアプリ開発することができます。 Goの機能のみを使って、簡易的なアプリを作ってみたいと思います。
プロジェクト作成
$ mkdir list-app $ touch server.go
net/httpパッケージ
HTTPを扱うパッケージで、HTTPクライアントとHTTPサーバーを実装するために必要な機能が提供されています。HTTPサーバー用の機能を使用することで、簡単にWebサーバーを立てることができます。
・http.HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
指定したパターンとハンドラー関数をDefaultServeMux
に登録します。
・http.ListenAndServe
func ListenAndServe(addr string, handler Handler) error
TCPネットワークアドレスでリッスン(第一引数)、ハンドラは通常はnil
(第二引数)で、その場合はDefaultServeMux
が使用されます。
package main import ( "fmt" "log" "net/http" ) func helloHandler(w http.ResponseWriter, r *http.Request) { hello := []byte("Hello World!!!") _, err := w.Write(hello) if err != nil { log.Fatal(err) } } func main() { http.HandleFunc("/hello", helloHandler) fmt.Println("Server Start Up........") log.Fatal(http.ListenAndServe("localhost:8080", nil)) }
$ go run server.go Server Start Up........
Webサーバーが立ち上がったのでlocalhost:8080/hello
にアクセスします。
リストアプリの作成
好きな言語を追加していくだけの、簡単なアプリケーションを作成したいと思います。 HTMLファイルを返すことができるようにします。
$ touch view.html
view.htmlにコードを記述します。
<h1>読んだ書籍</h1> <div> <p>・スッキリわかるSQL入門</p> <P>・達人に学ぶSQL徹底指南書</P> <P>・達人に学ぶDB設計 徹底指南書</P> </div>
view.htmlをサーバーのレスポンスとして返すようにします。
viewHandlerを作成し、template.ParseFiles
で引数に渡したhtmlをパースします。
Execute
メソッドを使用。第一引数に出力先、第二引数にテンプレートに埋め込みたいデータを渡します。
今回は、渡すデータがないのでnil
とします。
package main import ( "fmt" "html/template" "log" "net/http" ) func viewHandler(w http.ResponseWriter, r *http.Request) { html, err := template.ParseFiles("view.html") if err != nil { log.Fatal(err) } if err := html.Execute(w, nil); err != nil { log.Fatal(err) } } func main() { http.HandleFunc("/view", viewHandler) fmt.Println("Server Start Up........") log.Fatal(http.ListenAndServe("localhost:8080", nil)) }
サーバーを起動します。
$ go run server.go Server Start Up........
Webサーバーが立ち上がったのでlocalhost:8080/view
にアクセスします。
データをテンプレートに埋め込む
読書した内容を記載したファイルを作成し、その中身をテンプレートに埋め込むように、コードを追加していきたいと思います。
$ touch reading.txt
ファイルの中身を読み取る
reading.txt
の中身を読み取る関数を作成し、読書内容が記載されたファイルを読み取りたいと思います。
func fileRead(fileName string) []string { var bookList []string file, err := os.Open(fileName) if os.IsNotExist(err) { return nil } defer file.Close() scaner := bufio.NewScanner(file) for scaner.Scan() { bookList = append(bookList, scaner.Text()) } return bookList } func viewHandler(w http.ResponseWriter, r *http.Request) { bookList := fileRead("reading.txt") fmt.Println(bookList) html, err := template.ParseFiles("view.html") if err != nil { log.Fatal(err) } if err := html.Execute(w, nil); err != nil { log.Fatal(err) } }
ファイルの中身を保持できるようにstructを追加します。
type BookList struct { Books []string } func New(books []string) *BookList { return &BookList{Books: books} }
viewHandlerを変更します。
func viewHandler(w http.ResponseWriter, r *http.Request) { bookList := fileRead("reading.txt") html, err := template.ParseFiles("view.html") if err != nil { log.Fatal(err) } getBooks := New(bookList) if err := html.Execute(w, getBooks); err != nil { log.Fatal(err) } }
reading.txt
を出力できるようになったので、これをhtmlで表示できるように変更します。
<h1>読んだ書籍</h1> <div> {{ range .Books }} <p>{{.}}</p> {{ end }} </div>
サーバーを起動します。
$ go run server.go Server Start Up........
Webサーバーが立ち上がったのでlocalhost:8080/view
にアクセスします。
違いが分かるよう、reading.txt
に読んだ本を追加してます。
しっかり表示できてますね。
フォームを追加
読んだ本が増えるたびに追加していきたいので、フォームを作成して、フォームからのデータをreading.txt
に書き込むように変更したい思います。
view.htmlにフォームを追加します。
<h1>読んだ書籍</h1> <form action="/view/create" method="post"> <div><input type="text" name="value"></div> <div><input type="submit" value="追加"></div> </form> <div> {{ range .Books }} <p>{{.}}</p> {{ end }} </div>
package main import ( "bufio" "fmt" "html/template" "log" "net/http" "os" ) type BookList struct { Books []string } func New(books []string) *BookList { return &BookList{Books: books} } func fileRead(fileName string) []string { var bookList []string file, err := os.Open(fileName) if os.IsNotExist(err) { return nil } defer file.Close() scaner := bufio.NewScanner(file) for scaner.Scan() { bookList = append(bookList, scaner.Text()) } return bookList } func viewHandler(w http.ResponseWriter, r *http.Request) { bookList := fileRead("reading.txt") html, err := template.ParseFiles("view.html") if err != nil { log.Fatal(err) } getBooks := New(bookList) if err := html.Execute(w, getBooks); err != nil { log.Fatal(err) } } func createHandler(w http.ResponseWriter, r *http.Request) { formValue := r.FormValue("value") file, err := os.OpenFile("reading.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0600)) defer file.Close() if err != nil { log.Fatal(err) } _, err = fmt.Fprintln(file, formValue) if err != nil { log.Fatal(err) } http.Redirect(w, r, "/view", http.StatusFound) } func main() { http.HandleFunc("/view", viewHandler) http.HandleFunc("/view/create", createHandler) fmt.Println("Server Start Up........") log.Fatal(http.ListenAndServe("localhost:8080", nil)) }
サーバーを起動します。
$ go run server.go Server Start Up........
Webサーバーが立ち上がったのでlocalhost:8080/view
にアクセスします。
フォームから読んだ本を追加してみます。
追加できてますね。 このようにして、Goではフレームワークを使わなくてアプリケーション開発行える機能がたくさん備わってて便利ですね。