goa v3をそろそろ検証してみる
はじめに
今回は以下について
- とりあえず触ってみた & 感想
- Middlewareについて調査
- Panic時の挙動とPanicをMiddlewareでハンドリングする
とりあえずGetting Startedを参考に触ってみた
https://goa.design/learn/getting-started/ 見ながらやっていく。
スッと動いた。良き!!
[calcapi] 16:16:15 HTTP "Add" mounted on GET /add/{a}/{b} [calcapi] 16:16:15 HTTP "./gen/http/openapi.json" mounted on GET /openapi.json [calcapi] 16:16:15 serving gRPC method calc.Calc/Add [calcapi] 16:16:15 HTTP server listening on "localhost:8000" [calcapi] 16:16:15 gRPC server listening on "localhost:8080" [calcapi] 16:16:17 id=sGdiU14f req=GET /add/1/2 from=127.0.0.1
generateされたclientもあるけど、あえてcurlで叩いてみる。
curl -i -X GET http://localhost:8000/add/1/2 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 20 Oct 2019 07:20:22 GMT Content-Length: 2 3
よさそう
v1 -> v3の移行手順はこれらしい
https://goa.design/learn/upgrading/
designは基本的には、これにしたがって移行していけばよさそう。
また今度実際に移行したら色々書く予定
midlewareについて
gRPCに対応したことで、プロトコル関係なく実行するmiddlewareとプロトコル単位で設定できるmiddlewareの2種類になったようす。
Endpoint Middleware
Useを使えば今まで通りすべてのpathに対してのmiddlewareになるみたい。
calcEndpoints.Add
に対してだけ設定するみたいなやりかたもあるみたい。
calc/main.go.diff
// Wrap the services in endpoints that can be invoked from other services // potentially running in different processes. var ( calcEndpoints *calc.Endpoints ) { calcEndpoints = calc.NewEndpoints(calcSvc) // Apply ErrorLogger to all endpoints. calcEndpoints.Use(middleware.ErrorLogger(logger, "calc")) // Or apply ErrorLogger to specific endpoint. //calcEndpoints.Add = middleware.ErrorLogger(logger, "add")(calcEndpoints.Add) }
Transport MIddleware
HTTPだけgRPCだけ実行みたいなのがやりたいときに使うぽい。
calc/middleware/auth.go
package middleware import ( "context" "net/http" "strings" ) // WithAuthToken is a HTTP server middleware that reads the value of the // Authorization header and if present writes it in the request context. func WithAuthToken() func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { // A HTTP handler is a function. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { req := r // Grab Authorization header and initialize request context with it. if bearerToken := r.Header.Get("Authorization"); bearerToken != "" { ctx := context.WithValue(r.Context(), AuthTokenKey, strings.Replace(bearerToken, "Bearer ", "", 1)) req = r.WithContext(ctx) } // Call initial handler. h.ServeHTTP(w, req) }) } }
goa.Context
じゃなくて普通の context.Context
がくるのでそこからGetすればOK
calc.go
s.logger.Print("Token = ", ctx.Value(middleware.AuthTokenKey))
PanicHandler
httpパッケージのserver.goのserveメソッド側のrecoverでhandlingされる。
2019/10/20 21:16:16 http: panic serving 127.0.0.1:57525: test goroutine 21 [running]: net/http.(*conn).serve.func1(0xc00017e280) /usr/local/opt/go/libexec/src/net/http/server.go:1767 +0x139 panic(0x1507000, 0x168e4c0) /usr/local/opt/go/libexec/src/runtime/panic.go:679 +0x1b2 calc.(*calcsrvc).Add(0xc0000b0550, 0x16a6860, 0xc0001d43f0, 0xc0001d2130, 0xc0001ec101, 0xc0001d2130, 0xc0000eb760) /Users/kyokomi/workspace/ghq/github.com/kyokomi-sandbox/sandbox/golang/goav3/calc/calc.go:29 +0x11c
これはこれでとりあえず助かるんですが、responseも以下みたいな感じになってしまい...
共通の500エラーとか返したいなぁと...
$ curl -i -X GET -H "Authorization: Bearer hogehogehogehoge" http://localhost:8000/add/1/2 curl: (52) Empty reply from server
v1のようにEndpoint Middlewareでrecoverを入れてみる
注意点としては、recoverしたときにreturnのerrorをちゃんとdefer内で差し替えないと、正常処理に入ってしまいそっちで別のpanic等が起きてしまうことがあります(encodeとかdecodeのあたりで)
calc/middleware/panic_handler.go
package middleware import ( "context" "fmt" "log" goa "goa.design/goa/v3/pkg" ) // PanicRecover panic recover middleware func PanicRecover(l *log.Logger) func(goa.Endpoint) goa.Endpoint { return func(e goa.Endpoint) goa.Endpoint { return func(ctx context.Context, req interface{}) (response interface{}, err error) { defer func() { if exception := recover(); exception != nil { panicErr, ok := exception.(error) if !ok { panicErr = fmt.Errorf("recover error %v", exception) } l.Printf("[PANIC] %v", panicErr) err = panicErr } }() response, err = e(ctx, req) return } } }
適当にhandlerでpanicさせてときのログ
[calcapi] 21:19:33 calc.add [calcapi] 21:19:33 Token = hogehogehogehoge [calcapi] 21:19:33 [PANIC] recover error test [calcapi] 21:19:33 [ERROR] calc: recover error test [calcapi] 21:19:33 id=IGTAuYo3 status=500 bytes=111 time=247.777µs
curlの結果もこんな感じになる
$ curl -i -X GET -H "Authorization: Bearer hogehogehogehoge" http://localhost:8000/add/1/2 HTTP/1.1 500 Internal Server Error Content-Type: application/json Date: Sun, 20 Oct 2019 12:19:33 GMT Content-Length: 111 {"name":"fault","id":"Aghc9GSY","message":"recover error test","temporary":false,"timeout":false,"fault":true}