Go の Gin でのリクエスト・レスポンス処理
Gin ではリクエストデータの取得とレスポンスの返却が直感的に行える。JSON、フォーム、ファイルなど様々な形式に対応している。
リクエストボディの取得(JSON)
JSON リクエストは構造体にバインドできる。
type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
r.POST("/users", func(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(201, gin.H{
"message": "User created",
"user": req,
})
})ShouldBindJSON はエラーを返すが、BindJSON はエラー時に自動で 400 レスポンスを返す。柔軟なエラーハンドリングには ShouldBindJSON を使う。
フォームデータの取得
HTML フォームからのデータも同様にバインドできる。
type LoginForm struct {
Username string `form:"username"`
Password string `form:"password"`
}
r.POST("/login", func(c *gin.Context) {
var form LoginForm
if err := c.ShouldBind(&form); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 認証処理...
})ShouldBind は Content-Type を見て自動的に適切なバインダーを選択する。
個別にパラメータを取得
構造体を使わず、個別に値を取得することもできる。
r.POST("/upload", func(c *gin.Context) {
// フォーム値
name := c.PostForm("name")
description := c.DefaultPostForm("description", "No description")
// クエリパラメータ
page := c.Query("page")
// パスパラメータ
id := c.Param("id")
// ヘッダー
token := c.GetHeader("Authorization")
})ファイルアップロード
単一ファイルのアップロードは FormFile で処理する。
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(400, gin.H{"error": "File required"})
return
}
// ファイルを保存
dst := "./uploads/" + file.Filename
if err := c.SaveUploadedFile(file, dst); err != nil {
c.JSON(500, gin.H{"error": "Failed to save file"})
return
}
c.JSON(200, gin.H{
"filename": file.Filename,
"size": file.Size,
})
})複数ファイルの場合は MultipartForm を使う。
r.POST("/uploads", func(c *gin.Context) {
form, _ := c.MultipartForm()
files := form.File["files"]
for _, file := range files {
c.SaveUploadedFile(file, "./uploads/"+file.Filename)
}
c.JSON(200, gin.H{"count": len(files)})
})JSON レスポンス
最も一般的なレスポンス形式だ。
// gin.H を使う
c.JSON(200, gin.H{
"status": "success",
"data": someData,
})
// 構造体を使う
type Response struct {
Status string `json:"status"`
Data interface{} `json:"data"`
}
c.JSON(200, Response{Status: "success", Data: someData})その他のレスポンス形式
// 文字列
c.String(200, "Hello, %s!", name)
// XML
c.XML(200, gin.H{"message": "hello"})
// YAML
c.YAML(200, gin.H{"message": "hello"})
// ファイルダウンロード
c.File("./files/report.pdf")
// ファイル添付(ダウンロードを促す)
c.FileAttachment("./files/report.pdf", "monthly-report.pdf")
// リダイレクト
c.Redirect(301, "https://example.com")レスポンスヘッダーの設定
r.GET("/api/data", func(c *gin.Context) {
c.Header("X-Custom-Header", "custom-value")
c.Header("Cache-Control", "no-cache")
c.JSON(200, data)
})ストリーミングレスポンス
大きなデータを少しずつ送信する場合は Stream を使う。
r.GET("/stream", func(c *gin.Context) {
c.Stream(func(w io.Writer) bool {
w.Write([]byte("chunk of data\n"))
time.Sleep(time.Second)
return true // true を返すと継続、false で終了
})
})Raw ボディの取得
バインドせずに生のリクエストボディを取得したい場合もある。
r.POST("/webhook", func(c *gin.Context) {
body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.JSON(400, gin.H{"error": "Failed to read body"})
return
}
// body を処理...
})Gin のリクエスト・レスポンス処理は、Go の net/http をラップして使いやすくしたものだ。必要に応じて c.Request や c.Writer から生の http.Request と http.ResponseWriter にアクセスすることもできる。