Gin フレームワーク チートシート

## Ginパッケージ概要
Ginは、Go言語のための高速で軽量なWebフレームワーク。多くのウェブアプリケーションで必要とされる機能（ルーティング、ミドルウェア、リクエスト/レスポンス処理など）を簡潔に実装できるように設計されている。

## 基本設定・初期化

```go
// パッケージのインポート
import "github.com/gin-gonic/gin"

// デフォルト設定でエンジンを初期化（ロガーとリカバリーミドルウェア付き）
r := gin.Default()

// ミドルウェアなしの空のエンジンを初期化
// r := gin.New()

// サーバーを起動（デフォルトは8080ポート）
r.Run()

// カスタムポートで起動
r.Run(":3000")

```

## ルーティング

```go
// 基本的なルート定義
r.GET("/path", handlerFunc)
r.POST("/path", handlerFunc)
r.PUT("/path", handlerFunc)
r.DELETE("/path", handlerFunc)
r.PATCH("/path", handlerFunc)
r.HEAD("/path", handlerFunc)
r.OPTIONS("/path", handlerFunc)

// 複数のハンドラーをチェーン
r.GET("/path", middleware1, middleware2, handlerFunc)

// 動的パラメータを持つルート
r.GET("/users/:id", getUserHandler)
r.GET("/users/:id/posts/:post_id", getPostHandler)

// ワイルドカードルート
r.GET("/assets/*filepath", serveAssets)

```

## リクエスト処理

```go
// ハンドラー関数の基本形
func handlerFunc(c *gin.Context) {
    // 処理を実装
}

// URLパラメータの取得
func getUserHandler(c *gin.Context) {
    id := c.Param("id") // /users/:id からidを取得
}

// クエリパラメータの取得
func listHandler(c *gin.Context) {
    limit := c.DefaultQuery("limit", "10") // ?limit=x または デフォルト値
    page := c.Query("page") // ?page=x または 空文字列
}

// フォームデータの取得
func formHandler(c *gin.Context) {
    name := c.PostForm("name")
    email := c.DefaultPostForm("email", "no-email")
}

// リクエストボディをバインド（JSON）
func createHandler(c *gin.Context) {
    var json struct {
        Name string `json:"name" binding:"required"`
        Age  int    `json:"age"`
    }

    if err := c.ShouldBindJSON(&json); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
}

// マルチパートフォームのファイルアップロード
func uploadHandler(c *gin.Context) {
    file, _ := c.FormFile("file")
    c.SaveUploadedFile(file, dst)
}

```

## レスポンス処理

```go
// JSONレスポンス
c.JSON(200, gin.H{
    "message": "success",
    "data": item,
})

// 構造体を直接JSONとして返す
c.JSON(200, user)

// HTML返答
c.HTML(200, "template.html", gin.H{
    "title": "Hello",
})

// リダイレクト
c.Redirect(302, "/new-url")

// プレーンテキスト
c.String(200, "Hello %s", name)

// ステータスコードのみ
c.Status(204) // No Content

// ファイル送信
c.File("path/to/file")

// ストリーム送信
c.DataFromReader(200, contentLength, contentType, reader, extraHeaders)

```

## ミドルウェア

```go
// グローバルミドルウェア
r.Use(gin.Logger())
r.Use(gin.Recovery())

// カスタムミドルウェア
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 認証処理
        if !authenticated {
            c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
            return
        }
        // 次のハンドラーへ進む
        c.Next()
    }
}

// ミドルウェアの適用
r.Use(AuthMiddleware())

// 特定のルートにのみミドルウェアを適用
r.GET("/admin", AuthMiddleware(), adminHandler)

// ミドルウェア内でリクエスト処理をスキップ
c.Abort()

// CORS設定ミドルウェア
r.Use(func(c *gin.Context) {
    c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
    c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
    c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

    if c.Request.Method == "OPTIONS" {
        c.AbortWithStatus(204)
        return
    }

    c.Next()
})

```

## エラーハンドリング

```go
// エラーレスポンス
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
c.JSON(http.StatusNotFound, gin.H{"error": "Resource not found"})
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})

// エラーチェックパターン
if err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
}

// パニックからの回復（gin.Defaultには含まれる）
r.Use(gin.Recovery())

```

