- Created by 郭强, last modified on May 17, 2023
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
Version 34 Next »
一、基本介绍
从v2.0
版本开始,框架的Server
组件提供了规范化的路由注册方式,更加适合团队规范化的使用场景,实现了以下特性:
- 规范化API按照结构化编程设计
- 规范化API接口方法参数风格定义
- 更加简化的路由注册与维护
- 统一接口返回数据格式设计
- 保障代码与接口文档同步维护
- 自动的API参数对象化接收与校验
- 自动生成基于标准
OpenAPIv3
协议的接口文档 - 自动生成
SwaggerUI
页面
注意一下哦:规范路由与原有的函数、对象、分组路由方式都是框架的HTTP Server组件并存支持的路由注册方式,是为了解决一些规范化和自动化管理接口的场景,更适用于团队多人协作使用。其他的路由方式,特别是框架v1
版本的函数、对象注册方式也是在新版本支持的哈!看个人使用习惯进行合理选择哟。
二、简单示例
1、配置文件
这里使用YAML
配置文件:config.yaml
server: address: ":8199" openapiPath: "/api.json" swaggerPath: "/swagger"
2、示例代码
我们从一个简单的Hello
例子开始:
package main import ( "context" "fmt" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" ) type HelloReq struct { g.Meta `path:"/hello" method:"get"` Name string `v:"required" dc:"Your name"` } type HelloRes struct { Reply string `dc:"Reply content"` } type Hello struct{} func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) { g.Log().Debugf(ctx, `receive say: %+v`, req) res = &HelloRes{ Reply: fmt.Sprintf(`Hi %s`, req.Name), } return } func main() { s := g.Server() s.Use(ghttp.MiddlewareHandlerResponse) s.Group("/", func(group *ghttp.RouterGroup) { group.Bind( new(Hello), ) }) s.Run() }
拷贝这段代码,我们运行起来试试,终端输出信息如下:
2021-11-19 23:31:35.277 25580: http server started listening on [:8199] SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE ----------|---------|---------|--------|------------|-----------------------------------------------------------|-------------------- default | default | :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.MiddlewareHandlerResponse | GLOBAL MIDDLEWARE ----------|---------|---------|--------|------------|-----------------------------------------------------------|-------------------- default | default | :8199 | ALL | /api.json | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec-fm | ----------|---------|---------|--------|------------|-----------------------------------------------------------|-------------------- default | default | :8199 | GET | /hello | main.(*Hello).Say | ----------|---------|---------|--------|------------|-----------------------------------------------------------|-------------------- default | default | :8199 | ALL | /swagger/* | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI-fm | HOOK_BEFORE_SERVE ----------|---------|---------|--------|------------|-----------------------------------------------------------|--------------------
可以看到,除了我们的业务路由之外,Server
自动帮我们注册了两个路由:/api.json
和/swagger/*
。前者是自动生成的基于标准的OpenAPIv3
协议的接口文档,后者是自动生成SwaggerUI
页面,方便开发者查看和调试。这两个功能默认是关闭的,开发者可以通过前面示例中的openapiPath
和swaggerPath
两个配置项开启。
3、接口文档
接口文档通过OpenAPIv3
协议生成,一般来说需要结合相应的UI工具查看,地址:http://127.0.0.1:8199/api.json
由于OpenAPIv3
协议是规范的接口定义协议,因此开发者根据协议内容可以做很多事,例如:自定义UI展示、Client代码生成、协议转换等等。
4、SwaggerUI
我们来看看这个SwaggerUI
页面:http://127.0.0.1:8199/swagger/
这里只有一个我们的路由地址以及对应的输入输出结构体。当然,这只是个简单的示例,你可以在真实的项目中通过一些配置使得页面更加丰富多彩。
我们接着在这个页面上做下接口测试吧:
嗯,接口返回了一个固定数据格式的Json
内容,但是能看到其中的data
为我们需要的返回结果。
5、返回中间件
等等,似乎漏掉了什么东西?是的,我们这里使用了一个Server
组件提供的中间件,它是拿来做什么的呢?我们开看看它的方法定义:
是的,它在我们没有提供自定义的返回数据格式处理中间件时,使用了一个默认的中间件处理我们的请求,并返回了一个默认的数据格式。
三、如何使用
1、路由方法定义
从上面的例子中,我们可以看到,路由方法定义使用固定的格式:
func Handler(ctx context.Context, req *Request) (res *Response, err error)
其中输入参数和输出参数都是两个,并且都是必须的一个都不能少。简单介绍下:
参数 | 说明 | 注意事项 |
---|---|---|
ctx context.Context | 上下文 | Server 组件会自动从请求中获取并传递给接口方法 |
req *Request | 请求对象 | 就算没有接收参数也要定义,因为请求结构体中不仅仅包含请求参数的定义,也包含了接口的请求定义。 |
res *Response | 返回对象 | 就算没有返回参数也要定义,因为返回结构体中不仅仅包含返回参数的定义,也可以包含接口返回定义。 |
err error | 错误对象 | Server 通过该参数判断接口执行成功或失败。 |
2、请求/返回结构体
在规范化路由注册中,非常重要的是请求/返回结构体的定义,在该结构体不仅仅包含了输入参数的定义,也包含了接口的定义,特别是路由地址、请求方法、接口描述等信息。为保证命名规范化,输入数据结构以XxxReq
方式命名,输出数据结构以XxxRes
方式命名。即便输入或者输出参数为空,也需要定义相应的数据结构,这样的目的一个是便于后续扩展,另一个是便于接口信息的管理。关于结构体中涉及到OpenAPIv3
协议的标签介绍请查看后续章节。
请求参数自动转换到请求数据结构,字段映射转换不区分大小写,也会自动忽略特殊字符。
3、数据校验
请求结构体在进入API接口执行前将会被自动执行校验,如果其中一条规则校验失败,那么将终止后续规则的校验。校验功能使用的是框架统一的校验组件,具体请参考:数据校验
4、数据返回
接口的数据返回处理需要设置统一的后置中间件,当然也可以使用Server
默认提供的数据返回中间件。开发者自定义中间件时可以参考Server
默认提供的中间件。注意其中的一个重要的方法:
// GetHandlerResponse retrieves and returns the handler response object and its error. // return type handlerResponse struct { // Object interface{} // Error error // } func (r *Request) GetHandlerResponse() interface{}
通过后置中间件执行时通过请求对象的GetHandlerResponse
方法获取当前业务执行的结果,并根据需要做相应处理。
5、路由注册
我们推荐使用对象化的方式来管理所有路由方法,并通过分组路由的Bind
方法执行统一注册。
需要注意的是,在规范化路由方式下,路由地址以及请求方式将由请求结构体在g.Meta
中定义,通过分组路由可以定义分组下的所有路由前缀。
四、OpenAPIv3
协议
Server
组件自动生成的接口文档使用的是最新的OpenAPIv3
协议。更多介绍请参考章节:接口文档
五、Ctx
中的Request
对象
我们可以通过RequestFromCtx/g.RequestFromCtx
方法从ctx
中获取Request
对象。
方法定义:
func RequestFromCtx(ctx context.Context) *Request
使用示例:
func (c *cHello) Hello(ctx context.Context, req *apiv1.HelloReq) (res *apiv1.HelloRes, err error) { g.RequestFromCtx(ctx).Response.Writeln("Hello World!") return }
六、常见问题
1、在规范路由下,同一接口如何支持多种HTTP Method
提交方式
一个接口应当只做一件事情,HTTP Method
是有意义的(例如RESTful
接口风格设计),一个接口支持多种HTTP Method
方式是接口设计不合理,在规范路由下不支持。建议重新审视接口设计。
例如,拿用户接口来讲,一个CURD
接口在RESTful
实现应当有4-5
个API
定义,实现不同的业务逻辑。那么可能存在以下API
定义的RESTful
接口:
接口名称 Method Path 创建用户 PUT /user 用户列表 GET /user 用户详情 GET /user/{uid} 修改用户 POST /user/{uid} 删除用户 DELETE /user/{uid}
不存在一个API
需要绑定多个HTTP Method
的场景。
如果确实需要注册类似于ALL
这种请求处理路由,在标准的OpenAPI
协议里面不支持,可以不使用规范路由,而是使用普通的路由注册方式,将方法定义为func(r *ghttp.Request)
即可。不同的路由注册方式可以结合使用,虽然不是很推荐。
2、在使用默认提供的Response
结构体下,如何让Data
字段只返回数组而无需指定名称的键值对
使用类型别名即可。
源码地址:https://github.com/gogf/gf/tree/master/example/httpserver/response_with_json_array
结果示例:
- No labels