見出し画像

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のキー値がgreetingHellotargetの値を取得してレスポンスに設定しています。

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のキー値がgreetingGoodbyetargetの値を取得してレスポンスに設定しています。

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サービスの開発エンジニアを募集しています!
社員・フリーランスに関わらず、参画後に安心してご活躍できることを目指し、応募された方の特性・ご希望にマッチしたチームをご紹介します。
ちょっと話を聞いてみたい、ぜひ応募したい、など、当社にご興味を持っていただけましたら、お気軽にエントリーください!!