🌟

【Go】Echoによる構造体タグの使い方まとめ|query・param・form・json・xml のバインド方法

に公開

はじめに

Echoでは、主にjsonタグを使うことが多いですが、他にも複数のタグがサポートされています。
本記事では、それぞれのタグの使い方を備忘録として整理します!

📝この記事でわかること

  1. EchoのBind()と各種タグの役割
    Echoでは、queryparamformjsonxmlといったタグを構造体に付けることで、リクエストの各種データを自動的にバインドできます。

  2. タグがない場合の挙動と注意点
    タグを指定しないとバインドが失敗するケースが多く、特にスネークケースのJSONや大文字小文字を区別するXMLでは、正しくフィールドに値が入らないことがあります。

  3. リクエスト形式ごとの適切なタグの使い分け
    GETにはquery、URLパスにはparam、フォームにはform、JSON APIにはjson、XML APIにはxmlタグを使うことで、正確なデータ受け取りが可能になります。

🛠️1. queryタグ – クエリパラメータのバインド

queryタグは、GETリクエストのURLに含まれるクエリパラメータを構造体にマッピングします。

使用例

main.go
package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

type User struct {
    UserID  int    `query:"user_id"`
    Name    string `query:"name"`
    IsAdmin bool   `query:"is_admin"`
}

func main() {
    e := echo.New()
    e.GET("/user", func(c echo.Context) error {
        user := User{}
        if err := c.Bind(&user); err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, user)
    })
    e.Logger.Fatal(e.Start(":8080"))
}

実行例と出力

①すべてのクエリパラメータが渡された場合

ターミナル
curl 'http://localhost:8080/user?user_id=1&name=Taro&is_admin=true'
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":true}

②一部のクエリパラメータが省略された場合

ターミナル
curl 'http://localhost:8080/user?user_id=1&name=Taro'
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":false}

指定されなかったフィールドは、ゼロ値が設定されます。

③タグを指定しなかった場合

ターミナル
curl 'http://localhost:8080/user?user_id=1&name=Taro&is_admin=true'
# 出力結果 {"UserID":0,"Name":"","IsAdmin":false}

タグがないフィールドにはゼロ値が入ります。

🛠️2. paramタグ – パスパラメータのバインド

paramタグは、URLの一部(例: /user/:user_id)を構造体にバインドするために使用します。

使用例

main.go
package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

type User struct {
    UserID int `param:"user_id"`
}

func main() {
    e := echo.New()
    e.GET("/user/:user_id", func(c echo.Context) error {
        user := User{}
        if err := c.Bind(&user); err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, user)
    })
    e.Logger.Fatal(e.Start(":8080"))
}

実行例と出力

①タグを指定した場合

ターミナル
curl 'http://localhost:8080/user/1'
# 出力結果 {"UserID":1}

②タグを指定しなかった場合

ターミナル
curl 'http://localhost:8080/user/1'
# 出力結果 {"UserID":0}

タグがないフィールドにはゼロ値が入ります。

🛠️3. formタグ – フォーム送信データのバインド

formタグは、application/x-www-form-urlencodedmultipart/form-dataで送信されたボディデータを構造体にマッピングします。

使用例

main.go
package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

type User struct {
    UserID  int    `form:"user_id"`
    Name    string `form:"name"`
    IsAdmin bool   `form:"is_admin"`
}

func main() {
    e := echo.New()
    e.POST("", func(c echo.Context) error {
        user := User{}
        if err := c.Bind(&user); err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, user)
    })
    e.Logger.Fatal(e.Start(":8080"))
}

実行例と出力

①すべてのデータが渡された場合

ターミナル
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'user_id=1&name=Taro&is_admin=true' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":true}

②一部のデータが省略された場合

ターミナル
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'user_id=1&name=Taro' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":false}

タグがないフィールドにはゼロ値が入ります。

③タグを指定しなかった場合

ターミナル
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'user_id=1&name=Taro&is_admin=true' http://localhost:8080
# 出力結果 {"UserID":0,"Name":"","IsAdmin":false}

タグがないフィールドにはゼロ値が入ります。

