Go言語で作る簡単なWeb Server

以下の記事を参考に、シンプルなWeb Serverを作っていきます。


このチュートリアルでは、net/httpパッケージを使ってweb serverを作ります。

server.go

package main
import (
"fmt"
"html"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
http.HandleFunc("/h1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi")
})
log.Fatal(http.ListenAndServe(":8081", nil))
}

ListenAndServeは、指定されたアドレスとハンドラ(default値はnil)を持つHTTPサーバーを開始します。

上の2つHandleFuncは、DefaultServeMux(ListenAndServe関数の第二引数)に、ハンドラを追加します。

上のサーバーを go built server.go で立ち上げて、ブラウザーからhttp://localhost:8081/を叩くとHiの文字が現れます。

またURLのpathに文字を渡すと、その文字を表示されます。

例えば、http://localhost:8081/worldはHello Worldが見れますね。


次にページカウントを実装します。

Webサーバーは非同期である事から、競合状態によるパグを回避する必要があります。その為に、ここではミューテックスを使用して、カウンタが競合状態になることを防いでいます。Mutexについてはこちらのサイトが詳しいです。

つまりは、一つのgoroutineだけにLock()/Unlock()で囲んだ処理を、アクセスできるようにしています。

package main
import (
"fmt"
"log"
"net/http"
"strconv"
"sync"
)
var counter int
var mutex = &sync.Mutex{}
func echoString(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello")
}
func incrementCounter(w http.ResponseWriter, r *http.Request) {
mutex.Lock()
counter++
fmt.Fprintf(w, strconv.Itoa(counter))
mutex.Unlock()
}
func main() {
http.HandleFunc("/", echoString)
http.HandleFunc("/increment", incrementCounter)
http.HandleFunc("h1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi")
})
log.Fatal(http.ListenAndServe(":8081", nil))
}

因みに、 strconv.Itoa(counter) でint型からstring型へ変換させています。

http://localhost:8081/incrementを叩くと、ページを読み込む度に、counterが一つずつ加算されているのが分かります。


最後に、同じproject directory内に次のindex.htmlファイルを入れます。

htmlファイルをGo serverがレンダリングしていくかどうか、見ていきます。

<html>
<head>
<title>Hello World</title>
</head>
<body>
<h2>Hello World!</h2>
</body>
</html>

server.goファイルは次のように書き換えます。

package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
})
http.HandleFunc("/h1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi")
})
log.Fatal(http.ListenAndServe(":8081", nil))
}

http.ServeFile メソッドは、指定されたファイルまたはディレクトリーの内容をもって要求に応答します。

先に作っていおいた、index.htmlファイルを参照する

http://localhost:8081/index.htmlを打ったときは、index.htmlファイルがレンダリングされていることが分かります。

index.htmlを読み込む場合はhttp://localhost:8081でも、同じ結果になります。

また、pathがユーザーからの入力である場合は、サニタイズが必要になります。


参考リンク

  • net/http
    httpパッケージのドキュメント
  • 開いているポートを見るlinux command
    一度、同じポート番号で違うgo serverを立ち上げようとした時に、空いているportを調べる必要がありました。これは、lsof -i tcp:<port番号>で分かります。
  • go-plus
    Atom Editor用のGo lintのようなものです。詳しい設定の仕方は、こちらにあります。