專案地址:https://github.com/Codexiaoyi/linweb
這是一個系列文章:
前言
上文說過,linweb不追求效能,相比而言注重程式設計正規化。本人也是dotneter,個人覺得.net那種註解定義路由的方式更為舒服,並且介面檔案統一規定在Controller資料夾下,以 XxxController 命名。
當然,在Go中也是可以實現這樣的方式,但是將用到大量反射,所以勢必會降低效能,所以說"不太一樣的Web框架"。
基本正規化
所謂程式設計正規化,也就是你的框架定義規範,使用使用者按照你的規範寫邏輯業務。
路由
路由解析是一個web框架不可避免的模組,我們看gin是如何定義路由的。
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
在應用gin的時候,我們通常會將路由定義與介面方法對應寫在一起,統一管理,這樣管理方便,也是Go的web框架多數採用的方式。
相比之下,linweb更希望路由定義和方法放在一起,然後以定義Controller的方式,路由地址更加直觀。
package controllers
import (
"github.com/Codexiaoyi/linweb/interfaces"
)
type BlogController struct {
}
//[GET("/blog/:id")]
func (blog *BlogController) GetBlog(c interfaces.IContext) {
}
在根目錄下建立一個 controllers 資料夾,所有的api都定義在controllers包中。根據不同的 controller 名稱區分檔案。
自定義外掛
linweb中所有功能與主流程間的依賴都是解耦的,所有實現都面向介面,完全遵守依賴倒置原則。
要實現這樣的方式也不難:
- 定義一套介面,使得實現相應介面的結構體都可以執行在linweb中。
- 完成預設實現,預設應用預設實現在linweb中。
- 開放傳入自定義外掛實現的方法。
程式碼實現
在Go語言框架中大量使用Option模式實現上述的需求(完整程式碼詳見github)。
1.首先我們定義一個自定義外掛型別,並寫一個預設的外掛函式。
//自定義外掛型別,返回可以傳入Linweb的函式
type CustomizePlugins func(lin *Linweb)
func defaultPlugins() CustomizePlugins {
return func(lin *Linweb) {
lin.markRouter = router.New()
lin.markContext = &context.Context{}
lin.markMiddleware = &middleware.Middleware{}
lin.markInject = injector.Instance()
lin.markCache = cache.Instance()
lin.markModel = &model.Model{}
}
}
2.定義各個外掛模組對應的快捷入口函式。
// Customize router plugin.這裡引數傳入自定義的Router實現
func RouterPlugin(router interfaces.IRouter) CustomizePlugins {
return func(lin *Linweb) {
lin.markRouter = router
}
}
// Customize context plugin.
func ContextPlugin(context interfaces.IContext) CustomizePlugins {
return func(lin *Linweb) {
lin.markContext = context
}
}
......
3.在linweb包的New初始化函式中,傳入使用者自定義的CustomizePlugins。
// Create a new Linweb.
// Add customize plugins with method of plugins.go, otherwise use default plugins.
func NewLinweb(plugins ...CustomizePlugins) *Linweb {
lin := &Linweb{}
//應用預設外掛
defaultPlugins()(lin)
//根據傳入的使用者自定義外掛覆蓋預設外掛
for _, plugin := range plugins {
plugin(lin)
}
pluginsModel = lin.markModel
Cache = lin.markCache
return lin
}
如何應用
在linweb專案中的linweb_test.go中,用到了mock框架(後續測試部分會介紹)模擬介面實現:
// Arrange:mock data
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// mock a new context instance
mock_context := mocks.NewMockIContext(ctrl)
//Act
linweb := NewLinweb(ContextPlugin(mock_context))
如程式碼所見,我們呼叫linweb的ContextPlugin函式傳入自定義外掛,再將返回值傳入NewLinweb初始化函式中。
總結
本文,介紹了linweb的基本的程式設計正規化,也實現了自定義外掛功能。
接下來,我們需要根據前文說的功能逐步新增到linweb中。