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ではフレームワークを使わなくてアプリケーション開発行える機能がたくさん備わってて便利ですね。
Discussion