AWS API Gateway がリクエストヘッダーを小文字にしてしまう時の解決策 (golang)


ここ最近業務で API Gateway + Lambda を使った開発を行っており、API Gateway の振る舞いで注意しなければならない点を見つけたので、解決方法も添えて記事にしたいと思います。開発言語は Go言語です

AWS API Gateway がリクエストヘッダーを小文字にしてしまう

  • 今回使用するサンプルプログラムです
package main

import (
    "errors"
    "fmt"
    "log"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-lambda-go/lambdacontext"
)

type (
    request  = events.APIGatewayProxyRequest
    response = events.APIGatewayProxyResponse
)

// handler handles AWS Lambda execution.
func handler(ctx context.Context, r request) (response, error) {
    if _, ok := lambdacontext.FromContext(ctx); !ok {
        return response{StatusCode: http.StatusBadRequest}, errors.New("not invoked from aws lambda")
    }
    log.Print(r.Headers)
    accept, _ := r.Headers["Accept"] // 実際は accept としないと取得できない
    if == strings.Contains(accept, "application/json") {
        return response{StatusCode: http.StatusOK, Body:"{}"}, nil
    }
    return response{StatusCode: http.StatusOK, Body: fmt.Sprintf("It Works! path=%s", r.Path)}, nil
}

func main() {
    lambda.Start(handler)
}

サンプルプログラムでは Accept ヘッダーが application/json だった場合に空の json を返します。

今回の一例ですが、下記のようなリクエストを送信したとします。

curl -X GET -H "Accept:application/json" https://hogehoge

これが API Gateway を通って Lambda 側にきた時には Accept → accept になっていて、r.Headers["Accept"]では取得できません。
そもそもRFCの定義では、リクエストヘッダーは小文字大文字を区別しないとなっています。
「とりあえず全部小文字に変換しておくから、アプリケーション側で取得する時は小文字で取得してね」という事なのでしょうかね?

大文字、小文字の区別なく取得できるようにするには

全て小文字で書かなければならないのはとてもモヤモヤします。この問題に対して有効な解決策が、Lambda の SDK 開発の PR の議論のなかにありました。
vents.APIGatewayProxyRequest の header を net/http の header に置き換えてしまうというやり方です。
net/http の Header は CanonicalHeaderKey() を用いて正規化されるので、AcceptでもacceptでもACCEPTでも問題ありません。


headers := http.Header{}
for header, values := range r.MultiValueHeaders {
    for _, value := range values {
        headers.Add(header, value)
    }
}

headers.Get("Accept")

golang で Lambda 開発を行う方は是非参考にしてください。