Gin フレームワークの基礎

**Gin は Go 言語で実装された軸量込みで高性能な Web フレームワークです。RESTful API サーバーや Web アプリの開発に適しています。ここでは Gin の基本的な使い方を紹介します。**

Gin の特徴

**高性能**
スレッドレスな HTTP リクエストを高速に処理します。

**シンプルな API**
ルーティングやミドルウェアが直感的で、視覚的に使いやすい。

**ミドルウェア支援**
ログ出力や認証などをカスタマイズした処理の結合が可能です。

**コミュニティサポート**
コミュニティが浸透しているため、問題が発生した時に情報を容易に採得できます。

インストール

下記コマンドを実行して Gin をインストールします。
```go
go get -u github.com/gin-gonic/gin

go.mod にバージョン情報が追加されます。

require github.com/gin-gonic/gin v1.9.0
```


**Gin の基本的な使い方**
# 1. 最小構成のサーバ

これは Gin の最小構成の Web サーバーのコードです。
```go
package main

import (
    "net/http"

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

func main() {
    // デフォルトのエンジンを作成
    router := gin.Default()

    // ルート設定
    router.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello, Gin!")
    })

    // 3000番ポートでサーバ起動
    router.Run(":3000")
}
```

コード実行後、http://localhost:3000/ にアクセスすると「Hello, Gin!」が表示されます。

# 2. ルーティング

HTTP メソッドに対応したルートを設定します。

```go
router.GET("/ping", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})
```

パスパラメータとクエリパラメータ

ルートのパラメータを簡単に取得できます。
```go
// パスパラメータ
router.GET("/user/:id", func(c *gin.Context) {
    id := c.Param("id")
    c.String(http.StatusOK, "User ID: %s", id)
})

// クエリパラメータ
router.GET("/search", func(c *gin.Context) {
    query := c.Query("q")
    c.String(http.StatusOK, "Search query: %s", query)
})
```

# 3. JSON レスポンス

JSON 形式のレスポンスを返す例です。
```go
router.GET("/json", func(c *gin.Context) {
    data := gin.H{
        "message": "hello",
        "status":  200,
    }
    c.JSON(http.StatusOK, data)
})
```
gin.H は map[string]interface{} のシンタックスシュガーです。


**ミドルウェア**

**1. 組み込みミドルウェア**

Logger: ログ出力

Recovery: パニックが発生した際のエラー対処

**2. カスタムミドルウェア**

自分で定義するミドルウェアの例です。
```go
func MyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 前処理
        c.Next()
        // 後処理
    }
}

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

    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello, Middleware!")
    })

    r.Run(":3000")
}
```
グループルーティング

グループを使用すると、ルートを結合し、共通のパスやミドルウェアを適用できます。
```go
func main() {
    r := gin.Default()

    api := r.Group("/api")
    {
        api.GET("/users", func(c *gin.Context) {
            c.String(http.StatusOK, "GET /api/users")
        })
        api.POST("/users", func(c *gin.Context) {
            c.String(http.StatusOK, "POST /api/users")
        })
    }

    admin := r.Group("/admin", AuthMiddleware())
    {
        admin.GET("/dashboard", func(c *gin.Context) {
            c.String(http.StatusOK, "Admin Dashboard")
        })
    }

    r.Run(":3000")
}
```

**テンプレートエンジン**

HTML テンプレートをレンダリングする例です。
```go
func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")

    r.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{
            "title":   "Main Page",
            "message": "Hello, Gin Template!",
        })
    })

    r.Run(":3000")
}

templates/index.html の例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{ .title }}</title>
</head>
<body>
    <p>{{ .message }}</p>
</body>
</html>
```

**CRUD 実装**

**シンプルな CRUD 操作を Gin で実装する例を紹介します。**

モデル
```go
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

var users = []User{
    {ID: 1, Name: "Alice", Email: "alice@example.com"},
    {ID: 2, Name: "Bob", Email: "bob@example.com"},
}
```

**エンドポイント**

Create (追加)
```go
router.POST("/users", func(c *gin.Context) {
    var newUser User
    if err := c.ShouldBindJSON(&newUser); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    newUser.ID = len(users) + 1
    users = append(users, newUser)
    c.JSON(http.StatusCreated, newUser)
})
```

Read (読み取り)
```go
router.GET("/users", func(c *gin.Context) {
    c.JSON(http.StatusOK, users)
})

router.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    for _, user := range users {
        if strconv.Itoa(user.ID) == id {
            c.JSON(http.StatusOK, user)
            return
        }
    }
    c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
})
```

Update (更新)
```go
router.PUT("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    var updatedUser User
    if err := c.ShouldBindJSON(&updatedUser); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    for i, user := range users {
        if strconv.Itoa(user.ID) == id {
            users[i] = updatedUser
            users[i].ID = user.ID
            c.JSON(http.StatusOK, users[i])
            return
        }
    }
    c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
})
```

Delete (削除)
```go
router.DELETE("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    for i, user := range users {
        if strconv.Itoa(user.ID) == id {
            users = append(users[:i], users[i+1:]...)
            c.JSON(http.StatusOK, gin.H{"message": "User deleted"})
            return
        }
    }
    c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
})
```