GoFrame 路由注冊(cè)-規(guī)范路由

2022-04-13 15:08 更新

一、基本介紹

從v2.0版本開始,框架的?Server?組件提供了規(guī)范化的路由注冊(cè)方式,更加適合團(tuán)隊(duì)規(guī)范化的使用場(chǎng)景,實(shí)現(xiàn)了以下特性:

  • 規(guī)范化API按照結(jié)構(gòu)化編程設(shè)計(jì)
  • 規(guī)范化API接口方法參數(shù)風(fēng)格定義
  • 更加簡(jiǎn)化的路由注冊(cè)與維護(hù)
  • 統(tǒng)一接口返回?cái)?shù)據(jù)格式設(shè)計(jì)
  • 保障代碼與接口文檔同步維護(hù)
  • 自動(dòng)的API參數(shù)對(duì)象化接收與校驗(yàn)
  • 自動(dòng)生成基于標(biāo)準(zhǔn)?OpenAPIv3?協(xié)議的接口文檔
  • 自動(dòng)生成?SwaggerUI?頁面

二、簡(jiǎn)單示例

1、配置文件

這里使用?YAML?配置文件:?config.yaml?

server:
  address:     ":8199"
  openapiPath: "/api.json"
  swaggerPath: "/swagger"

2、示例代碼

我們從一個(gè)簡(jiǎn)單的?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()
}

拷貝這段代碼,我們運(yùn)行起來試試,終端輸出信息如下:

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  
----------|---------|---------|--------|------------|-----------------------------------------------------------|--------------------

