今回は、簡単なWebアプリケーションを開発していきます。
<目次>
学習記事まとめ
シンプルなWebアプリ
Go言語にデフォルトで組み込まれているnet/httpパッケージを使用するとシンプルなWebサーバーを立てることができます。
1 2 |
$ mkdir simple_webserver/ $ touch simple_webserver/server.go |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package main import ( "log" "net/http" ) func viewHandler(writer http.ResponseWriter, request *http.Request) { message := []byte("Hello World!") // Add message to the response _, err := writer.Write(message) if err != nil { log.Fatal(err) } } func main() { // register hander http.HandleFunc("/hello", viewHandler) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
実装を終えたら次のコマンドを実行してみましょう。
1 |
$ go run server.go |
続いて、「localhost:8080/hello」にアクセスします。

メッセージをbyte形式に変換し、レスポンスに乗せて呼び出し元に結果を返しています。
Go言語ではこのようなWebアプリケーションサーバーを迅速に立てることができます。
複数のエンドポイントを設定する
今回は、エンドポイントを「/hello」としましたが、複数設定することも可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package main import ( "log" "net/http" ) func write(writer http.ResponseWriter, message string) { _, err := writer.Write([]byte(message)) if err != nil { log.Fatal(err) } } func one(writer http.ResponseWriter, request *http.Request) { write(writer, "One") } func two(writer http.ResponseWriter, request *http.Request) { write(writer, "Two") } func three(writer http.ResponseWriter, request *http.Request) { write(writer, "Three") } func main() { http.HandleFunc("/one", one) http.HandleFunc("/two", two) http.HandleFunc("/three", three) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
Post App
感じたことをただ投稿するだけのアプリケーションを作成しましょう。
プロジェクト作成
最初にプロジェクトを作成します。
1 2 3 4 |
$ mkdir post_server $ touch post_server/post_server.go $ mkdir utils $ touch utils/check.go |
エラーチェックコード作成
utilsパッケージのcheck.goには、エラーチェックコードを書き込みます。
1 2 3 4 5 6 7 8 9 10 11 |
// utils/check.go package utils import "log" func Check(err error) { if err != nil { log.Fatal(err) } } |
Webサーバーコード作成
post_server.goには、Webサーバーコードを書き込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package main import ( "fmt" "log" "net/http" "github.com/hoge/utils" ) // post_server/post_server.go func postHandler(writer http.ResponseWriter, request *http.Request) { placeholder := []byte("signature list goes here") _, err := writer.Write(placeholder) utils.Check(err) } func main() { http.HandleFunc("/post", postHandler) fmt.Println("Starting Server...") log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
実行して見ましょう。
1 2 |
$ go run post_server/post_server.go Starting Server... |
「localhost:8080/post」にアクセスしてみます。

投稿リストの作成
これまでは、プレインなテキストファイルをただ返していましたが、HTMLファイルを返却できるようにしましょう。
1 2 3 4 5 6 7 8 9 10 |
mkdir views touch views/view.html tree ├── post_server │ └── post_server.go ├── utils │ └── check.go └── views └── view.html |
view.htmlに次のコードを記述しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
<h1>Post App!</h1> <div> X total Posts - <a href="/post/new">Add Your Post</a> </div> <div> <p>Posts</p> <p>go</p> <p>here</p> </div> |
このHTMLをサーバーのレスポンスを通じて表示できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package main import ( "fmt" "log" "net/http" "text/template" "github.com/hoge/utils" ) // post_server/post_server.go func postHandler(writer http.ResponseWriter, request *http.Request) { html, err := template.ParseFiles("views/view.html") utils.Check(err) err = html.Execute(writer, nil) utils.Check(err) } func main() { http.HandleFunc("/post", postHandler) fmt.Println("Starting Server...") log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
postHandlerを修正しました。template.ParseFilesでHTMLファイルをパースし、htmlパッケージのExecuteメソッドでリクエストとして返しています。
サーバを起動し、内容を確認して見ましょう。
1 2 |
$ go run post_server/post_server.go Starting Server... |
「localhost:8080/post」にアクセスしてみましょう。

テンプレートにデータを渡す
次は、先ほど作成したHTMLテンプレートにデータを渡して表示できるようにしましょう。
テンプレートにデータを表示させるためには、いくつかのルールを覚える必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package main import ( "html/template" "os" "github.com/hoge/utils" ) func main() { // 文字列を埋め込む時は、{{}}、データを参照する時は"." templateText := "Template start\nAction: {{.}}\nTemplate end\n" tmpl, err := template.New("test").Parse(templateText) utils.Check(err) err = tmpl.Execute(os.Stdout, "ABC") utils.Check(err) err = tmpl.Execute(os.Stdout, 123) utils.Check(err) err = tmpl.Execute(os.Stdout, true) utils.Check(err) } |
1 2 3 4 5 6 7 8 9 10 11 |
$ go run main.go Template start Action: ABC Template end Template start Action: 123 Template end Template start Action: true Template end |
Djangoなどのフレームワークを使って開発されてきた人には、とっつきやすいかもしれません。
テンプレートにデータを埋め込みたい時は、「{{}}」で囲います。そして、データを参照したい場合は、「.」を指定してデータを取得します。
if action
if actionで判定処理を行うこともできます。
1 |
executeTemplate("Start {{if .}}Dot is true!{{end}} finish\n", true) |
range action
range actionで、コレクションデータ(array, slice, map, channel)に対して繰り返し処理を実行できます。
1 2 3 |
templateText := "Before loop: {{.}}\n{{range .}}In loop: {{.}}\n{{end}}After loop] {{.}}\n" executeTemplate(templateText, []string{"do", "re", "mi"}) |
Structフィールド名をテンプレートに埋め込む
Structフィールドを名をテンプレートに埋め込むことができます。
1 2 3 4 5 6 7 |
type Book struct { Title string Author string } templateText := "Title: {{.Title}}\nAuthor: {{.Author}}\n" executeTemplate(templateText, Book{Name: "Harry Potter", Author: "J.K"}) |
他のファイルの情報を読み取る
getStrings関数を追加して、他のファイルの情報を読み取れるようにします。
1 2 |
make docs touch docs/signatures.txt |
1 2 3 4 5 |
# signatures.txt First signature Second signature Third signature |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// post_server/post_server.go package main import ( "bufio" "fmt" "log" "net/http" "os" "text/template" "github.com/hoge/utils" ) func getStrings(fileName string) []string { var lines []string file, err := os.Open(fileName) if os.IsNotExist(err) { return nil } utils.Check(err) defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { lines = append(lines, scanner.Text()) } utils.Check(scanner.Err()) return lines } func postHandler(writer http.ResponseWriter, request *http.Request) { signatures := getStrings("docs/signatures.txt") fmt.Printf("%#v\n", signatures) html, err := template.ParseFiles("views/view.html") utils.Check(err) err = html.Execute(writer, nil) utils.Check(err) } func main() { http.HandleFunc("/post", postHandler) _, err := os.Stdout.Write([]byte("Server Start....")) utils.Check(err) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
1 2 |
$ go run post_server/post_server.go Server Start....[]string{"First signature", "Second signature", "Third signature"} |
投稿数を保持できるように新規にStructを定義します。
1 2 |
mkdir post touch post/post.go |
1 2 3 4 5 6 7 8 9 10 11 12 |
// post/post.go package post type Post struct { PostCount int Signatures []string } func New(postCount int, signatures []string) *Post { return &Post{postCount, signatures} } |
続いて、postHandlerを以下のように書き換えます。
1 2 3 4 5 6 7 8 9 |
func postHandler(writer http.ResponseWriter, request *http.Request) { signatures := getStrings("docs/signatures.txt") fmt.Printf("%#v\n", signatures) html, err := template.ParseFiles("views/view.html") utils.Check(err) getPost := post.New(len(signatures), signatures) err = html.Execute(writer, getPost) utils.Check(err) } |
sigunatures.txtの中身をHTMLファイル上に出力できるようにしました。
次は、このファイルの中身をview.html側で表示できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// views/view.html <h1>Post App!</h1> <div> {{ .PostCount }} total posts - <a href="/post/new">Add Your Post</a> </div> <div> {{range .Signatures}} <p>{{.}}</p> {{end}} </div> |
サーバーを起動させて「localhost:8080/post」にアクセスします。
1 |
$ go run post_server/post_server.go |

HTMLフォームを追加する
次は、HTMLフォームを追加して、動的にデータを格納できるようにします。
1 |
touch views/new.html |
1 2 3 4 5 6 |
<!-- views/new.html --> <h1>Add a Post</h1> <form> <div><input type="text" name="post"></div> <div><input type="submit" /></div> </form> |
新しくハンドラーを追加し、このhtmlファイルを表示できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package main import ( "bufio" "fmt" "log" "net/http" "os" "text/template" "github.com/hoge/post" "github.com/hoge/utils" ) func getStrings(fileName string) []string { var lines []string file, err := os.Open(fileName) if os.IsNotExist(err) { return nil } utils.Check(err) defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { lines = append(lines, scanner.Text()) } utils.Check(scanner.Err()) return lines } func postHandler(writer http.ResponseWriter, request *http.Request) { signatures := getStrings("docs/signatures.txt") fmt.Printf("%#v\n", signatures) html, err := template.ParseFiles("views/view.html") utils.Check(err) getPost := post.New(len(signatures), signatures) err = html.Execute(writer, getPost) utils.Check(err) } # new func newHandler(writer http.ResponseWriter, request *http.Request) { html, err := template.ParseFiles("views/new.html") utils.Check(err) err = html.Execute(writer, nil) utils.Check(err) } func main() { http.HandleFunc("/post", postHandler) # new http.HandleFunc("/post/new", newHandler) _, err := os.Stdout.Write([]byte("Server Start....")) utils.Check(err) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
サーバーを再起動して、「localhost:8080/post/new」にアクセスします。

このフォームからデータをサーバー側に送って、signatures.txtに書き込めるようにします。
まずは、フォームの送信形式をPOSTに変更します。
1 2 3 4 5 6 |
<!-- views/new.html --> <h1>Add a Post</h1> <form action="/post/create" method="POST"> <div><input type="text" name="post"></div> <div><input type="submit" /></div> </form> |
次にpost_server.goにcreateHandlerを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func createHandler(writer http.ResponseWriter, request *http.Request) { post := request.FormValue("post") options := os.O_WRONLY | os.O_APPEND | os.O_CREATE file, err := os.OpenFile("docs/signatures.txt", options, os.FileMode(0600)) utils.Check(err) _, err = fmt.Fprintln(file, post) utils.Check(err) err = file.Close() utils.Check(err) } func main() { http.HandleFunc("/post", postHandler) http.HandleFunc("/post/new", newHandler) http.HandleFunc("/post/create", createHandler) _, err := os.Stdout.Write([]byte("Server Start....")) utils.Check(err) log.Fatal(http.ListenAndServe("localhost:8080", nil)) } |
サーバーを起動してデータを送信してみましょう。
1 |
go run post_server/post_server.go |


フォームからデータを追加してみました。signature.txtの中身をみてみるとデータが追加されていることがわかります。
1 2 3 4 5 |
First signature Second signature Third signature Harry Potter |
最後に、投稿したらトップページにリダイレクトされるように変更しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
func createHandler(writer http.ResponseWriter, request *http.Request) { post := request.FormValue("post") options := os.O_WRONLY | os.O_APPEND | os.O_CREATE file, err := os.OpenFile("docs/signatures.txt", options, os.FileMode(0600)) utils.Check(err) _, err = fmt.Fprintln(file, post) utils.Check(err) err = file.Close() utils.Check(err) // new http.Redirect(writer, request, "/post", http.StatusFound) } |
もう一度サーバーを再起動して、データを投稿してみましょう。今度は、トップページにリダイレクトされるはずです。
おわりに
15章にも及ぶGo言語の学習もこの章で最後です。
ここまで勉強したらGo言語の基礎はバッチリでしょう。
次は、より実践的なアプリケーションに挑戦してみてください。
お疲れ様でした!
コメントを残す
コメントを投稿するにはログインしてください。