Gin - Golang learning step 5-1

  • 公開日
  • カテゴリ:WebFrameworks
  • タグ:Golang,roadmap.sh,学習メモ
Gin - Golang learning step 5-1

roadmap.sh > Go > Web Frameworks > Gin の学習を進めていきます。

※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。

contents

  1. 開発環境
  2. 参考 URL
  3. Gin
  4. Gin の特徴
  5. 開発環境のセットアップ
  6. 基本的な使い方
  7. ルーティング
    1. 動的パラメータ
    2. クエリパラメータ
    3. HTTPメソッド
    4. グループルーティング
  8. ミドルウェア
    1. ミドルウェアの使い方(基本構造)
    2. ミドルウェアの適用方法
  9. エラーハンドリング
  10. テスト

開発環境

  • チップ: Apple M2 Pro
  • OS: macOS Sonoma
  • go version: go1.23.2 darwin/arm64

参考 URL

Gin

Gin は Golang(Go)で書かれた高性能な HTTP Web フレームワークです。Gin は Martini に似た API を持ち、最大 40 倍の速度を実現していると言われています。Gin を使用することで、Go で Web アプリケーションやマイクロサービスを構築することができます。

Gin の特徴

  • 高速な HTTP ルーターを採用
  • ミドルウェアのサポート
  • エラーハンドリング機能
  • JSON のバリデーションと変換
  • ルートグループ化
  • 豊富なドキュメントとコミュニティ

開発環境のセットアップ

  1. プロジェクトの作成
mkdir gin-project
cd gin-project
go mod init gin-project
  1. Ginのインストール
go get -u github.com/gin-gonic/gin

基本的な使い方

簡単な "Hello, World!" アプリを作成します。プロジェクトルートに main.go を作成し、以下を実装します。

package main

import (
  "github.com/gin-gonic/gin"
  "net/http"
)

func main() {
  // Gin のデフォルトのルーターを作成
  r := gin.Default()

  // GETエンドポイントの定義
  r.GET("/hello", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
      "message": "Hello, World!",
    })
  })

  // サーバーの起動(デフォルトは8080ポート)
  err := r.Run()
  if err != nil {
    log.Fatal(err)
  }
}

ターミナルで以下で実行し起動します。

go run main.go

ブラウザから http://localhost:8080/hello にアクセスすると、以下の JSON レスポンスが表示されます。

{
  "message": "Hello, World!"
}

ルーティング

Gin では、ルートを簡単に設定できます。また、動的パラメータやクエリパラメータを処理する方法も直感的です。

動的パラメータ

r.GET("/user/:id", func(c *gin.Context) {
  id := c.Param("id") // 動的パラメータを取得
  c.JSON(200, gin.H{
    "user_id": id,
  })
})

URL /user/123 にアクセスすると、以下の JSON が返されます:

{
  "user_id": "123"
}

クエリパラメータ

r.GET("/search", func(c *gin.Context) {
  keyword := c.Query("keyword") // クエリパラメータを取得
  c.JSON(200, gin.H{
    "search": keyword,
  })
})

URL /search?keyword=golang にアクセスすると、以下の JSON が返されます:

{
  "search": "golang"
}

HTTPメソッド

GET 以外にも POST, PUT, DELETE など一通りのメソッドも利用できます。以下は POST を受けるルーティング例です。

type User struct {
  Name string `json:"name" binding:"required"`
  Age  int  `json:"age" binding:"required"`
}

r.POST("/user", func(c *gin.Context) {
  var user User
  if err := c.ShouldBindJSON(&user); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  c.JSON(http.StatusOK, gin.H{"user": user})
})

ターミナルから curl コマンドで POST リクエストを送信します。

curl -X POST http://localhost:8080/user \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe", "age": 30}'

レスポンスは以下になります。

{"user":{"name":"John Doe","age":30}}

グループルーティング

複数の関連エンドポイントをまとめて管理することもできます。

api := r.Group("/api")
{
  api.GET("/users", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "User list"})
  })
  api.GET("/users/:id", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "User details"})
  })
}

ミドルウェア

ミドルウェアは、リクエストとレスポンスの間に挟まる「共通処理」を実行する仕組みです。

例えば、以下のような用途に使えます。

  • リクエストログの記録
  • 認証や権限チェック
  • リクエストやレスポンスのデータ加工
  • エラーハンドリング

Gin では、ミドルウェアを簡単に作成して使うことができます。

ミドルウェアの使い方(基本構造)

ミドルウェアは func(c *gin.Context) の形で定義します。

以下のように、リクエスト前後で処理を追加できます

func myMiddleware(c *gin.Context) {
  // リクエスト前の処理
  c.Set("example", "12345") // 値を保存

  c.Next() // 次のハンドラやミドルウェアを実行

  // リクエスト後の処理
  status := c.Writer.Status() // レスポンスステータスを取得
  c.Writer.Header().Set("X-Custom-Header", "Processed")
  fmt.Println("Response Status:", status)
}

ミドルウェアの適用方法

1. すべてのルートに適用

Use を使うと、すべてのルートにミドルウェアを適用できます。

package main

import (
  "github.com/gin-gonic/gin"
  "fmt"
)

func myMiddleware(c *gin.Context) {
  fmt.Println("Middleware before handler")
  c.Next() // 次の処理を呼び出す
  fmt.Println("Middleware after handler")
}