可以看到,除了我們的業(yè)務(wù)路由之外,?Server?自動(dòng)幫我們注冊(cè)了兩個(gè)路由:?/api.json?和?/swagger/*?。前者是自動(dòng)生成的基于標(biāo)準(zhǔn)的?OpenAPIv3?協(xié)議的接口文檔,后者是自動(dòng)生成?SwaggerUI?頁面,方便開發(fā)者查看和調(diào)試。這兩個(gè)功能默認(rèn)是關(guān)閉的,開發(fā)者可以通過前面示例中的?openapiPath?和?swaggerPath?兩個(gè)配置項(xiàng)開啟。

3、接口文檔

接口文檔通過?OpenAPIv3?協(xié)議生成,一般來說需要結(jié)合相應(yīng)的?UI?工具查看,地址:http://127.0.0.1:8199/api.json

由于?OpenAPIv3?協(xié)議是規(guī)范的接口定義協(xié)議,因此開發(fā)者根據(jù)協(xié)議內(nèi)容可以做很多事,例如:自定義?UI?展示、?Client?代碼生成、協(xié)議轉(zhuǎn)換等等。

4、SwaggerUI

我們來看看這個(gè)?SwaggerUI?頁面:http://127.0.0.1:8199/swagger/


這里只有一個(gè)我們的路由地址以及對(duì)應(yīng)的輸入輸出結(jié)構(gòu)體。當(dāng)然,這只是個(gè)簡(jiǎn)單的示例,你可以在真實(shí)的項(xiàng)目中通過一些配置使得頁面更加豐富多彩。

我們接著在這個(gè)頁面上做下接口測(cè)試吧:


嗯,接口返回了一個(gè)固定數(shù)據(jù)格式的?Json?內(nèi)容,但是能看到其中的?data?為我們需要的返回結(jié)果。

5、返回中間件

等等,似乎漏掉了什么東西?是的,我們這里使用了一個(gè)?Server?組件提供的中間件,它是拿來做什么的呢?我們開看看它的方法定義:


是的,它在我們沒有提供自定義的返回?cái)?shù)據(jù)格式處理中間件時(shí),使用了一個(gè)默認(rèn)的中間件處理我們的請(qǐng)求,并返回了一個(gè)默認(rèn)的數(shù)據(jù)格式。

三、如何使用

1、路由方法定義

從上面的例子中,我們可以看到,路由方法定義使用固定的格式:

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

其中輸入?yún)?shù)和輸出參數(shù)都是兩個(gè),并且都是必須的一個(gè)都不能少。簡(jiǎn)單介紹下:

參數(shù)
說明
注意事項(xiàng)
ctx context.Context 上下文 Server組件會(huì)自動(dòng)從請(qǐng)求中獲取并傳遞給接口方法
req *Request 請(qǐng)求對(duì)象

就算沒有接收參數(shù)也要定義,因?yàn)檎?qǐng)求結(jié)構(gòu)體中不僅僅包含請(qǐng)求參數(shù)的定義,也包含了接口的請(qǐng)求定義。

res *Response 返回對(duì)象

就算沒有返回參數(shù)也要定義,因?yàn)榉祷亟Y(jié)構(gòu)體中不僅僅包含返回參數(shù)的定義,也可以包含接口返回定義。

err error 錯(cuò)誤對(duì)象 Server通過該參數(shù)判斷接口執(zhí)行成功或失敗。

2、請(qǐng)求/返回結(jié)構(gòu)體

在規(guī)范化路由注冊(cè)中,非常重要的是請(qǐng)求/返回結(jié)構(gòu)體的定義,在該結(jié)構(gòu)體不僅僅包含了輸入?yún)?shù)的定義,也包含了接口的定義,特別是路由地址、請(qǐng)求方法、接口描述等信息。為保證命名規(guī)范化,輸入數(shù)據(jù)結(jié)構(gòu)以?XxxReq?方式命名,輸出數(shù)據(jù)結(jié)構(gòu)以?XxxRes?方式命名。即便輸入或者輸出參數(shù)為空,也需要定義相應(yīng)的數(shù)據(jù)結(jié)構(gòu),這樣的目的一個(gè)是便于后續(xù)擴(kuò)展,另一個(gè)是便于接口信息的管理。

請(qǐng)求參數(shù)自動(dòng)轉(zhuǎn)換到請(qǐng)求數(shù)據(jù)結(jié)構(gòu),字段映射轉(zhuǎn)換不區(qū)分大小寫,也會(huì)自動(dòng)忽略特殊字符。


3、數(shù)據(jù)校驗(yàn)

請(qǐng)求結(jié)構(gòu)體在進(jìn)入API接口執(zhí)行前將會(huì)被自動(dòng)執(zhí)行校驗(yàn),如果其中一條規(guī)則校驗(yàn)失敗,那么將終止后續(xù)規(guī)則的校驗(yàn)。校驗(yàn)功能使用的是框架統(tǒng)一的校驗(yàn)組件。

4、數(shù)據(jù)返回

接口的數(shù)據(jù)返回處理需要設(shè)置統(tǒng)一的后置中間件,當(dāng)然也可以使用?Server?默認(rèn)提供的數(shù)據(jù)返回中間件。開發(fā)者自定義中間件時(shí)可以參考?Server?默認(rèn)提供的中間件。注意其中的一個(gè)重要的方法:

// GetHandlerResponse retrieves and returns the handler response object and its error. 
// return type handlerResponse struct {
//		Object interface{}
//		Error  error
//	}
func (r *Request) GetHandlerResponse() interface{} 

通過后置中間件執(zhí)行時(shí)通過請(qǐng)求對(duì)象的?GetHandlerResponse?方法獲取當(dāng)前業(yè)務(wù)執(zhí)行的結(jié)果,并根據(jù)需要做相應(yīng)處理。

5、路由注冊(cè)

我們推薦使用對(duì)象化的方式來管理所有路由方法,并通過分組路由的?Bind?方法執(zhí)行統(tǒng)一注冊(cè)。

需要注意的是,在規(guī)范化路由方式下,路由地址以及請(qǐng)求方式將由請(qǐng)求結(jié)構(gòu)體在?g.Meta?中定義,通過分組路由可以定義分組下的所有路由前綴。


四、OpenAPIv3協(xié)議

?Server?組件自動(dòng)生成的接口文檔使用的是最新的?OpenAPIv3?協(xié)議。

五、Ctx中的Request對(duì)象

我們可以通過?RequestFromCtx?/?g.RequestFromCtx?方法從?ctx?中獲取?Request?對(duì)象。

方法定義:

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、在規(guī)范路由下,同一接口如何支持多種HTTP Method提交方式

一個(gè)接口應(yīng)當(dāng)只做一件事情,?HTTP Method?是有意義的,一個(gè)接口支持多種?HTTP Method?方式是接口設(shè)計(jì)不合理,在規(guī)范路由下不支持。建議重新審視接口設(shè)計(jì)。

如果確實(shí)需要注冊(cè)類似于?ALL?這種請(qǐng)求處理路由,在標(biāo)準(zhǔn)的?OpenAPI?協(xié)議里面不支持,可以不使用規(guī)范路由,而是使用普通的路由注冊(cè)方式,將方法定義為?func(r *ghttp.Request)?即可。不同的路由注冊(cè)方式可以結(jié)合使用,雖然不是很推薦。

2、在使用默認(rèn)提供的Response結(jié)構(gòu)體下,如何讓Data字段只返回?cái)?shù)組而無需指定名稱的鍵值對(duì)

使用類型別名即可。

源碼地址:https://github.com/gogf/gf/tree/master/example/httpserver/response_with_json_array

結(jié)果示例:



以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)