## バインディング・バリデーション

```go
// バインディングタグがついたモデル定義
type User struct {
    Name     string `json:"name" binding:"required"`
    Age      int    `json:"age" binding:"gte=0,lte=130"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"password" binding:"required,min=8"`
}

// 様々なバインディング方法
c.ShouldBindJSON(&obj)        // JSON
c.ShouldBindXML(&obj)         // XML
c.ShouldBindQuery(&obj)       // クエリパラメータ
c.ShouldBindYAML(&obj)        // YAML
c.ShouldBindUri(&obj)         // URIパラメータ
c.ShouldBindHeader(&obj)      // ヘッダー
c.ShouldBindWith(&obj, binding.Form) // カスタムバインダー

// バインディングエラーのハンドリング
if err := c.ShouldBindJSON(&user); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
}

```

## グループ化

```go
// ルートグループの作成
v1 := r.Group("/api/v1")
{
    v1.GET("/users", listUsers)
    v1.POST("/users", createUser)

    // ネストしたグループ
    users := v1.Group("/users")
    {
        users.GET("/:id", getUser)
        users.PUT("/:id", updateUser)
        users.DELETE("/:id", deleteUser)
    }
}

// グループにミドルウェアを適用
authorized := r.Group("/admin", AuthMiddleware())
{
    authorized.GET("/dashboard", dashboardHandler)
}

```

## Swaggerとの統合

```go
// swagコマンドのインストール
// go install github.com/swaggo/swag/cmd/swag@latest

// main.goにSwagger注釈
// @title Task API
// @version 1.0
// @description A simple task management API
// @host localhost:8080
// @BasePath /api/v1

// ハンドラー関数にSwagger注釈
// @Summary      タスク一覧の取得
// @Description  すべてのタスクのリストを返します
// @Tags         tasks
// @Accept       json
// @Produce      json
// @Success      200  {array}   models.Task
// @Router       /tasks [get]

// Swaggerハンドラーのセットアップ
import (
    swaggerFiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    _ "yourapp/docs" // 自動生成されたドキュメント
)

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

    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}

```

## よくあるユースケース

```go
// 並行処理を安全に行う（タスクリストの例）
var (
    tasks = make(map[string]models.Task)
    mutex = &sync.Mutex{} // 並行アクセスからデータを保護
)

func GetTasks(c *gin.Context) {
    mutex.Lock()
    defer mutex.Unlock()

    // tasksマップへの安全なアクセス
}

// タイムアウト設定
srv := &http.Server{
    Addr:         ":8080",
    Handler:      r,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
}
srv.ListenAndServe()

// 静的ファイルの提供
r.Static("/assets", "./public/assets")
r.StaticFile("/favicon.ico", "./resources/favicon.ico")

// テンプレートのロード
r.LoadHTMLGlob("templates/*")
r.GET("/", func(c *gin.Context) {
    c.HTML(http.StatusOK, "index.html", gin.H{
        "title": "Main website",
    })
})

```

## デバッグとロギング

```go
// デバッグモード切替
gin.SetMode(gin.DebugMode)   // デフォルト
gin.SetMode(gin.ReleaseMode) // 本番環境用
gin.SetMode(gin.TestMode)    // テスト用

// カスタムロガー設定
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
    return fmt.Sprintf("[%s] \"%s %s %s %d %s \"%s\" %s\"\n",
        param.TimeStamp.Format(time.RFC1123),
        param.Method,
        param.Path,
        param.Request.Proto,
        param.StatusCode,
        param.Latency,
        param.Request.UserAgent(),
        param.ErrorMessage,
    )
}))

// リクエスト情報をログに出力
c.Request.URL.Path   // リクエストパス
c.Request.Method     // HTTPメソッド
c.ClientIP()         // クライアントIP

```

## テスト

```go
// ハンドラーのテスト
func TestPingRoute(t *testing.T) {
    router := setupRouter()

    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/ping", nil)
    router.ServeHTTP(w, req)

    assert.Equal(t, 200, w.Code)
    assert.Equal(t, "pong", w.Body.String())
}

// JSONレスポンスのテスト
var json map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &json)
assert.Nil(t, err)
assert.Equal(t, "success", json["status"])

```