func main() {
  r := gin.Default()

  // ミドルウェアを全体に適用
  r.Use(myMiddleware)

  r.GET("/hello", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "Hello, World!"})
  })

  r.Run(":8080")
}

/hello にアクセスすると、リクエスト前後でミドルウェアのメッセージが出力されます`

Middleware before handler
Middleware after handler

2. 特定のルートに適用

ミドルウェアを特定のルートだけに適用したい場合は、ルートの後に Use を呼びます。

r.GET("/special", myMiddleware, func(c *gin.Context) {
  c.JSON(200, gin.H{"message": "This route uses middleware"})
})

3. グループに適用

ルートをグループ化し、そのグループにだけミドルウェアを適用することもできます。

api := r.Group("/api")
api.Use(myMiddleware)
{
  api.GET("/users", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "Users endpoint"})
  })
}

エラーハンドリング

エラーハンドリングもシンプルです。レスポンスにエラーステータスやメッセージを設定できます。

r.POST("/login", func(c *gin.Context) {
  var json struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
  }

  // JSON のバインディングとエラーチェック
  if err := c.ShouldBindJSON(&json); err != nil {
    c.JSON(400, gin.H{"error": err.Error()})
    return
  }

  // ログイン成功のレスポンス
  c.JSON(200, gin.H{"status": "logged in"})
})

テスト

各エンドポイントのテストも作成できます。以下は、これまで実装した各エンドポイントのテストです。

main.go でこれまで定義したルーティングを setupRouter として切り出します。

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
  "net/http"
)

type User struct {
  Name string `json:"name" binding:"required"`
  Age  int  `json:"age" binding:"required"`
}

func myMiddleware(c *gin.Context) {
  fmt.Println("Middleware before handler")
  c.Next() // 次の処理を呼び出す
  fmt.Println("Middleware after handler")
}

// ルーティング設定を関数として切り出す
func setupRouter() *gin.Engine {
  // Gin のデフォルトのルーターを作成
  r := gin.Default()

  // ミドルウェアを全体に適用
  //r.Use(myMiddleware)

  r.GET("/special", myMiddleware, func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "This route uses middleware"})
  })

  // GETエンドポイントの定義
  r.GET("/hello", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
      "message": "Hello, World!",
    })
  })

  r.GET("/user/:id", func(c *gin.Context) {
    id := c.Param("id") // 動的パラメータを取得
    c.JSON(200, gin.H{
      "user_id": id,
    })
  })

  r.GET("/search", func(c *gin.Context) {
    keyword := c.Query("keyword") // クエリパラメータを取得
    c.JSON(200, gin.H{
      "search": keyword,
    })
  })

  r.POST("/user", func(c *gin.Context) {
    var user User
    if err := c.ShouldBindJSON(&user); err != nil {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
    }
    c.JSON(http.StatusOK, gin.H{"user": user})
  })

  api := r.Group("/api")
  api.Use(myMiddleware)
  {
    api.GET("/users", func(c *gin.Context) {
      c.JSON(200, gin.H{"message": "Users endpoint"})
    })
  }

  return r
}

func main() {
  r := setupRouter()

  // サーバーの起動(デフォルトは8080ポート)
  err := r.Run()
  if err != nil {
    return
  }
}

切り出した setupRouter を使って、main_test.go にテストを実装します。

package main

import (
  "github.com/stretchr/testify/assert"
  "net/http"
  "net/http/httptest"
  "testing"
)

func TestHelloEndpoint(t *testing.T) {
  router := setupRouter()
  w := httptest.NewRecorder()
  req, _ := http.NewRequest("GET", "/hello", nil)

  router.ServeHTTP(w, req)

  assert.Equal(t, 200, w.Code)
  assert.Equal(t, "{\"message\":\"Hello, World!\"}", w.Body.String())
}

テストに必要なパッケージをインストールします。

# testifyパッケージのインストール
go get github.com/stretchr/testify

# 再度依存関係を解決
go mod tidy

テストを実行できます。

go test -v

=== RUN   TestHelloEndpoint
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET  /special          --> gin-project.setupRouter.func1 (4 handlers)
[GIN-debug] GET  /hello          --> gin-project.setupRouter.func2 (3 handlers)
[GIN-debug] GET  /user/:id         --> gin-project.setupRouter.func3 (3 handlers)
[GIN-debug] GET  /search           --> gin-project.setupRouter.func4 (3 handlers)
[GIN-debug] POST   /user           --> gin-project.setupRouter.func5 (3 handlers)
[GIN-debug] GET  /api/users        --> gin-project.setupRouter.func6 (4 handlers)
[GIN] 2024/12/02 - 22:09:46 | 200 |   230.458µs |         | GET    "/hello"
--- PASS: TestHelloEndpoint (0.00s)
PASS
ok    gin-project   0.369s

まとめ

  • Gin は Go で構築された高速かつ軽量な Web フレームワーク
  • 高速な HTTP ルーター、ミドルウェア、エラーハンドリングをサポート
  • ルートグループ化や JSON バインディング機能の提供
  • 初心者でも簡単に構築できるシンプルな API
  • テストしやすい構造の実現
  • Web アプリケーションやマイクロサービス開発に最適


[Next] Step 5-2: Echo

[Prev] Step 4-1: Gorm

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級