如何使 Buffalo 事务中间件提交?

How to cause Buffalo transaction middleware to commit?

在尝试使用 buffalo-pop/pop/popmw Transaction 中间件时,我没有成功写入数据库。未返回任何错误,调试输出显示 SQL 语句,但未提交更新和插入。

处理程序看起来像:


func MyHandler(c buffalo.Context) error {
    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.New("no transaction found")
    }

    f := models.File{
        Name: "file.txt",
    }
    if err := tx.Create(&f); err != nil {
        return err
    }

    return nil
}

app.go:

func App() *buffalo.App {
...
        app.GET("/myhandler", MyHandler)
        app.Use(popmw.Transaction(models.DB))
...
}

如果我使用 DB, _ := pop.Connect("development") 进行连接,它会正常工作。我还观察到每次命中此处理程序时 table 上的自动增量值都会发生变化。

在实际应用中,我们无法调用 c.Render 来报告响应代码,因为我们使用 gqlgen 作为 http 处理程序。它看起来像这样:

func GQLHandler(c buffalo.Context) error {
    h := handler.GraphQL(gqlgen.NewExecutableSchema(gqlgen.Config{Resolvers: &gqlgen.Resolver{}}))
    newCtx := context.WithValue(c.Request().Context(), "BuffaloContext", c)
    h.ServeHTTP(c.Response(), c.Request().WithContext(newCtx))

    return nil
}

Buffalo 的 Pop 中间件的一个特性是将操作和下面的中间件包装在数据库事务中的堆栈中。以下是 Pop 中间件自动提交的条件:

  • Commit if there was no error executing the middlewares and action; and the response status is a 2xx or 3xx.
  • Rollback otherwise.

来自 Buffalo integration with Pop.

因此,请确保您的操作或堆栈的中间件中没有返回错误;并且生成的响应状态为 200-ish 或 300-ish。

如果 Buffalo 通过调用 c.Render 未收到响应代码,事务中间件会将请求视为不成功。由于这个问题的上下文是使用gqlgen的GraphQL,并且不能使用c.Render,所以我发现显式关闭事务是有效的。像这样:

func GQLHandler(c buffalo.Context) error {
    gqlSuccess := true
    h := handler.GraphQL(gqlgen.NewExecutableSchema(gqlgen.Config{Resolvers: &gqlgen.Resolver{}})),
        handler.ErrorPresenter(
            func(ctx context.Context, e error) *gqlerror.Error {
                gqlSuccess = false
                return graphql.DefaultErrorPresenter(ctx, e)
            }))
    newCtx := context.WithValue(c.Request().Context(), "BuffaloContext", c)
    h.ServeHTTP(c.Response(), c.Request().WithContext(newCtx))

    if !gqlSuccess {
        return nil
    }

    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.New("no transaction found")
    }
    return tx.TX.Commit()
}