在goa框架中,如何访问原始请求数据

简介

Goa is a Go framework for writing microservices that promotes best practice by providing a single source of truth from which server code, client code, and documentation is derived. The code generated by Goa follows the clean architecture pattern where composable modules are generated for the transport, endpoint, and business logic layers. The Goa package contains middleware, plugins, and other complementary functionality that can be leveraged in tandem with the generated code to implement complete microservices in an efficient manner. By using goa for developing microservices, implementers don’t have to worry about the documentation getting out of sync as Goa takes care of generating OpenAPI specifications for HTTP based services and gRPC protocol buffer files for gRPC based services (or both if the service supports both transports). Reviewers and consumers can also rest assured that the implementation follows the documentation as the code is generated from the same source.

Goa是一个用于编写微服务的Go框架,它通过提供从服务器代码,客户端代码和文档派生的单一事实来源来促进最佳实践。

参考官网文档,可以采用中间件注入上下文的方式来实现额外信息注入。文档地址

  • 首先,按需编写中间件
package middleware

import (
    "bytes"
    "context"
    "io/ioutil"
    "net/http"
)

type CustomContextKey string

const (
    RawRequestWithoutBody CustomContextKey = "x_xx_rawRequest"
    RawBody               CustomContextKey = "x_xxx_rawBody"
)

func InjectRequest() func(http.Handler) http.Handler {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ctx := context.WithValue(r.Context(), RawRequestWithoutBody, r)

            //需要单独提取body内容,因为goa层处理的时候,会读取,进而使得下游无法【再次】读取
            body, err := ioutil.ReadAll(r.Body)
            if err == nil {
                r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
            }
            ctx = context.WithValue(ctx, RawBody, body)
            req := r.WithContext(ctx)

            h.ServeHTTP(w, req)
        })
    }
}
  • 然后在服务挂载的地方,将中间件挂载到服务中
import (
    "context"
    "net/http"
    "os"
    "sync"
    "time"

    demoHttpSvr "demo/pkg/api/gen/http/demo/server"
    mdlwr "demo/pkg/middleware"

        ...
)

    var (
        demoSever *demoHttpSvr.Server
    )
    {
        eh := errorHandler(logger)
        demoSever = demoHttpSvr.New(demoEndpoints, mux, dec, enc, eh, mdlwr.GoaErrorFormatterFunc)
        servers := goahttp.Servers{
            demoSever,
        }
        //将中间件挂载到服务中
        servers.Use(mdlwr.InjectRequest())
    }
  • 最后,在需要访问原始请求数据的地方访问
import (
    "context"
    "log"

    mdw "demo/pkg/middleware"

    ...
)

...
    //从上下文中读取
    r, ok := ctx.Value(mdw.RawBody).([]byte)
    if ok {
        log.Println(">>>>>>>>>>>>>>", string(r))
    }
...