go-swaggerで自動生成したバックエンドのmiddleware設定とcontextでの値のやり取りについて
こんにちは。レスキューナウでフロント&バックエンドエンジニアをしている大橋です。
現在、私のチームではGo言語を採用し、go-swagger(v0.27.0)を用いてバックエンドの側のコードを自動生成して開発を進めています。
今回はgo-swaggerで生成されたコードに独自のmiddlewareの追加方法及び、contextを使用してmiddlewareとAPI実装部での値のやり取りについてお話したいと思います。
事前準備
go-swagger インストール
go install github.com/go-swagger/go-swagger/cmd/swagger@v0.27.0
swagger.yaml
swagger: "2.0"
info:
title: Swagger Practice
version: 1.0.0
host: localhost
schemes:
- http
paths:
/hello:
get:
operationId: hello
responses:
200:
description: OK
schema:
type: string
/goodbye:
get:
operationId: goodbye
responses:
200:
description: OK
schema:
type: string
go-swaggerでコード自動生成
swagger generate server -t src/gen -f swagger/swagger.yaml
自動生成後のディレクトリ構成
├── src
│ ├── gen // 自動生成されたコードが配置される
│ │ ├── cmd
│ │ └── restapi
│ └── handler
│ ├── get_goodbye.go // /GET /goodbye で実行されるハンドラ
| └── get_hello.go. // /GET /hello で実行されるハンドラ
└── swagger
└── swagger.yaml
middlewareの設定
src/gen/restapi配下に、configure_swagger_practice.goというファイルが生成されているのでこちらのファイルのfunc setupMiddlewares(handler http.Handler)に、自作したmiddlewareを設定するよう修正する。
リクエスト毎にcontextに{"greetingHello": "Hello"}, {"greetingGoodbye": "Goodbye"}, {"target": "World"}の3つをkey値: value値として設定しています。
ここで設定した値を後述のhandler内の処理で取得します。
func setupMiddlewares(handler http.Handler) http.Handler {
return setupContext(handler)
}
func setupContext(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Middleware start.")
// contextに値を設定
ctx := r.Context()
ctx = context.WithValue(ctx, "greetingHello", "Hello")
ctx = context.WithValue(ctx, "greetingGoodbye", "Goodbye")
ctx = context.WithValue(ctx, "target", "World")
handler.ServeHTTP(w, r.WithContext(ctx))
fmt.Println("Middleware end.")
})
}
handlerの実装
GET /hello で実行されるhandler(get_hello.go)
middlewareで設定したcontextのキー値がgreetingHelloとtargetの値を取得してレスポンスに設定しています。
package handler
import (
"fmt"
"test/src/gen/restapi/operations"
"github.com/go-openapi/runtime/middleware"
)
func GetHello(p operations.HelloParams) middleware.Responder {
// middlewareでcontextに設定した値を取得
ctx := p.HTTPRequest.Context()
g := ctx.Value("greetingHello").(string)
t := ctx.Value("target").(string)
// レスポンスにcontextの値を出力
return operations.NewHelloOK().WithPayload(fmt.Sprintf("GET /hello response. %v %v.", g, t))
}
GET /goodbye で実行されるhandler(get_goodbye.go)
middlewareで設定したcontextのキー値がgreetingGoodbyeとtargetの値を取得してレスポンスに設定しています。
package handler
import (
"fmt"
"test/src/gen/restapi/operations"
"github.com/go-openapi/runtime/middleware"
)
func GetGoodbye(p operations.GoodbyeParams) middleware.Responder {
// middlewareでcontextに設定した値を取得
ctx := p.HTTPRequest.Context()
g := ctx.Value("greetingGoodbye").(string)
t := ctx.Value("target").(string)
// レスポンスにcontextの値を出力
return operations.NewHelloOK().WithPayload(fmt.Sprintf("GET /goodbye response. %v %v.", g, t))
}
実行
# GET /hello API実行
$ curl http://localhost:8080/hello
"GET /hello response. Hello World."
# GET /hello API実行
$ curl http://localhost:8080/goodbye
"GET /goodbye response. Goodbye World."
middleware内で設定したcontextの値を、それぞれのhandlerが取得してレスポンス出力されていることが確認できます。
middlewareはリクエスト毎に起動するので、API呼び出し毎に設定したfmt.Println()の内容が標準出力されていることも確認できます。
# サーバーの標準出力
go run src/gen/cmd/swagger-practice-server/main.go --port 8080
2022/10/12 17:37:46 Serving swagger practice at http://127.0.0.1:8080
Middleware start.
Middleware end.
まとめ
いかがでしたでしょうか。
go-swaggerで自動生成したバックエンドのコードに、独自のmiddlewareの設定とmiddlewareでcontextに設定した値をhandlerで取得してAPIのレスポンスに出力してみました。
今回は基本的なことしか行いませんでしたが、middlewareもcontextもGo言語で開発する上で使う機会はあると思いますので、応用的な使い方は自分自身も勉強していきたいと思います。
最後まで読んで頂き、ありがとうございました。
最後に
現在、レスキューナウでは、災害情報の提供、災害情報を活用した安否確認サービスなどのWebサービスの開発エンジニアを募集しています!
社員・フリーランスに関わらず、参画後に安心してご活躍できることを目指し、応募された方の特性・ご希望にマッチしたチームをご紹介します。
ちょっと話を聞いてみたい、ぜひ応募したい、など、当社にご興味を持っていただけましたら、お気軽にエントリーください!!