🛠️4. jsonタグ – JSONリクエストボディのバインド

jsonタグは、application/jsonのリクエストボディを構造体にマッピングします。
encoding/jsonに準拠し、タグがない場合はフィールド名と完全一致しなければバインドされません。

使用例

main.go
package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

type User struct {
    UserID  int    `json:"user_id"`
    Name    string `json:"name"`
    IsAdmin bool   `json:"is_admin"`
}

func main() {
    e := echo.New()
    e.POST("", func(c echo.Context) error {
        user := User{}
        if err := c.Bind(&user); err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, user)
    })
    e.Logger.Fatal(e.Start(":8080"))
}

実行例と出力

①すべてのデータが渡された場合

ターミナル
curl -X POST -H 'Content-Type: application/json' -d '{"user_id":1, "name":"Taro", "is_admin":true}' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":true}

②一部のデータが省略された場合

ターミナル
curl -X POST -H 'Content-Type: application/json' -d '{"user_id":1, "name":"Taro"}' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":false}

タグがないフィールドにはゼロ値が入ります。

③タグを指定しなかった場合

ターミナル
curl -X POST -H 'Content-Type: application/json' -d '{"user_id":1, "name":"Taro", "is_admin":true}' http://localhost:8080
# 出力結果 {"UserID":0,"Name":"Taro","IsAdmin":false}

この場合、Nameだけがバインドされています。
タグがないと user_id → UserID のようなスネークケースは認識されません。
大文字小文字は無視されるため "Name" → Name へのマッピングは可能です。

🛠️5. xmlタグ – XMLボディのバインド

xmlタグは、application/xml形式のリクエストボディを構造体にバインドします。
encoding/xmlパッケージと連携して動作します。

使用例

main.go
package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

type User struct {
    UserID  int      `xml:"user_id"`
    Name    string   `xml:"name"`
    IsAdmin bool     `xml:"is_admin"`
}

func main() {
    e := echo.New()
    e.POST("", func(c echo.Context) error {
        user := User{}
        if err := c.Bind(&user); err != nil {
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, user)
    })
    e.Logger.Fatal(e.Start(":8080"))
}

実行例と出力

①すべてのデータが渡された場合

ターミナル
curl -X POST -H 'Content-Type: application/xml' -d '<user><user_id>1</user_id><name>Taro</name><is_admin>true</is_admin></user>' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":true}

②一部のデータが省略された場合

ターミナル
curl -X POST -H 'Content-Type: application/xml' -d '<user><user_id>1</user_id><name>Taro</name></user>' http://localhost:8080
# 出力結果 {"UserID":1,"Name":"Taro","IsAdmin":false}

タグがないフィールドにはゼロ値が入ります。

③タグを指定しなかった場合

ターミナル
curl -X POST -H 'Content-Type: application/json' -d '{"user_id":1, "name":"Taro", "is_admin":true}' http://localhost:8080
# 出力結果 {"UserID":0,"Name":"","IsAdmin":false}

XMLはJSONと異なり、大文字・小文字を区別します。
なので例えば以下のように送った場合はNameがバインドされます。

ターミナル
curl -X POST -H 'Content-Type: application/xml' -d '<user><user_id>1</user_id><Name>Taro</Name></user>' http://localhost:8080
# 出力結果{"UserID":0,"Name":"Taro","IsAdmin":false}

✅まとめ

Echoを使ったAPI開発では、構造体へのバインド処理をシンプルにするために、各リクエスト形式に応じたタグの活用が不可欠です。
タグを適切に指定することで、手動での値の抽出や型変換を減らし、保守性可読性の高いコードが実現できます。
特に以下の点を意識すると、安全かつ効率的に開発が進められます。

  • 必ずタグを指定すること(特にjson/xml)
  • 形式ごとのデータの違い(大小文字、ケース)に注意すること
  • バインドされない=ゼロ値になることを前提にした処理を設計すること

このような知識を踏まえたうえで、EchoのBind()機能を活用すれば、APIの受け口をより堅牢に構築できます。

✏️参考

https://echo.labstack.com/docs/binding
https://go.dev/wiki/Well-known-struct-tags

Discussion