GoFrame系列:5、項目配置、服務路由及運作方式
文章目錄
- GoFrame系列:5、項目配置、服務路由及運作方式
- 1. 項目配置
- 1.1 go.mod
- 1.2 配置檔案
- 1.3 啟動設定
- 2. 服務路由
- 2.1 路由注冊
- 2.2 分組路由
- 2.3 中間件注冊
- 3. 運作
- 3.1 配置引入
- 3.2 `main`包
- 3.3 編譯運作
- 3.4 接口測試
- 3.4.1 使用者注冊 - `/user/signup`
- 3.4.2 使用者登入 - `/user/signin`
- 4. 最後
1. 項目配置
我們推薦使用
go.mod
來管理項目依賴,這也是Golang官方推薦的包管理方式。
1.1 go.mod
https://github.com/gogf/gf-demos/blob/master/go.mod
module github.com/gogf/gf-demos
require github.com/gogf/gf latest
go 1.12
其中注意
module
名稱設定為
github.com/gogf/gf-demos
。這裡我們隻需要依賴
GF
架構即可。其中的
go 1.12
表示運作該項目所需的最低
Go
版本,這裡也可以不設定。
Goland
會自動幫我們設定為目前使用的
Go
版本。
1.2 配置檔案
GF
架構的核心元件均實作了便捷的檔案配置管理方式,包括
Server
、日志元件、資料庫
ORM
、模闆引擎等等,非常強大便捷。具體的配置項可以檢視後續對應的章節介紹。
GF
架構大部分的核心元件配置項是不區分大小寫的,但是為保證規範統一,我們建議在配置檔案中統一使用小駝峰形式。
https://github.com/gogf/gf-demos/blob/master/config/config.example.toml
需要注意哦,倉庫中提供的
config.example.toml
為配置檔案示例,如果想要正常運作示例程式,你可以将其拷貝或者重命名為
config.toml
。
配置檔案中如果存在檔案路徑相關的配置,建議統一使用絕對路徑配置,會少踩很多坑。如果使用了相對路徑,你得清楚知道該配置的路徑相對的是哪個路徑(不同的部署方式,工作目錄可能不可預知)。
# HTTP Server.
[server]
address = ":8199"
serverRoot = "public"
serverAgent = "gf-demos"
logPath = "/tmp/log/gf-demos/server"
nameToUriType = 2
routeOverWrite = true
# Logger configurations.
[logger]
path = "/tmp/log/gf-demos"
level = "all"
stdout = true
# Template view configurations.
[viewer]
path = "template"
defaultFile = "index.html"
# Database configurations.
[database]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
debug = true
# 資料庫日志對象配置
[database.logger]
Path = "/tmp/log/gf-demos/sql"
Level = "all"
Stdout = true
# GF-CLI工具配置
[gfcli]
# 自定義DAO生成配置(預設是讀取database配置)
[[gfcli.gen.dao]]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables = "user"
1.3 啟動設定
在
boot
包中執行代碼層級的初始化,比如一些元件子產品的設定。
https://github.com/gogf/gf-demos/blob/master/boot/boot.go
package boot
import (
_ "github.com/gogf/gf-demos/packed"
"github.com/gogf/gf/frame/g"
"github.com/gogf/swagger"
)
// 用于應用初始化。
func init() {
s := g.Server()
s.Plugin(&swagger.Swagger{})
}
可以看到,我們的包初始化管理使用了包初始化方法
init
來做隐式初始化,這樣做的好處是可以在
boot
目錄中使用不同的
go
檔案注冊不同的
init
來分别實作不同的初始化配置管理,在業務比較複雜的項目中比較實用。
2. 服務路由
2.1 路由注冊
這裡使用了分組路由的注冊方式,分組路由也是推薦的路由注冊方式。由于
gf-demos
項目包含其他的示例功能,是以該路由中包含了其他的一些路由注冊項,僅供參考。
https://github.com/gogf/gf-demos/blob/master/router/router.go
package router
import (
"github.com/gogf/gf-demos/app/api"
"github.com/gogf/gf-demos/app/service"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func init() {
s := g.Server()
// 分組路由注冊方式
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(
service.Middleware.Ctx,
service.Middleware.CORS,
)
group.ALL("/chat", api.Chat)
group.ALL("/user", api.User)
group.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware.Auth)
group.ALL("/user/profile", api.User.Profile)
})
})
}
可以看到,我們的路由注冊管理也使用了包初始化方法
init
實作隐式初始化,這樣做的好處是可以在
router
目錄中使用不同的
go
檔案注冊不同的
init
來分别實作不同的路由注冊。當項目的路由比較多的時候,可以采用不同的
go
檔案管理不同的路由,這在團隊協作的項目中也比較友善。
2.2 分組路由
這裡通過
s.Group
方法使用了分組路由實作路由注冊,并通過
group.All
來實作具體的路由注冊,分組路由請參考後續 路由注冊-分組路由 章節。
如果您對
HTTP Method
沒有特殊的需求,建議統一使用
ALL
方法注冊路由,使得該路由可以被任意的
HTTP Method
通路。特别是在跨域場景中,可能會同一個路由接口需要同時滿足
POST
和
OPTIONS
請求,這種情況隻有使用
ALL
方法注冊路由。
2.3 中間件注冊
這裡使用了
group.Middleware
方法注冊中間件,可以将中間件看做請求的攔截器,可以在中間件中實作特定的請求處理後轉發給下一層請求繼續執行。中間件可以注冊多個,
WebServer
會按照中間件注冊的順序執行。中間件的具體介紹請參考後續 路由管理-中間件/攔截器 章節。
在該路由的配置中,所有接口均綁定了兩個中間件:
-
初始化上下文變量service.Middleware.Ctx
-
允許跨域請求的中間件service.Middleware.CORS
其中
/user/profile
路由綁定了鑒權中間件
service.Middleware.Auth
,需要鑒權控制才能通路。
3. 運作
3.1 配置引入
由于
boot
和
router
包使用了
init
包初始化方式來進行相關配置,是以我們需要使用:
import _ "PATH"
方式來引入。
需要注意,由于
Go
的
import
存在先後順序,往往需要将這兩個包置于
main.go
所有業務包的最上方引入。
3.2 main
包
main
每個項目至少存在一個
package main
,用于程式的入口執行。
/main.go
package main
import (
_ "github.com/gogf/gf-demos/boot"
_ "github.com/gogf/gf-demos/router"
"github.com/gogf/gf/frame/g"
)
func main() {
g.Server().Run()
}
需要非常注意:
boot
包的引入往往需要在
main
包中的最頂層位置,以保證應用的初始化最開始進行。特别是一些單例對象的初始化/配置管理操作往往放在
boot
包中,這樣其他包才能正常使用到這些單例對象。
這裡建議引入
boot
包、
router
包和其他包之間加入一個空行以作區分,特别是
Goland
IDE的
import
插件不會将引入包進行自動排序。
3.3 編譯運作
我們可以使用
IDE
執行運作,也可以使用以下指令編譯運作。
$ go build main.go
$ ./main
執行後,終端輸出的路由表如下:
| DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat | github.com/gogf/gf-demos/app/api.(*apiChat).Index | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/index | github.com/gogf/gf-demos/app/api.(*apiChat).Index | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/setname | github.com/gogf/gf-demos/app/api.(*apiChat).SetName | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/websocket | github.com/gogf/gf-demos/app/api.(*apiChat).WebSocket | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /swagger/* | github.com/gogf/swagger.(*Swagger).Install.func1.1 | HOOK_BEFORE_SERVE
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/checknickname | github.com/gogf/gf-demos/app/api.(*apiUser).CheckNickName | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/checkpassport | github.com/gogf/gf-demos/app/api.(*apiUser).CheckPassport | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/issignedin | github.com/gogf/gf-demos/app/api.(*apiUser).IsSignedIn | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/profile | github.com/gogf/gf-demos/app/api.(*apiUser).Profile-fm | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm,service.(*serviceMiddleware).Auth-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signin | github.com/gogf/gf-demos/app/api.(*apiUser).SignIn | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signout | github.com/gogf/gf-demos/app/api.(*apiUser).SignOut | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signup | github.com/gogf/gf-demos/app/api.(*apiUser).SignUp | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
3.4 接口測試
我們通過
curl
指令來對其中兩個接口執行簡單的測試。
3.4.1 使用者注冊 - /user/signup
/user/signup
注冊一個賬号
test001
,昵稱為
john
,密碼為
123456
。
curl -d 'nickname=john&passport=test001&password=123456&password2=123456'
{"data":null,"code":0,"message":"ok"}
我們再次使用剛才的資訊注冊一次試試。
curl -d 'nickname=john&passport=test001&password=123456&password2=123456'
{"data":null,"code":1,"message":"賬号 test001 已經存在"}
可以看到注冊失敗了,相同名稱隻能注冊一個賬号。
3.4.2 使用者登入 - /user/signin
/user/signin
我們先通路擷取使用者資訊的接口,驗證鑒權中間件是否生效。
curl
Forbidden
我們用剛才注冊的賬号登入。
curl -i -d 'passport=test001&password=123456'
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin,Content-Type,Accept,User-Agent,Cookie,Authorization,X-Auth-Token,X-Requested-With
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3628800
Content-Type: application/json
Server: gf-demos
Set-Cookie: gfsessionid=BZT1SP2OX980EHALYV; Path=/; Expires=Sun, 10 Jan 2021 14:56:36 GMT
Date: Sat, 11 Jan 2020 14:56:36 GMT
Content-Length: 37
{"code":0,"message":"ok","data":null}
我們這裡使用了
-i
選項用于終端列印出服務端傳回的
Header
資訊,目的是為了擷取
sessionid
。
GF
架構預設的
sessionid
名稱為
gfsessionid
,我們看到傳回的
Header
中已經有了,并且是通過
Cookie
方式傳回的。
随後我們再次通路擷取使用者資訊接口,并且這次送出
gfsessionid
,該資訊可以通過
Header
也可以通過
Cookie
送出,服務端都是能夠自動識别的。
curl -H 'gfsessionid:BZT1SP2OX980EHALYV'
{"code":0,"message":"","data":{"id":1,"passport":"test001","password":"123456","nickname":