Versions Compared
Key
- This line was added.
- This line was removed.
- Formatting was changed.
基本介绍
从v2.0
版本开始,框架的Server
组件提供了规范化的路由注册方式,更加适合团队规范化的使用场景,实现了以下特性:组件额外提供了规范化的路由注册方式,更加适合团队规范化的使用场景、业务复杂度更高的项目。规范路由实现了以下特性:
- 规范化
API
按照结构化编程设计 - 规范化
API
接口方法参数风格定义 - 规范化API按照结构化编程设计
- 规范化API接口方法参数风格定义
- 更加简化的路由注册与维护
- 统一接口返回数据格式设计
- 保障代码与接口文档同步维护自动的API参数对象化接收与校验
- 自动的
API
参数对象化接收与校验 - 自动生成基于标准
OpenAPIv3
协议的接口文档 - 自动生成
SwaggerUI
页面
Tip |
---|
注意一下哦:规范路由与原有的函数、对象、分组路由方式都是框架的HTTP Server组件并存支持的路由注册方式,是为了解决一些规范化和自动化管理接口的场景,更适用于团队多人协作使用。其他的路由方式,特别是框架路由方式都是框架的 |
相关内容
1、配置文件
这里使用YAML
配置文件:config.yaml
Code Block | ||
---|---|---|
| ||
server:
address: ":8199"
openapiPath: "/api.json"
swaggerPath: "/swagger" |
2、示例代码
我们从一个简单的Hello
例子开始:
Code Block | ||
---|---|---|
| ||
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()
} |
拷贝这段代码,我们运行起来试试,终端输出信息如下:
Code Block | ||
---|---|---|
| ||
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
Tip |
---|
由于 |
4、SwaggerUI
我们来看看这个SwaggerUI
页面:http://127.0.0.1:8199/swagger/
Image Removed
这里只有一个我们的路由地址以及对应的输入输出结构体。当然,这只是个简单的示例,你可以在真实的项目中通过一些配置使得页面更加丰富多彩。
我们接着在这个页面上做下接口测试吧:
Image Removed
嗯,接口返回了一个固定数据格式的Json
内容,但是能看到其中的data
为我们需要的返回结果。
5、返回中间件
等等,似乎漏掉了什么东西?是的,我们这里使用了一个Server
组件提供的中间件,它是拿来做什么的呢?我们开看看它的方法定义:
Image Removed
是的,它在我们没有提供自定义的返回数据格式处理中间件时,使用了一个默认的中间件处理我们的请求,并返回了一个默认的数据格式。
三、如何使用
1、路由方法定义
从上面的例子中,我们可以看到,路由方法定义使用固定的格式:
Code Block | ||
---|---|---|
| ||
func Handler(ctx context.Context, req *Request) (res *Response, err error) |
其中输入参数和输出参数都是两个,并且都是必须的一个都不能少。简单介绍下:
ctx context.Context
Server
组件会自动从请求中获取并传递给接口方法req *Request
就算没有接收参数也要定义,因为请求结构体中不仅仅包含请求参数的定义,也包含了接口的请求定义。
res *Response
就算没有返回参数也要定义,因为返回结构体中不仅仅包含返回参数的定义,也可以包含接口返回定义。
err error
Server
通过该参数判断接口执行成功或失败。Children Display | ||||
---|---|---|---|---|
|
2、请求/返回结构体
在规范化路由注册中,非常重要的是请求/返回结构体的定义,在该结构体不仅仅包含了输入参数的定义,也包含了接口的定义,特别是路由地址、请求方法、接口描述等信息。为保证命名规范化,输入数据结构以XxxReq
方式命名,输出数据结构以XxxRes
方式命名。即便输入或者输出参数为空,也需要定义相应的数据结构,这样的目的一个是便于后续扩展,另一个是便于接口信息的管理。关于结构体中涉及到OpenAPIv3
协议的标签介绍请查看后续章节。
Tip |
---|
请求参数自动转换到请求数据结构,字段映射转换不区分大小写,也会自动忽略特殊字符。 |
Image Removed
3、数据校验
请求结构体在进入API接口执行前将会被自动执行校验,如果其中一条规则校验失败,那么将终止后续规则的校验。校验功能使用的是框架统一的校验组件,具体请参考:数据校验
4、数据返回
接口的数据返回处理需要设置统一的后置中间件,当然也可以使用Server
默认提供的数据返回中间件。开发者自定义中间件时可以参考Server
默认提供的中间件。注意其中的一个重要的方法:
Code Block | ||
---|---|---|
| ||
// 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
中定义,通过分组路由可以定义分组下的所有路由前缀。
Image Removed
四、OpenAPIv3
协议
Server
组件自动生成的接口文档使用的是最新的OpenAPIv3
协议。更多介绍请参考章节:接口文档
五、Ctx
中的Request
对象
我们可以通过RequestFromCtx/g.RequestFromCtx
方法从ctx
中获取Request
对象。
方法定义:
Code Block | ||
---|---|---|
| ||
func RequestFromCtx(ctx context.Context) *Request |
使用示例:
Code Block | ||
---|---|---|
| ||
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
接口:
Code Block | ||
---|---|---|
| ||
接口名称 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
结果示例:
Image Removed
Panel | ||
---|---|---|
| ||
|