自定义服务

Flamego 提供的核心服务都很实用,但对于开发复杂的 Web 应用来说显然是远远不够的。届时,你必然会需要开发自己的中间件来满足应用的实际需求。

注入服务

Flame 实例是基于 inject.TypeMapper自定义服务 - 图1在新窗口打开 来为处理器提供服务注入功能的,并且内置在了 flamego.Flame自定义服务 - 图2在新窗口打开flamego.Context自定义服务 - 图3在新窗口打开 这两个类型中,以便中间件和处理器进行服务注入的管理和使用。

Map 是用于注入服务类型本身的方法,可以是具体的类型(如 *log.Logger自定义服务 - 图4在新窗口打开 )或接口(如 io.Writer自定义服务 - 图5在新窗口打开):

  1. l := log.New(os.Stdout, "[Flamego] ", 0)
  2. f := flamego.New()
  3. f.Map(l)
  4. // 或
  5. var w io.Writer = &bytes.Buffer{}
  6. f := flamego.New()
  7. f.Map(w)

MapTo 是用于将某个服务类型注入为其实现的某一接口的方法:

  1. buf := &bytes.Buffer{}
  2. f := flamego.New()
  3. f.MapTo(buf, (*io.Writer)(nil))

你也可以使用 MapTo 方法将一个接口变换为另一个接口:

  1. var w io.ReadCloser = io.NopCloser(&bytes.Buffer{})
  2. f := flamego.New()
  3. f.MapTo(w, (*io.Reader)(nil))
  4. f.MapTo(w, (*io.Closer)(nil))

注意

MapTo 仅实现无脑的类型映射,如果服务的底层类型并没有实现对应的接口则会在运行时发生错误。

全局服务

全局服务直接与整个 Flame 实例而非具体某个路由绑定,且可以被所有路由的处理器使用。

全局服务可以通过调用 Flame 实例的 MapMapTo 方法完成注入,或通过调用 Use 方法在全局中间件中注入:

  1. db := database.New()
  2. f := flamego.New()
  3. f.Map(db)
  4. // 或
  5. f := flamego.New()
  6. f.Use(func(c flamego.Context) {
  7. db := database.New()
  8. c.Map(db)
  9. })

组级服务

组级服务可以被组内的所有路由的处理器使用,并且只可以通过组级中间件注入:

  1. f := flamego.New()
  2. f.Group("/user",
  3. func() {
  4. f.Get("/settings", func(user *database.User) {
  5. ...
  6. })
  7. },
  8. func(c flamego.Context) {
  9. user := database.GetUser()
  10. c.Map(user)
  11. },
  12. )
  13. f.Group("/repo", func() {
  14. f.Get("/settings", func(user *database.User) {
  15. // 由于 *database.User 对该路由并不可用,该处理器会发生运行时错误
  16. })
  17. })

上例中,*database.User 仅可被用于位于第 3 至 7 行的路由组内,尝试在该路由组外使用它会导致运行时错误(第 14 行)。

路由级服务

路由级服务仅可被与该路由绑定的处理器使用,并且只可以通过路由级中间件注入:

  1. f := flamego.New()
  2. f.Get("/user",
  3. func(c flamego.Context) {
  4. user := database.GetUser()
  5. c.Map(user)
  6. },
  7. f.Get("/settings", func(user *database.User) {
  8. ...
  9. }),
  10. )
  11. f.Get("/repo", func(user *database.User) {
  12. // 由于 *database.User 对该路由并不可用,该处理器会发生运行时错误
  13. })

上例中,*database.User 仅可被用于位于第 7 至 9 行的路由内,尝试在该路由外使用它会导致运行时错误(第 11 行)。

重载服务

你可以通过重载已注入的服务更变服务的状态或行为。

下面展示了如何在路由级重载一个全局服务:

  1. package main
  2. import (
  3. "bytes"
  4. "io"
  5. "github.com/flamego/flamego"
  6. )
  7. func main() {
  8. f := flamego.New()
  9. f.Use(func(c flamego.Context) {
  10. buf := bytes.NewBufferString("this is from a global service")
  11. f.MapTo(buf, (*io.Reader)(nil))
  12. })
  13. f.Get("/",
  14. func(c flamego.Context) {
  15. buf := bytes.NewBufferString("this is from a route-level service")
  16. f.MapTo(buf, (*io.Reader)(nil))
  17. },
  18. func(r io.Reader) string {
  19. p, err := io.ReadAll(r)
  20. if err != nil {
  21. // 处理错误
  22. }
  23. return string(p)
  24. },
  25. )
  26. f.Run()
  27. }

运行上面的程序并执行 curl http://localhost:2830/ 后,可以在终端看到如下输出:

  1. $ curl http://localhost:2830
  2. this is from a route-level service