はじめに
個人開発での勉強でGoを触ってみたので備忘録も兼ねて残すことにしました。
Goでは標準ライブラリの"net/http"パッケージを使用することで非常に少ないコードでWebサーバを作成することができます。
サンプルとしてHello Worldのような初歩的なものからJSONを返すAPIチックなものまで簡単なアプリケーションを作成します。
環境設定
PC
- MacBook Air M1, 2020
- macOS Ventura 13.0.1
Goのインストール
まずはGolangのバージョン管理ツールである「goenv」をインストールし、その後goenv経由でGoをインストールします。
goenvを使わずに公式サイト( https://go.dev/dl/ )からGoのパッケージをDL&インストールすることも可能です。
ちなみにHomebrew経由でgoenvをインストールするとなぜかインストール可能なGoのバージョンが古かったので諦めました。
goenvのインストール
gitから最新版を取得します。
git clone https://github.com/syndbg/goenv.git ~/.goenv
zshrcやbashrcに以下を追加した後、sourceコマンドを使い設定を適用します。
export GOENV_ROOT="$HOME/.goenv"
export PATH="$GOENV_ROOT/bin:$PATH"
eval "$(goenv init -)"
以下のコマンドでgoenvのバージョンが確認できればOKです。
goenv --version
Goのインストール
まずはインストール可能なGoのバージョン一覧を表示します。
goenv install --list
バージョンを指定してGoをインストールします。
goenv install 1.20.1
global設定を行います。
goenv global 1.20.1
以下のコマンドでGoのバージョンが確認できればOKです。
go version
Webサーバを作成する
Hello World表示
まずはHello Worldという文字を返すだけのWebサーバを作成してみます。
main.goファイルを作成し以下を記述します。
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
以下のコマンドでプログラムを実行してサーバーを起動します。
go run main.go
ブラウザでhttp://localhost:8080
にアクセスすると「Hello World」と表示されます。
パラメータを受け取る
次にパラメータを受け取れるようにします。
FormValue
を使いキーを指定することでパラメータの値を取得することができます。
以下の例ではnameが指定されているかどうかで出力するメッセージを変えています。
package main
import (
"fmt"
"net/http"
)
const baseMessage = "Hello World"
func handler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
if name == "" {
fmt.Fprint(w, baseMessage)
return
}
fmt.Fprintf(w, "%s, %s", baseMessage, name)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
ブラウザでhttp://localhost:8080?name=Taro
にアクセスすると「Hello World, Taro」と表示されることが確認できます。
nameを指定しない時はこれまで通り「Hello World」を返すようになっています。
POSTのみ許可
POSTのみ許可したい場合は次のようにメソッドの判定を追加します。
// ... 略
func handler(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return
+ }
// ... 略
http.Error
を用いることで、エラーメッセージとHTTPステータスコードを同時に設定することができます。
試しにブラウザでアクセスするとGETメソッドでのアクセスとなるので、「Method Not Allowed」というレスポンスと共に405エラーが返ってきます。
ターミナルからcurlを使いPOSTメソッドでアクセスするとエラーにはならず「Hello World, Taro」が返ってきます。これでHTTPリクエストメソッドに応じた制御が効いていることがわかりました。
$ curl -X POST "http://localhost:8080" -d "name=Taro"
Hello World, Taro
レスポンスをJSONで返す
次にAPIを作ることを想定してレスポンスをJSONで返すようにしてみます。
JSONを扱うためには「encoding/json」というパッケージを使用します。
また、Userという構造体を作り名前を保持できるようにしました。
(リクエストパラメータで指定されたnameをUserにセットしてJSONのレスポンスとして返す構成)
また、nameが指定されていない場合は400エラーになるようにエラー処理を追加しています。
package main
import (
"net/http"
"encoding/json"
)
type User struct {
Name string `json:"name"`
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
name := r.FormValue("name")
if name == "" {
http.Error(w, "name is required", http.StatusBadRequest)
return
} else {
user := User{
Name: name,
}
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(w).Encode(user)
}
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
curlでアクセスした結果はこちら。-i
でレスポンスのヘッダーも出力しています。
$ curl -X POST "http://localhost:8080" -d "name=Taro" -i
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Tue, 28 Feb 2023 14:23:10 GMT
Content-Length: 16
{"name":"Taro"}
ちゃんとJSONで返ってきましたね!
Content-Typeも指定した値になっていることが確認できます。
おわりに
Go言語を使うことで標準ライブラリだけでも非常に簡単にWebサーバを構築できることがわかりました。
また、今回は紹介しませんでしたがGinやEchoなど様々なMVCフレームワークも提供されているので、より効率的にWebアプリケーションの開発を行いたい場合はこれらのフレームワークを導入してみると良さそうです。
Goを触ったのは今回が初めてですが、他の言語と比べてエラーハンドリングの方法が全く異なる思想で面白いと感じました。(try〜catchのような例外機構ではなく、メソッドの戻り値としてエラーを返す。ただし、defer、panic、recoverを使うことで同じことは可能)