🏷️

【Go】headerタグの使い方

に公開

はじめに

前回の記事では、さまざまな構造体タグについて紹介しました。
ただ、実はまだ触れていないタグが1つあります。それがheaderタグです。
今回は、このheaderタグの使い方を紹介します。

↓前回の記事はこちら↓
https://zenn.dev/tmyhrn/articles/729a5a1aee2542

この記事でわかること

  • Echoでのリクエストヘッダーの値の取得方法
  • headerタグの使い方
  • headerタグを使うときの注意点

Echoにおける一般的なリクエストヘッダーの取得

今回は example:aaaというヘッダーを付け、その値を取得してみます。
一般的な方法は以下の通りになるかと思います。

main.go
package main

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

func main() {
    e := echo.New()
    e.GET("/header", func(c echo.Context) error {
        a := c.Request().Header.Get("example")
        return c.JSON(http.StatusOK, a)
    })
    e.Logger.Fatal(e.Start(":8080"))
}
ターミナル
curl -H 'example:aaa' http://localhost:8080/header
# 出力結果: "aaa"

なお、Getの引数は大文字・小文字を区別しません。
たとえばGet("Example")でもaaaが返されます。

headerタグを使ったリクエストヘッダー取得

Echoの公式ドキュメントには、以下のような記載があります。

Parsing request data is a crucial part of a web application. In Echo this is done with a process called binding. This is done with information passed by the client in the following parts of an HTTP request:

  • URL Path parameter
  • URL Query parameter
  • Header
  • Request body

Echoでは、ヘッダーもバインディング対象です。
ただし、ヘッダーのバインディングは他と異なり、以下の注意点があります。

Note that headers is not one of the included sources with Context#Bind. The only way to bind header data is by calling BindHeaders directly.

ざっくりいうと、headerタグを構造体フィールドに付与したとしても、Bindメソッドではリクエストヘッダーをバインドすることができないということです。
それではどのように取得するのか、それは以下の通りとなります。

main.go
package main

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

type Header struct {
    Example string `header:"example"`
}

func main() {
    e := echo.New()
    e.GET("/header", func(c echo.Context) error {
        h := Header{}
        if err := (&echo.DefaultBinder{}).BindHeaders(c, &h); err != nil{
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, h.Example)
    })
    e.Logger.Fatal(e.Start(":8080"))
}
ターミナル
curl -H 'example:aaa' http://localhost:8080/header
# 出力結果: "aaa"

headerタグを使うときは、(&echo.DefaultBinder{}).BindHeaders(c, &h)を用いてバインドする必要があります。

ですので、以下はアンチパターンとなります。

main.go
package main

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

type Header struct {
    Example string `header:"example"`
}

func main() {
    e := echo.New()
    e.GET("/header", func(c echo.Context) error {
        h := Header{}
        if err := c.Bind(&h); err != nil{
            return c.JSON(http.StatusBadRequest, err)
        }
        return c.JSON(http.StatusOK, h.Example)
    })
    e.Logger.Fatal(e.Start(":8080"))
}
ターミナル
curl -H 'example:aaa' http://localhost:8080/header
# 出力結果: ""

両者の違い

Echoでリクエストヘッダーを取得する方法として2通りの方法を確認しました。
これらの違いやそれぞれの特徴は以下の通りです。

方法 概要 向いているケース
c.Request().Header.Get("key") ヘッダーを1つずつ手動で取得する 軽量に済ませたい時、1~2個程度の取得
(&echo.DefaultBinder{}).BindHeaders(c, &struct) 構造体にheaderタグを使って一括でバインドする 複数ヘッダーの管理、バリデーションとの連携、再利用性重視の設計

c.Request().Header.Get()の特徴

  • Goの標準機能を使って直接ヘッダーを取り出す
  • 書き方がシンプル
  • 小規模用途に向いている

BindHeaders()の特徴

  • 構造体を使ってヘッダーの設計を明示的に記述できる
  • 複数のヘッダーがある場合に冗長にならず、保守性が高い
  • バリデーションパッケージ(echo.Validator)と連携しやすい

たとえば AuthorizationX-Request-ID など、単発のヘッダーを取得するだけであれば Header.Get()が便利ですが、業務用APIで複数のヘッダーを扱いたい場合やバリデーションをかけたい場合は、BindHeaders()のほうが適しています。

✅まとめ

今回は、headerタグを使ったリクエストヘッダーの取得方法について見ていきました。
一般的にリクエストヘッダーはc.Request().Header.Get("key")で取得できます。
一方で、headerタグを構造体に付与してバインドすることもできますが、c.Bind()ではなく、DefaultBinder{}.BindHeadersを明示的に呼び出す必要があります。
シンプルな用途にはHeader.Get()、複数ヘッダーや再利用性が必要な場面ではBindHeaders()を使い分けましょう。

✏️参考

https://echo.labstack.com/docs/binding

Discussion