路由方法定义

从上面的例子中,我们可以看到,路由方法定义使用固定的格式:

func Handler(ctx context.Context, req *Request) (res *Response, err error)

其中输入参数和输出参数都是两个,并且都是必须的一个都不能少。简单介绍下:

参数说明注意事项
ctx context.Context上下文Server组件会自动从请求中获取并传递给接口方法
req *Request请求对象

就算没有接收参数也要定义,因为请求结构体中不仅仅包含请求参数的定义,也包含了接口的请求定义。

res *Response返回对象

就算没有返回参数也要定义,因为返回结构体中不仅仅包含返回参数的定义,也可以包含接口返回定义。

err error错误对象Server通过该参数判断接口执行成功或失败。

路由统一注册

Group.Bind方法

我们推荐使用对象化的方式来管理所有路由方法,并通过分组路由Bind方法执行统一注册。需要注意的是,在规范化路由方式下,路由地址以及请求方式将由请求结构体在g.Meta元数据对象中通过标签定义,通过分组路由可以定义分组下的所有路由前缀。

BindHandler方法

我们也可以通过基础的BindHandler方法来注册规范路由,但是该方法只能注册一个路由函数。使用示例:

s := g.Server()
s.BindHandler("/user/{uid}", func(ctx context.Context, req *SaveReq) (res *SaveRes, err error) {
	// ...
})

规范参数结构

在规范化路由注册中,非常重要的是请求/返回参数结构体的定义,在该结构体不仅仅包含了输入参数的定义,也包含了接口的定义,特别是路由地址、请求方法、接口描述等信息。通过结构体方式的数据结构参数维护,更有利于更丰富的接口能力扩展、团队间的接口交互、长期的接口维护、自动化的接口文档生成。

为保证命名规范化,输入数据结构以XxxReq方式命名,输出数据结构以XxxRes方式命名。即便输入或者输出参数为空,也需要定义相应的数据结构,这样的目的一个是便于后续扩展,另一个是便于接口信息的管理。关于结构体中涉及到OpenAPIv3协议的标签介绍请查看后续章节。

请求参数自动转换到请求数据结构,字段映射转换不区分大小写,也会自动忽略特殊字符。

输入数据校验

请求结构体在进入API接口执行前将会被自动执行校验,如果其中一条规则校验失败,那么将终止后续规则的校验(自动使用了bail校验修饰规则)。校验功能使用的是框架统一的校验组件,具体请参考:数据校验

需要特别注意:如果参数校验存在多条校验规则,并且规则中存在required*规则时,那么建议将required*校验规则放置于所有规则之前,否则规范路由中内置启用的bail校验规则的特性(校验失败即停止后续校验)有可能会引起后续规则的required*规则不生效。

统一返回中间件

接口的数据返回处理需要设置统一的后置中间件,当然也可以使用Server默认提供的数据返回中间件。开发者自定义中间件时可以参考Server默认提供的中间件MiddlewareHandlerResponse

这里顺便提一下,在自定义返回中间件时一个重要的方法:

// GetHandlerResponse retrieves and returns the handler response object and its error. 
func (r *Request) GetHandlerResponse() interface{} 

通过后置中间件执行时通过请求对象的GetHandlerResponse方法获取当前业务执行的结果,并根据需要做相应处理。

扩展介绍

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
}



Content Menu

  • No labels

5 Comments

  1. 结合最后一部分:

    Ctx中的Request对象

    这么说,也可以不通过 自己往ctx中写入session变量了,直接通过 g.RequestFromCtx.Session 即可

  2. “这里顺便提一下,在自定义返回中间件时一个重要的方法:

    // GetHandlerResponse retrieves and returns the handler response object and its error. 
    func (r *Request) GetHandlerResponse() interface{} 

    通过后置中间件执行时通过请求对象的GetHandlerResponse方法获取当前业务执行的结果,并根据需要做相应处理。” 读起来有些拗口。

    其中①

    “在自定义返回中间件时一个重要的方法”

    可以修改成

    “在自定义返回中间件时的一个重要方法”;

    “通过后置中间件执行时通过请求对象的GetHandlerResponse方法获取当前业务执行的结果,并根据需要做相应处理。”

    可修改成

    “在后置中间件执行时,可通过请求对象的 GetHandlerResponse方法来获取当前业务执行的结果,并可根据需要做相应处理。”。

  3. 大佬,遇到一个问题,困扰了半天

    post请求,参数为{"data":"密文"},所有的请求都是这种方式,密文解密后是真正需要验证的请求参数,比如{"id":1,"name":"lucy"},id和name为必填字段

    使用规范路由,restful风格的接口设计,很容易猜到请求参数,解密是在中间件中处理

    模拟请求,{"data":"","id":1,"name":"lucy"},因为data为空字符串,即使在中间件中自定义参数,请求校验也会通过

    我只想设置data解密后的数据去校验,而其它参数成为了校验参数,造成了干扰,目前好像还没有解决方案

  4. 我这写了一个后置中间件统一处理返回error提示的中间件:


    // HandleReturn 后置中间件:处理返回数据
    func (s *middlewareService) HandleReturn(r *ghttp.Request) {
        r.Middleware.Next()
        err := r.GetError()
        if err != nil {
            r.Response.WriteJsonExit(g.Map{
                "state":   consts.ERROR,
                "message": err.Error(),
            })
        } else {
            res := r.GetHandlerResponse()
            if res != nil {
                r.Response.WriteJsonExit(res)
            }
        }
    }

    r.GetHandlerResponse()它只能获取到规范路由返回的数据和err,对于r.Response.WriteJsonExit(g.Map{
                "state":   consts.ERROR,
                "message": "解析Token数据失败",
            })这样直接返回json的,获取到的内容是nil

  5. 看楼主的中文,感觉很费劲,比如这句:”通过后置中间件执行时通过请求对象的GetHandlerResponse方法获取当前业务执行的结果。“