LoginSignup
5
3

More than 1 year has passed since last update.

Goの標準ライブラリだけでWebサーバを作る

Posted at

はじめに

個人開発での勉強で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ファイルを作成し以下を記述します。

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を使うことで同じことは可能)

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3