Gin - Golang learning step 5-1
- 公開日
- カテゴリ:WebFrameworks
- タグ:Golang,roadmap.sh,学習メモ
roadmap.sh > Go > Web Frameworks > Gin の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
開発環境
- チップ: 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 のバリデーションと変換
- ルートグループ化
- 豊富なドキュメントとコミュニティ
開発環境のセットアップ
- プロジェクトの作成
mkdir gin-project
cd gin-project
go mod init gin-project
- 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