Skip to content

Goでサーバ処理を追加する#

Goでは、HTTPハンドラを使用してWebリクエストを処理します。標準ライブラリのnet/httpパッケージやGinなどのフレームワークを使うことで、さまざまなエンドポイントと処理を簡単に実装できます。

WebアプリケーションではURLのパスに紐づけて処理をすることが多いです。

URLとは、サイトを表示するときにブラウザに指定する文字列のことで、例えば https://ukiuni.com/hello のような文字列です。 {通信方式}://{サーバ名}/{パス} のような形式で出来上がっています。

このパスに、処理を紐付けることで、1つのサーバに色々な処理を依頼できるようになっています。

前提#

もし、Goプロジェクトが手元になければ、こちらの手順でGoプロジェクトを用意してください。こちらでも簡単なハンドラを作成していますが、Goの重要な処理なので、こちらで改めて説明しています。

標準パッケージを使用したハンドラとルーティングの追加#

main.goファイルを更新して、新しいハンドラ関数を追加します:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"
)

// Message はJSONレスポンス用の構造体です
type Message struct {
    Text string `json:"text"`
}

// OrderMessage は注文レスポンス用の構造体です
type OrderMessage struct {
    Message string `json:"message"`
    Time    string `json:"time"`
}

// helloHandler は/helloエンドポイントを処理します
func helloHandler(w http.ResponseWriter, r *http.Request) {
    message := Message{
        Text: "こんにちは!",
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(message)
}

// orderOudonHandler は/oudonエンドポイントを処理します
func orderOudonHandler(w http.ResponseWriter, r *http.Request) {
    // 現在時刻を文字列に変換
    currentTime := time.Now().Unix()
    timeStr := fmt.Sprintf("%d", currentTime)

    orderMessage := OrderMessage{
        Message: "おうどんください。",
        Time:    timeStr,
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(orderMessage)
}

func main() {
    // ハンドラを設定
    http.HandleFunc("/hello", helloHandler)
    http.HandleFunc("/oudon", orderOudonHandler)

    // サーバーを起動
    fmt.Println("サーバーを起動します: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

この例では:

  1. OrderMessage構造体を定義しています。これはJSON形式で応答するためのデータ構造です。
  2. /oudonパスに対応するorderOudonHandler関数を定義しています。
  3. メイン関数内でhttp.HandleFunc()を使用してURLパスとハンドラ関数を関連付けています。

注意点: - Goでは、http.HandleFunc()を使用してルートを定義します。 - レスポンスを返す際にはhttp.ResponseWriterインターフェースを使用します。 - JSONエンコードにはjson.NewEncoderを使用します。

パラメータを受け取るハンドラの追加#

URLクエリパラメータを受け取るハンドラも実装してみましょう:

// greetHandler は/greetエンドポイントを処理し、クエリパラメータを受け取ります
func greetHandler(w http.ResponseWriter, r *http.Request) {
    // URLクエリパラメータを取得
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "ゲスト"
    }

    response := struct {
        Message string `json:"message"`
    }{
        Message: fmt.Sprintf("こんにちは、%sさん!", name),
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

// main関数内に以下を追加
// http.HandleFunc("/greet", greetHandler)

この例をmain.goに追加して、main関数内のhttp.HandleFunc("/greet", greetHandler)を追加してください:

func main() {
    // ハンドラを設定
    http.HandleFunc("/hello", helloHandler)
    http.HandleFunc("/oudon", orderOudonHandler)
    http.HandleFunc("/greet", greetHandler)

    // サーバーを起動
    fmt.Println("サーバーを起動します: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

サーバーを起動すると、http://localhost:8080/greet?name=田中のようなURLでアクセスできるようになります。

Ginフレームワークを使用したバージョン#

Ginフレームワークを使うとより簡潔で読みやすいコードを書くことができます。main.goを更新してGinバージョンを実装しましょう:

package main

import (
    "fmt"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
)

// Message はJSONレスポンス用の構造体です
type Message struct {
    Text string `json:"text"`
}

// OrderMessage は注文レスポンス用の構造体です
type OrderMessage struct {
    Message string `json:"message"`
    Time    string `json:"time"`
}

func main() {
    // Ginルーターを初期化
    r := gin.Default()

    // /helloエンドポイント
    r.GET("/hello", func(c *gin.Context) {
        message := Message{
            Text: "こんにちは!",
        }
        c.JSON(http.StatusOK, message)
    })

    // /oudonエンドポイント
    r.GET("/oudon", func(c *gin.Context) {
        currentTime := time.Now().Unix()
        timeStr := fmt.Sprintf("%d", currentTime)

        orderMessage := OrderMessage{
            Message: "おうどんください。",
            Time:    timeStr,
        }
        c.JSON(http.StatusOK, orderMessage)
    })

    // /greetエンドポイント - クエリパラメータの使用
    r.GET("/greet", func(c *gin.Context) {
        name := c.DefaultQuery("name", "ゲスト")
        response := struct {
            Message string `json:"message"`
        }{
            Message: fmt.Sprintf("こんにちは、%sさん!", name),
        }
        c.JSON(http.StatusOK, response)
    })

    // POSTリクエストを処理するエンドポイント
    r.POST("/users", func(c *gin.Context) {
        var user struct {
            Username string `json:"username" binding:"required"`
            Email    string `json:"email" binding:"required"`
        }

        // リクエストボディをバインド
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        // ユーザーデータを処理...
        c.JSON(http.StatusCreated, gin.H{"message": fmt.Sprintf("ユーザー %s が作成されました", user.Username)})
    })

    // サーバーを起動
    fmt.Println("サーバーを起動します: http://localhost:8080")
    r.Run(":8080") // listen and serve on 0.0.0.0:8080
}

Ginフレームワークを使用する主な利点:

  1. より簡潔なルーティング定義(r.GET(), r.POST()など)
  2. パスパラメータとクエリパラメータの簡単な取得(c.Param(), c.Query()
  3. リクエストボディの自動バインディング(c.ShouldBindJSON()
  4. ミドルウェアの簡単な使用
  5. グループルーティングの管理がしやすい

HTTPメソッドとルーティンググループ#

Ginでは、さまざまなHTTPメソッドに対応するメソッドが用意されています:

r.GET("/resource")    // GETリクエスト
r.POST("/resource")   // POSTリクエスト
r.PUT("/resource")    // PUTリクエスト
r.DELETE("/resource") // DELETEリクエスト

また、ルーティンググループを使用してルートをまとめることができます:

// APIのバージョン1のグループ
v1 := r.Group("/api/v1")
{
    v1.GET("/users", getUsers)
    v1.GET("/users/:id", getUserByID)
    v1.POST("/users", createUser)
}

// APIのバージョン2のグループ
v2 := r.Group("/api/v2")
{
    v2.GET("/products", getProducts)
    v2.POST("/products", createProduct)
}

このようにグループ化することで、共通のパスプレフィックスを持つルートを整理できます。

サーバを起動してみる#

これで、Goでのサーバー処理の基本が完成しました。サーバーを起動してみましょう:

go run main.go

ブラウザで以下のURLにアクセスしてみてください: - http://localhost:8080/hello - http://localhost:8080/oudon - http://localhost:8080/greet?name=あなたの名前

適切なJSONレスポンスが表示されれば成功です!

次は、Goでデータベースを扱ってみましょう。