gqlgen和使用golang的依赖注入

bwntbbo3  于 2023-05-20  发布在  Go
关注(0)|答案(1)|浏览(177)

我正在使用gqlgen lib来实现一个图形ql服务器。下面的设置代码是直接的

port := os.Getenv("PORT")
    if port == "" {
        port = defaultPort
    }

    graphCfg := graph.Config{
        Resolvers: graph.NewRootResolver(),

        // TODO: add complexity calc and shield
        //Complexity: graph.ComplexityRoot{},
        //Directives: graph.DirectiveRoot{},
    }
    graphSchema := graph.NewExecutableSchema(graphCfg)

    var gqlSrv http.Handler
    gqlSrv = handler.NewDefaultServer(graphSchema)
    gqlSrv = factory.Middleware(gqlSrv)
    http.Handle("/graphql", gqlSrv)

根解析程序:

package graph

import (
    communicationResolvers "github.com/venn-city/service-venn-graph/modules/communication/resolvers"
    realEstateResolvers "github.com/venn-city/service-venn-graph/modules/realestate/resolvers"
    socialResolvers "github.com/venn-city/service-venn-graph/modules/social/resolvers"
)

// Generate gql models
//go:generate go run github.com/99designs/gqlgen generate

// This file will not be regenerated automatically.
// It serves as dependency injection for your app, add any dependencies you require here.

type RootResolver struct{}

func NewRootResolver() *RootResolver {
    return &RootResolver{}
}

如注解所述// It serves as dependency injection for your app, add any dependencies you require here.
问题是RootResolver在整个应用程序生命周期中只创建一次。如果我有能力为每个请求创建一个有作用域的IOC容器,那就简单多了。
我目前的方法是编写一个自定义的中间件/处理程序,它将为每个请求创建可执行模式,以便RootResolver将为每个请求创建新的...
例如,我想创建一个带有RequetId字段的记录器,并将该记录器传递给所有解析器和较低级别的逻辑以使用相同的记录器。
会非常感激任何关于这方面的见解!谢谢!

dxxyhpgq

dxxyhpgq1#

在每次请求时重新创建根解析程序不是一个好主意。代码注解中提到的依赖注入是服务范围内的依赖。需要在根解析器中设置一次的内容。例如配置值、缓存等。
如果您需要请求范围的依赖项,请使用gqlgen中间件和Server类型上可用的Around*方法。这些中间件函数获取context.Context作为第一个参数,您可以在其中设置请求范围的值。
gqlgen源代码包含一个代码注解块,解释了请求生命周期是如何工作的(作者:Brandon Sprague):

//  +--- REQUEST   POST /graphql --------------------------------------------+
  //  | +- OPERATION query OpName { viewer { name } } -----------------------+ |
  //  | |  RESPONSE  { "data": { "viewer": { "name": "bob" } } }             | |
  //  | +- OPERATION subscription OpName2 { chat { message } } --------------+ |
  //  | |  RESPONSE  { "data": { "chat": { "message": "hello" } } }          | |
  //  | |  RESPONSE  { "data": { "chat": { "message": "byee" } } }           | |
  //  | +--------------------------------------------------------------------+ |
  //  +------------------------------------------------------------------------+
  • AroundOperations在每个querymutationsubscription操作之前被调用。
  • 在返回响应之前调用AroundResponses
  • AroundRootFields在进入每个根解析程序之前被调用。在上面的查询示例中,它在viewer解析器之前调用。
  • AroundFields在每个字段解析器之前被调用。

如果是日志记录器,可以使用AroundOperations。您可以在中间件本身中计算所需的任何值,或者使用在封闭作用域中初始化的值。

logger := // create some logger

gqlSrv = handler.NewDefaultServer(graphSchema)

gqlSrv.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
        ctx = context.WithValue(ctx, "request-id-key", "1234")
        ctx = context.WithValue(ctx, "logger-key", logger)
        // call next to execute the next middleware or resolver in the chain
        next(ctx)
    })

然后在解析器实现中,您可以像往常一样从上下文访问这些值:ctx.Value("logger-key")。(注意,最好使用未导出的结构体作为上下文键,而不是字符串)。

相关问题