Go Fiber 框架系列教程 02:詳解相關(guān) API 的使用
閱讀本文大概需要 5 分鐘。
大家好,我是 polarisxu。
該系列第一篇文章發(fā)出后,大家褒貶不一,很正常。選什么,不選什么,大家自己評(píng)估,沒有什么是最好的。我這個(gè)系列,更多只是讓大家對(duì) Fiber 有些了解,說(shuō)不定正好合你胃口呢?
前面對(duì) Fiber 有了大概的印象。今天著重較深入探討 Fiber 相關(guān)功能。
先從 fiber.New 函數(shù)配置開始。
01 配置
大部分 Go 框架,獲得實(shí)例的函數(shù)是不支持配置的,比如 Gin、Echo 等。但 Fiber 框架的 New 函數(shù)支持傳遞配置:
// New creates a new Fiber named instance.
// app := fiber.New()
// You can pass optional configuration options by passing a Config struct:
// app := fiber.New(fiber.Config{
// Prefork: true,
// ServerHeader: "Fiber",
// })
func New(config ...Config) *App
一般情況,使用默認(rèn)配置即可(即不手動(dòng)傳遞配置),但有必要了解下,通過(guò)配置,我們能干些什么。
比如,我們希望響應(yīng)頭中,Server 用自定義的。
config := fiber.Config{
ServerHeader: "Go Fiber Framework",
}
app := fiber.New(config)
響應(yīng)頭類似這樣:
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Mon, 20 Sep 2021 14:58:45 GMT
Server: Go Fiber Framework
實(shí)際上,在前文模板引擎使用的 Views 就是一個(gè)配置項(xiàng)。
目前配置 29 項(xiàng)之多,有不少是關(guān)于 HTTP 的配置。所有的配置和說(shuō)明可以在文檔找到:https://docs.gofiber.io/api/fiber#config。建議掃一遍,有一個(gè)印象,方便將來(lái)有需求時(shí)知道在這里找。
02 路由
標(biāo)準(zhǔn)庫(kù) net/http 的路由比較簡(jiǎn)單,這大概也是有各種路由庫(kù)(框架)的原因之一。
最簡(jiǎn)單的路由莫過(guò)于直接匹配,如:
// 請(qǐng)求匹配到 /about
app.Get("/about", func(c *fiber.Ctx) error {
return c.SendString("about")
})
而命名路由(也叫參數(shù)路由)是一個(gè)強(qiáng)大框架必須的,即提供占位符。比如:
app.Get("/hello/:username", func(c *fiber.Ctx) error {
str := fmt.Sprintf("Hello, %s", c.Params("username"))
return c.SendString(str)
})
這個(gè)路由就可以匹配任意的以 /hello/ 開頭的請(qǐng)求,比如:/hello/polarisxu,最后會(huì)輸出:Hello, polarixu。
不過(guò),如果請(qǐng)求的剛好是 /hello/ 呢?Fiber 會(huì)返回 404,報(bào)路由找不到。如果你希望這時(shí)候把 username 當(dāng)空處理,而不是返回 404,可以在 :username 后加一個(gè) ?:
app.Get("/hello/:username?", func(c *fiber.Ctx) error {
str := fmt.Sprintf("Hello, %s", c.Params("username"))
return c.SendString(str)
})
此外,還有 + 和 * 進(jìn)行通配,區(qū)別在于 + 要求至少要有一個(gè),而 * 可以沒有。通過(guò) c.Params("+") 和 c.Params("*") 獲得對(duì)于的值。
此外,F(xiàn)iber 還支持有 - 和 . 的復(fù)雜路由,例如:
// http://localhost:3000/flights/LAX-SFO
app.Get("/flights/:from-:to", func(c *fiber.Ctx) error {
fmt.Fprintf(c, "%s-%s\n", c.Params("from"), c.Params("to"))
return nil // LAX-SFO
})
注意,如果路由中需要包含特殊字符,比如
:,需要進(jìn)行轉(zhuǎn)義。
因?yàn)?Fiber 的目標(biāo)之一是成為 Go 最快、最清晰的 Web 框架,因此對(duì)于更復(fù)雜的路由,比如正則表達(dá)式,F(xiàn)iber 不會(huì)支持。
Fiber 還提供了方法,返回所有注冊(cè)的路由信息:
var handler = func(c *fiber.Ctx) error { return nil }
func main() {
app := fiber.New()
app.Get("/john/:age", handler)
app.Post("/register", handler)
data, _ := json.MarshalIndent(app.Stack(), "", " ")
fmt.Println(string(data))
app.Listen(":3000")
}
返回結(jié)果如下:
[
[
{
"method": "GET",
"path": "/john/:age",
"params": [
"age"
]
}
],
[
{
"method": "HEAD",
"path": "/john/:age",
"params": [
"age"
]
}
],
[
{
"method": "POST",
"path": "/register",
"params": null
}
]
]
可以輔助排查路由問(wèn)題。
03 Static
上文介紹了服務(wù)靜態(tài)資源的 Static 方法,這里詳細(xì)解釋下。
Static 方法可以多個(gè)。默認(rèn)情況下,如果目錄下有 index.html 文件,對(duì)目錄的訪問(wèn)會(huì)以該文件作為響應(yīng)。
app.Static("/static/", "./public")
以上代碼用于項(xiàng)目根目錄下 public 目錄的文件和文件夾。
此外,Static 方法有第三個(gè)可選參數(shù),以便對(duì) Static 行為進(jìn)行微調(diào),這可以通過(guò) fiber.Static 結(jié)構(gòu)體控制。
// Static defines configuration options when defining static assets.
type Static struct {
// When set to true, the server tries minimizing CPU usage by caching compressed files.
// This works differently than the github.com/gofiber/compression middleware.
// Optional. Default value false
Compress bool `json:"compress"`
// When set to true, enables byte range requests.
// Optional. Default value false
ByteRange bool `json:"byte_range"`
// When set to true, enables directory browsing.
// Optional. Default value false.
Browse bool `json:"browse"`
// The name of the index file for serving a directory.
// Optional. Default value "index.html".
Index string `json:"index"`
// Expiration duration for inactive file handlers.
// Use a negative time.Duration to disable it.
//
// Optional. Default value 10 * time.Second.
CacheDuration time.Duration `json:"cache_duration"`
// The value for the Cache-Control HTTP-header
// that is set on the file response. MaxAge is defined in seconds.
//
// Optional. Default value 0.
MaxAge int `json:"max_age"`
// Next defines a function to skip this middleware when returned true.
//
// Optional. Default: nil
Next func(c *Ctx) bool
}
上文說(shuō),默認(rèn)情況下,對(duì)目錄訪問(wèn)的索引文件是 index.html,通過(guò) Index 可以改變?cè)撔袨椤H绻胍獑⒂媚夸洖g覽功能,可以設(shè)置 Browse 為 true。
04 路由處理器
在前面提到,F(xiàn)iber 有對(duì)應(yīng)的方法支持所有 HTTP Method。除此之外,還有兩個(gè)特殊的方法:Add 和 All。
Add 方法是所有 HTTP Method 對(duì)應(yīng)方法的底層實(shí)現(xiàn),比如 Get 方法:
func (app *App) Get(path string, handlers ...Handler) Router {
return app.Add(MethodHead, path, handlers...).Add(MethodGet, path, handlers...)
}
它底層調(diào)用了 Add 方法,做了兩次綁定,分別是 HEAD 和 GET,也就是說(shuō),對(duì)于 Get 方法,支持 HTTP GET 和 HEAD。
我之前寫過(guò)一篇文章:網(wǎng)友很強(qiáng)大,發(fā)現(xiàn)了Go并發(fā)下載的Bug。Echo 框架,對(duì)于 Get 方法,只是 HTTP GET,不支持 HEAD 請(qǐng)求。目前看,F(xiàn)iber 的做法更合理。如果你真的只需要 GET,可以通過(guò) Add 方法實(shí)現(xiàn)。
而 All 方法表示支持任意 HTTP Method。
05 Mount 和 Group
Mount 方法可以將一個(gè) Fiber 實(shí)例掛載到另一個(gè)實(shí)例。
func main() {
micro := fiber.New()
micro.Get("/doe", func(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
})
app := fiber.New()
app.Mount("/john", micro) // GET /john/doe -> 200 OK
log.Fatal(app.Listen(":3000"))
}
Group 是路由分組功能,框架基本會(huì)支持該特性,對(duì)于 API 版本控制很有用。
func main() {
app := fiber.New()
api := app.Group("/api", handler) // /api
v1 := api.Group("/v1", handler) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2", handler) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
}
06 fiber.Ctx 的方法
此外,就是 handler 中的參數(shù) fiber.Ctx,這是一個(gè)結(jié)構(gòu)體,包含了眾多的方法(不少都是方便開發(fā)的方法),在使用時(shí)查閱 API 文檔,或訪問(wèn) https://docs.gofiber.io/api/ctx 瀏覽。
這里介紹幾個(gè)其他框架可能沒有的方法。
// BodyParser binds the request body to a struct.
// It supports decoding the following content types based on the Content-Type header:
// application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
// If none of the content types above are matched, it will return a ErrUnprocessableEntity error
func (c *Ctx) BodyParser(out interface{}) error
該方法將請(qǐng)求綁定到結(jié)構(gòu)體。(響應(yīng)的也有 QueryParser 方法,主要處理查詢字符串到結(jié)構(gòu)體的綁定)
看一個(gè)例子:
type Person struct {
Name string `json:"name" xml:"name" form:"name"`
Pass string `json:"pass" xml:"pass" form:"pass"`
}
app.Post("/login", func(ctx *fiber.Ctx) error {
p := new(Person)
if err := ctx.BodyParser(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
return ctx.SendString("Success")
})
// 運(yùn)行下面的命令進(jìn)行測(cè)試
// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000/login
// curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000/login
// curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000/login
// curl -X POST -F name=john -F pass=doe http://localhost:3000/login
// curl -X POST "http://localhost:3000/login?name=john&pass=doe"
關(guān)于獲取參數(shù),包括路由參數(shù)、查詢參數(shù)、表單參數(shù),F(xiàn)iber 都非常友好的提供了可選的默認(rèn)值形式,也就是說(shuō),當(dāng)沒有傳遞對(duì)應(yīng)值時(shí),我們可以給一個(gè)默認(rèn)值,比如:
// 10 是可選的。以下代碼表示,當(dāng) page 參數(shù)沒有傳遞,page=10
page := ctx.Query("page", 10)
默認(rèn)值模式(可選參數(shù))在 Fiber 中有大量使用,這能極大為使用者帶來(lái)方便。
此外,路由參數(shù)還有 ParamsInt 方法,用來(lái)獲取 int 類型的路由參數(shù)。
07 小結(jié)
通過(guò)本文對(duì) Fiber 內(nèi)置功能的介紹,我的感受是,F(xiàn)iber 為開發(fā)者提供了很多便利。如果你沒有用過(guò)其他框架,可能沒有那么大的感受。后續(xù)文章考慮出一個(gè)不同框架相關(guān)寫法的對(duì)比。
下篇文章介紹 Fiber 的中間件~
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語(yǔ)言并創(chuàng)建了 Go 語(yǔ)言中文網(wǎng)!著有《Go語(yǔ)言編程之旅》、開源圖書《Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)》等。
堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長(zhǎng)!也歡迎加我微信好友交流:gopherstudio
