Ginパッケージ概要
Ginは、Go言語のための高速で軽量なWebフレームワーク。多くのウェブアプリケーションで必要とされる機能(ルーティング、ミドルウェア、リクエスト/レスポンス処理など)を簡潔に実装できるように設計されている。
基本設定・初期化
// パッケージのインポート
import "github.com/gin-gonic/gin"
// デフォルト設定でエンジンを初期化(ロガーとリカバリーミドルウェア付き)
r := gin.Default()
// ミドルウェアなしの空のエンジンを初期化
// r := gin.New()
// サーバーを起動(デフォルトは8080ポート)
r.Run()
// カスタムポートで起動
r.Run(":3000")
ルーティング
// 基本的なルート定義
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)
リクエスト処理
// ハンドラー関数の基本形
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)
}
レスポンス処理
// 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)
ミドルウェア
// グローバルミドルウェア
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()
})
エラーハンドリング
// エラーレスポンス
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())
バインディング・バリデーション
// バインディングタグがついたモデル定義
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
}
グループ化
// ルートグループの作成
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との統合
// 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))
}
よくあるユースケース
// 並行処理を安全に行う(タスクリストの例)
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",
})
})
デバッグとロギング
// デバッグモード切替
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
テスト
// ハンドラーのテスト
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"])