进阶 www.iris-go.com 2026-10-20 02:39:14 · 18 阅读

第4章 依赖注入

# 依赖注入(Dependency Injection) Iris 通过请求处理器和基于返回值的服务端响应,对依赖注入提供了一流的支持。 使用 Iris 可以获得真正安全的绑定。它的速度极快,性能接近原生处理器,因为我们在服务器上线之前就预先分配好了必要的信息! 依赖可以是函数,也可以是静态值。函数依赖还可以接收先前注册的依赖作为输入参数。 示例代码: ```go func printFromTo(from, to string) string { return "message" } // [...] app.ConfigureContainer(func (api *iris.APIContainer){ api.Get("/{from}/{to}", printFromTo) }) ``` 如上所示,`iris.Context` 这个输入参数完全是可选的。Iris 足够智能,会自动帮你绑定它,无需任何额外操作。 ## 概述 一个路由最常见的处理场景是: - 接收一个或多个路径参数和请求数据(payload) - 返回一个响应(payload),如 JSON、XML 等 在上述场景中,新版 Iris 依赖注入功能比前代快了约 33.2%,进一步缩小了原生处理器与带依赖的处理器之间的性能差距。正是出于安全性和性能的考虑,我们推出了新的 `Party.ConfigureContainer(builder ...func(*iris.APIContainer)) *APIContainer` 方法,它会返回诸如 `Handle(method, relativePath string, handlersFn ...interface{}) *Route` 和 `RegisterDependency` 这样的方法。 看看使用 Iris 后,你的代码可以有多简洁: ```go package main import "github.com/kataras/iris/v12" type ( testInput struct { Email string `json:"email"` } testOutput struct { ID int `json:"id"` Name string `json:"name"` } ) func handler(id int, in testInput) testOutput { return testOutput{ ID: id, Name: in.Email, } } func main() { app := iris.New() app.ConfigureContainer(func(api *iris.APIContainer) { api.Post("/{id:int}", handler) }) app.Listen(":5000", iris.WithOptimizations) } ``` 你的眼睛没骗你。你看得很仔细,这里既没有 `ctx.ReadJSON(&v)`,也没有 `ctx.JSON(send)`,更没有错误处理。虽然如此简洁让人很安心,但如果你确实需要这些功能,你依然可以完全掌控它们,包括来自依赖的错误。以下是 `Party.ConfigureContainers` 新增字段和方法的快速列表: ```go // Container 持有该 Party 特有的依赖注入容器。 // 用它可将函数或结构体(控制器)手动转换为 Handler。 Container *hero.Container // OnError 为该 Party 的 DI Hero Container 及其 handler(或 controller)添加错误处理器。 // "errorHandler" 负责处理 Party 的 hero handler 在依赖注入过程中或 handler 自身执行时 // 可能产生并返回的任何错误。 OnError(errorHandler func(iris.Context, error)) // RegisterDependency 添加一个依赖。 // 该值可以是单个结构体值或一个函数。 // 遵循以下规则: // * {structValue} // * func(accepts ) 返回 或 (, error) // * func(accepts iris.Context) 返回 或 (, error) // // 一个 Dependency 可以接受之前已注册的依赖,并返回一个新依赖或更新后的同一依赖。 // * func(accepts1 , accepts2 ) 返回 或 (, error) 或 error // * func(acceptsPathParameter1 string, id uint64) 返回 或 (, error) // // 用法示例: // // - RegisterDependency(loggerService{prefix: "dev"}) // - RegisterDependency(func(ctx iris.Context) User {...}) // - RegisterDependency(func(User) OtherResponse {...}) RegisterDependency(dependency interface{}) // UseResultHandler 向 Container 添加一个结果处理器。 // 结果处理器可用于注入请求 handler 返回的结构体值,或替换默认的渲染器。 UseResultHandler(handler func(next iris.ResultHandler) iris.ResultHandler) type ResultHandler func(ctx iris.Context, v interface{}) error // Use 与普通 Party 的 "Use" 类似,但它接受动态函数作为 "handlersFn" 输入。 Use(handlersFn ...interface{}) // Done 与普通 Party 的 "Done" 类似,但它接受动态函数作为 "handlersFn" 输入。 Done(handlersFn ...interface{}) ``` 4.1 依赖注入与处理器函数 `Handle` 方法的行为与普通 Party 的 `Handle` 一致,但它接受一个或多个 `handlersFn` 函数。每个函数都可以接收与 Party 已注册的容器 `Dependencies` 相匹配的任意输入参数,以及任意输出结果。这些结果可以是自定义结构体 ``、`string`、`[]byte`、`int`、`error`、上述类型的组合,甚至是 `hero.Result`(`hero.View` 或 `hero.Response`)等。 通常情况下,Hero 的处理器甚至不需要接收 `Context`。为此,如果开发者没有手动调用,`handlersFn` 会自动调用 `ctx.Next()`。若要停止执行且不再继续调用下一个 `handlersFn`,终端开发者应输出一个错误并返回 `iris.ErrStopExecution`。 ```go Handle(method, relativePath string, handlersFn ...interface{}) *Route // Get 注册一个 GET 路由,等同于 `Handle("GET", relativePath, handlersFn....)`。 Get(relativePath string, handlersFn ...interface{}) *Route // 依此类推…… ``` 4.2 内置依赖列表 以下是所有可用的内置依赖列表:Request、Response 和 Path Parameters。 步骤 1. 声明 Go 类型用于客户端请求体和服务端响应 ```go type ( request struct { Firstname string `json:"firstname"` Lastname string `json:"lastname"` } response struct { ID uint64 `json:"id"` Message string `json:"message"` } ) ``` 步骤 2. 创建路由处理器 路径参数和请求体会被自动绑定: - `id uint64` 绑定到 `"id:uint64"` - `input request` 绑定到客户端请求数据(如 JSON) ```go func updateUser(id uint64, input request) response { return response{ ID: id, Message: "User updated successfully", } } ``` 步骤 3. 按组配置容器并注册路由 ```go app.Party("/user").ConfigureContainer(container) func container(api *iris.APIContainer) { api.Put("/{id:uint64}", updateUser) } ``` 步骤 4. 模拟客户端请求,发送数据到服务器并显示响应 ```bash curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42 ``` ```javascript { "id": 42, "message": "User updated successfully" } ``` # 自定义 Preflight 在继续下一节「注册依赖」之前,你可能会想了解如何在响应发送给客户端之前,通过 iris.Context 对其进行自定义。 服务器会在把响应发送给客户端之前,自动执行函数返回值结构体上的 Preflight(iris.Context) error 方法。 举个例子:假设你想根据 handler 内部的自定义逻辑返回不同的 HTTP 状态码,并且想在响应发送给客户端之前修改响应体的内容。这时你的响应类型可以定义一个如下所示的 Preflight 方法。 gotype response struct { ID uint64 `json:"id,omitempty"` Message string `json:"message"` Code int `json:"code"` Timestamp int64 `json:"timestamp,omitempty"` } func (r *response) Preflight(ctx iris.Context) error { if r.ID > 0 { r.Timestamp = time.Now().Unix() } if r.Code > 0 { ctx.StatusCode(r.Code) } return nil }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这样,所有返回 *response 值的 handler 都会自动调用 response.Preflight 方法。 gofunc deleteUser(db *sql.DB, id uint64) *response { // [...custom logic] return &response{ Message: "User has been marked for deletion", Code: iris.StatusAccepted, } }1
2
3
4
5
6
7
8
注册路由并发起请求后,你会看到如下输出:timestamp 字段已被填充,客户端收到的 HTTP 状态码为 202(Status Accepted)。 javascript{ "message": "User has been marked for deletion", "code": 202, "timestamp": 1583313026 }1
2
3
4
5
# 注册依赖 1. 导入与数据库交互所需的包。go-sqlite3 是 SQLite 的数据库驱动。 goimport "database/sql" import _ "github.com/mattn/go-sqlite3"1
2
2. 配置容器以注册你的依赖。Handler 需要一个 *sql.DB 实例。 golocalDB, _ := sql.Open("sqlite3", "./foo.db") api.RegisterDependency(localDB)1
2
3. 注册创建用户的路由。 goapi.Post("/{id:uint64}", createUser)1
4. 创建用户处理器(Handler)。该 Handler 接收数据库及客户端请求数据(如 JSON、Protobuf、Form、URL Query 等),并返回响应。 ```go func createUser(db *sql.DB, user request) *response { // [使用 db 的自定义逻辑] userID, err := db.CreateUser(user) if err != nil { return &response{ Message: err.Error(), Code: iris.StatusInternalServerError, } } return &response{ ID: userID, Message: "User created", Code: iris.StatusCreated, } } ``` 5. 模拟客户端创建用户。 ```bash # JSON curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \ --header 'Content-Type: application/json' \ http://localhost:8080/user ``` ```bash # Form (multipart) curl --request POST 'http://localhost:8080/users' \ --header 'Content-Type: multipart/form-data' \ --form 'firstname=John' \ --form 'lastname=Doe' ``` ```bash # Form (URL-encoded) curl --request POST 'http://localhost:8080/users' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'firstname=John' \ --data-urlencode 'lastname=Doe' ``` ```bash # URL Query curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe' ``` 响应: ```javascript { "id": 42, "message": "User created", "code": 201, "timestamp": 1583313026 } ``` 返回值 查看所有输出值及可能结果的列表。 ```go type response struct { ID uint64 `json:"id,omitempty"` Message string `json:"message"` Code int `json:"code"` Timestamp int64 `json:"timestamp,omitempty"` } func (r *response) Preflight(ctx iris.Context) error { if r.ID > 0 { r.Timestamp = time.Now().Unix() } ctx.StatusCode(r.Code) return nil } func deleteUser(db *sql.DB, id uint64) *response { // [...自定义逻辑] return &response{ Message: "User has been marked for deletion", Code: iris.StatusAccepted, } }

评论 (0)