为什么使用go fiber?

出于好奇心,之前就听闻过这个框架,虽然我的不会Express,听说Fiber是一个参考了Express的Web框架,建立在Go语言写的最快的FasthttpHTTP引擎的基础上。
按官网说的:皆在简化 零内存分配和提高性能,以便快速开发。
至于为啥不用Gin,beego,iris,echo,gf·····感觉上面哪些的框架目前市面上应该是已经很多人,有自己的教脚手架了!我自己出于刚重新接触GO回来没多久的!尝鲜的过程中去学东西也是很有感觉的!所以试一试吧!
目前的它已经出到了V2的版本了,和V1的差别还是比较大的!百度一番之后,也没教程!惯例!哈哈搬砖系列~之官网!!
Fiber 的特点(优势)
官网的大佬的给的几个点:
强大的路由 静态文件服务 极限表现 内存占用低 API 接口 中间件和Next支持 快速服务器端编程 模版引擎 WebSocket 支持 频率限制器 15 种语言
Fiber的限制
由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.18.0 已经在 Go 1.14 到 1.17 上验证过。Fiber 与 net/http 接口不兼容。这意味着你无法使用 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。
Fiber v2.21.0 版本初步使用
本节内容:
热更新插件使用 Fiber app对象的配置项 Fiber app路由和路由组 Fiber app启动监听自定义(http和https的配置)
1、来自官网的示例代码
1.1 fresh热重启
插件:
D:\code\go\awesomeProject1>go get github.com/pilu/fresh
使用:
D:\code\go\awesomeProject1>fresh
20:23:57 runner | InitFolders
20:23:57 runner | mkdir ./tmp
20:23:57 runner | mkdir ./tmp: Cannot create a file when that file already exists.
20:23:57 watcher | Watching .
20:23:57 main | Waiting (loop 1)...
20:23:57 main | receiving first event /
20:23:57 main | sleeping for 600 milliseconds
20:23:57 main | flushing events
20:23:57 main | Started! (5 Goroutines)
20:23:57 main | remove tmp\runner-build-errors.log: The system cannot find the file specified.
20:23:57 build | Building...
20:23:58 runner | Running...
自己玩的示例代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
// 定义路由
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("你好小钟同学!")
})
app.Listen(":3000")
}
启动图示:

然后访问接口即可:http://127.0.0.1:3000/
其实对比Fastapi的框架的来说的,其实框架这东西多数蕾西,而且也和我们的GIN其实总体是保持差不多的。
1.2 其他官网示例的安利
📖 基础路由
func main() {
app := fiber.New()
// GET /api/register
app.Get("/api/*", func(c *fiber.Ctx) error {
msg := fmt.Sprintf("✋ %s", c.Params("*"))
return c.SendString(msg) // => ✋ register
})
// GET /flights/LAX-SFO
app.Get("/flights/:from-:to", func(c *fiber.Ctx) error {
msg := fmt.Sprintf("💸 From: %s, To: %s", c.Params("from"), c.Params("to"))
return c.SendString(msg) // => 💸 From: LAX, To: SFO
})
// GET /dictionary.txt
app.Get("/:file.:ext", func(c *fiber.Ctx) error {
msg := fmt.Sprintf("📃 %s.%s", c.Params("file"), c.Params("ext"))
return c.SendString(msg) // => 📃 dictionary.txt
})
// GET /john/75
app.Get("/:name/:age/:gender?", func(c *fiber.Ctx) error {
msg := fmt.Sprintf("👴 %s is %s years old", c.Params("name"), c.Params("age"))
return c.SendString(msg) // => 👴 john is 75 years old
})
// GET /john
app.Get("/:name", func(c *fiber.Ctx) error {
msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name"))
return c.SendString(msg) // => Hello john 👋!
})
log.Fatal(app.Listen(":3000"))
}
📖 静态文件服务
func main() {
app := fiber.New()
app.Static("/", "./public")
// => http://localhost:3000/js/script.js
// => http://localhost:3000/css/style.css
app.Static("/prefix", "./public")
// => http://localhost:3000/prefix/js/script.js
// => http://localhost:3000/prefix/css/style.css
app.Static("*", "./public/index.html")
// => http://localhost:3000/any/path/shows/index/html
log.Fatal(app.Listen(":3000"))
}
📖 中间件和Next
func main() {
app := fiber.New()
// 全局中间件件,对所有的路由生效
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First handler")
return c.Next()
})
// 匹配所有使用 /api开头的路由
app.Use("/api", func(c *fiber.Ctx) error {
fmt.Println("🥈 Second handler")
return c.Next()
})
// 配置多个中间件一起的使用
app.Use("/api",func(c *fiber.Ctx) error {
c.Set("X-Custom-Header", random.String(32))
return c.Next()
}, func(c *fiber.Ctx) error {
return c.Next()
})
//注册的具体的路由示例,地址为/api/list
app.Get("/api/list", func(c *fiber.Ctx) error {
fmt.Println("🥉 Last handler")
return c.SendString("Hello, World 👋!")
})
log.Fatal(app.Listen(":3000"))
}
📖 根据官网扩展示例-获取路径参数(1)
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
app.Get("/:value", func(c *fiber.Ctx) error {
return c.SendString("value: " + c.Params("value"))
// => Get request with value: hello world
})
app.Listen(":3000")
}
访问地址:http://127.0.0.1:3000/sdf43534 输出的结果是:
value: sdf43534
📖 根据官网扩展示例-获取路径t数(2)
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
//对参数的校验
app.Get("/:name?", func(c *fiber.Ctx) error {
if c.Params("name") != "" {
return c.SendString("Hello " + c.Params("name"))
// => Hello john
}
return c.SendString("Where is john?")
})
app.Listen(":3000")
}
访问的结果:
地址:http://127.0.0.1:3000/name
结果:Hello name
============
地址:http://127.0.0.1:3000
结果:Where is john?
📖 根据官网扩展示例-获取路径t数(3)-通配符的形式
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
//对参数的校验
app.Get("/api/*", func(c *fiber.Ctx) error {
return c.SendString("API path: " + c.Params("*"))
// => API path: user/john
})
app.Listen(":3000")
}
访问:
地址:http://127.0.0.1:3000/api/34534/3453
结果:API path: 34534/3453
============
📖 【新增】多应用和flask和fastapi的多应用有些类似!
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"))
}
上面的访问的的地址则为:GET /john/doe -> 200 OK
2、 Fiber实例的对象的配置
这官网的就有点不地道,锁好翻译很多种语言了!可惜进入官网还是英文!哈哈 那也只好应啃吧!
其实这个的Fiber实例的对象的配置和我们的之前的fastapi的配置是一个概念,就是对我们的Fiber实例的对象进行特殊的参数配置初始化。
2.1 new使用的是默认的配置
app := fiber.New()
2.2 自定义定制个人的配置初始化信息
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
ServerHeader: "Fiber",
})
如上面的开启了多进程之后 Prefork: true,:

2.3 配置初始化信息-配置项有哪些?
挑一些比较值得关注的:
Prefork: 是否开启多进程
默认值是 false 作用: 是否开启多进程模式,官网说注意点是:注意:如果启用了,应用程序将需要在shell中运行,因为预叉模式设置了环境变量。如果您使用的是Docker,请确保该应用程序是与CMD ./app或CMD ["sh", "-c", "/app"] 示例,如果开启多进程的情况后:如上面的开启了多进程之后 Prefork: true,: 
ServerHeader: 定义响应头中的Server的标记头 默认值是 :“” 作用:启用Server具有给定值的http标头。 示例,写入 ServerHeader: "Fiber",:那么我们的请求响应头那就有会:

CaseSensitive: 路由定义大小写问题的匹配 默认值是 :false 作用:启用后,/Foo和/foo是不同的路由。
Immutable :按描述应该是上下文的值是否可复用的问题,以前gin使用的时候也会有这个问题 默认值是 :false 作用:启用后上下文方法返回的所有值都是不可变的。默认情况下,它们在从处理程序返回之前是有效的
UnescapePath:解决的应该是路由中的所有编码字符编码问题
ETag:应该解决缓存的问题
BodyLimit: 默认值是:int类型的4 * 1024 * 1024 作用:为请求体设置允许的最大大小,如果大小超过配置的限制,它将发送413 - Request Entity Too Large回应
Concurrency:
默认值是:int类型的256 * 1024 并发连接的最大数量。 ReadTimeout:
默认值是:time.Duration 读取请求完成的超时时间显示,默认是不约束。 WriteTimeout:
默认值是:time.Duration 写入最长响应时间,默认是不约束。 ReadBufferSize:请求读取的每个连接缓冲区大小
DisableKeepalive:禁用“保持活动连接”,服务器将在向客户端发送第一个响应后关闭传入连接。
ErrorHandler
默认的全局的错误的异常处理。
2.4 应用静态文件服务提供
2.4.1 不设置虚拟路径
配置静态文件:

如设置静态文件的目录:
app.Static("/", "./public")
完整代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
ServerHeader: "Fiber",
})
app.Static("/", "./public")
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
//对参数的校验
app.Get("/api/*", func(c *fiber.Ctx) error {
return c.SendString("API path: " + c.Params("*"))
// => API path: user/john
})
app.Listen(":3000")
}
直接的访问:
http://127.0.0.1:3000/

2.4.2 设置虚拟路径
其中路径实际上不存在于文件系统中,静态方法,为静态目录指定前缀路径
配置静态文件:

如设置静态文件的目录:
app.Static("/static", "./public")
完整代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
ServerHeader: "Fiber",
})
//app.Static("/", "./public")
app.Static("/static", "./public")
// 定义全局的中间件
app.Use(func(c *fiber.Ctx) error {
fmt.Println("🥇 First 2222handler")
return c.Next()
})
//对参数的校验
app.Get("/api/*", func(c *fiber.Ctx) error {
return c.SendString("API path: " + c.Params("*"))
// => API path: user/john
})
app.Listen(":3000")
}
访问地址:
http://127.0.0.1:3000/static/hello.html
结果:

2.4.3 更多的静态服务配置项参数
app.Static("/", "./public", fiber.Static{
Compress: true, //是否开启压缩
ByteRange: true, //是否启用字节范围请求。
Browse: true, //是否启用目录浏览
Index: "index.html" //默认的访问
CacheDuration: 10 * time.Second,//缓存时间
MaxAge: 3600,
})
3、 Fiber路由和路由组
3.1 Fiber路由和路由组
其实这个概念和GIN的类似,如果你接触过GIN的话其实都一样的性质。完整示例:
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
ServerHeader: "Fiber",
})
handler := func(c *fiber.Ctx) error {
c.Set("X-Custom-Header", "saaaaaaaaaa")
return c.SendString("我是你的谁!111111!")
}
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"))
app.Listen(":3000")
}
具体结果:



3.2 查看所有的路由信息列表
package main
import (
"encoding/json"
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
handler := func(c *fiber.Ctx) error {
c.Set("X-Custom-Header", "saaaaaaaaaa")
return c.SendString("我是你的谁!111111!")
}
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
//打印
data, _ := json.MarshalIndent(app.Stack(), "", " ")
fmt.Println(string(data))
app.Listen(":3000")
}
输出的结果为:

4、 Fiber启动的监听
http的启动监听:
通常我们的启动的,可以定制器的HOST和PORT,Fiber也一样的可以提供:
// 设置启动的监听端口,默认的HOTS的是本地
app.Listen(":8080")
// 自定义启动额HOTS+端口
app.Listen("127.0.0.1:8080")
https的启动监听:
app.ListenTLS(":443", "./cert.pem", "./cert.key");
还可以对https进行配置:
&tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
Certificates: []tls.Certificate{
cert,
},
}
使用net.Listen来启动
package main
import (
"crypto/tls"
"net"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
handler := func(c *fiber.Ctx) error {
c.Set("X-Custom-Header", "saaaaaaaaaa")
return c.SendString("我是你的谁!111111!")
}
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
//打印
//data, _ := json.MarshalIndent(app.Stack(), "", " ")
//fmt.Println(string(data))
ln, _ := net.Listen("tcp", ":3000")
cer, _ := tls.LoadX509KeyPair("server.crt", "server.key")
ln = tls.NewListener(ln, &tls.Config{Certificates: []tls.Certificate{cer}})
app.Listener(ln)
}
5、 Fiber请求上下文篇
5.1 添加响应头信息
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
app.Get("/", func(c *fiber.Ctx) error {
c.Accepts("png")
//c.Accepts("json", "text") // "json"
//c.Accepts("application/json") // "application/json"
//新增的响应头信息---下面的字段会出现再响应头
c.Append("Link", "Test")
c.Append("Link", "http://google.com", "http://localhost")
//返回的APP路由堆栈
return c.JSON(c.App().Stack())
})
app.Listen(":3000")
}
结果:

其他设置响应的方法 c.Set():
app.Get("/", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/plain")
// => "Content-type: text/plain"
// ...
c.Vary("Accept-Encoding", "Accept")
})
5.2 返回JSON格式数据
return c.JSON(c.App().Stack())
完整示例:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
type SomeStruct struct {
Name string
Age uint8
}
app.Get("/json1", func(c *fiber.Ctx) error {
// Create data struct:
data := SomeStruct{
Name: "Grame",
Age: 20,
}
return c.JSON(data)
})
app.Get("/json2", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"name": "Gram222e",
"age": 2000,
})
})
app.Listen(":3000")
}
或者链式的返回:
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"message": err.Error(),
})
5.3 获取请求的baseurl和hotsname
fmt.Println("c.BaseURL()", c.BaseURL())
fmt.Println("c.Hostname()", c.Hostname())

5.4 获取POST提交的body参数
示例:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Post("/", func(c *fiber.Ctx) error {
// Get raw body from POST request:
return c.Send(c.Body()) // []byte("user=john")
})
app.Listen(":3000")
}
结果:
5.5 POST中的body参数绑定解析
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
type Person struct {
Name string `json:"name" xml:"name" form:"name"`
Pass string `json:"pass" xml:"pass" form:"pass"`
}
app.Post("/", func(c *fiber.Ctx) error {
p := new(Person)
if err := c.BodyParser(p); err != nil {
return err
}
return c.JSON(p)
})
app.Listen(":3000")
}
结果:

5.6 Cookie设置和删除
package main
import (
"time"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
type Cookie struct {
Name string `json:"name"`
Value string `json:"value"`
Path string `json:"path"`
Domain string `json:"domain"`
MaxAge int `json:"max_age"`
Expires time.Time `json:"expires"`
Secure bool `json:"secure"`
HTTPOnly bool `json:"http_only"`
SameSite string `json:"same_site"`
}
app.Get("/", func(c *fiber.Ctx) error {
cookie := new(fiber.Cookie)
cookie.Name = "john"
cookie.Value = "doe"
cookie.Expires = time.Now().Add(24 * time.Hour)
// Set cookie
c.Cookie(cookie)
return c.SendString("设置cookie成功!")
})
app.Post("/", func(c *fiber.Ctx) error {
//删除所有的ClearCookie
c.ClearCookie()
// 根据键值对删除
c.ClearCookie("user")
return c.SendString("删除cookie成功!")
})
app.Listen(":3000")
}
5.7 文件下载和发送文件
下载文件示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
app.Get("/", func(c *fiber.Ctx) error {
//return c.Download("./files/report-12345.pdf");
// => Download report-12345.pdf
return c.Download("./小同学.txt", "小钟同学的密码文件.txt")
// => Download report.pdf
})
app.Listen(":3000")
}
结果:

发送文件示例代码:
从给定路径传输文件。设置内容-类型响应HTTP报头字段
PS:发送文件时候,默认的是开启了gzipping的压缩机制,如果需要关闭,设置为false即可,如下
c.SendFile("./static/index.html", false);
演示文件结构:
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/sendfile", func(c *fiber.Ctx) error {
return c.SendFile("./public/hello.html")
// Disable compression
//return c.SendFile("./static/index.html", false);
})
app.Listen(":3000")
}
执行:

5.8 上传文件-获取表单文件内容
番外篇说:
1:form-data主要是以键值对的形式来上传参数,同时参数之间以&分隔符分开,同时也可以上传文件,文件上传要指定文件类型。

2:x-www-form-urlencode 这种参数的传递与form-data最大的区别是,x-www-form-urlencode只能是以键值对的形式传参,但是不可以上传文件。
按名称检索MultipartForm文件,第一返回来自给定密钥的文件
示例代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//app.Server().MaxConnsPerIP = 1
app.Post("/", func(c *fiber.Ctx) error {
// Get first file from form field "document":
//MultipartForm()。这将返回一个map[string][]string
file, _ := c.FormFile("5gmsg.conf")
// Save file to root directory:
fmt.Println("文件名称", file.Filename)
return c.SaveFile(file, fmt.Sprintf("./%s", file.Filename))
})
app.Listen(":3000")
}
代码执行结果:

执行上传后:

5.9 多文件接收-获取表单文件内容
示例代码:
```
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Post("/", func(c *fiber.Ctx) error {
//MultipartForm()。这将返回一个map[string][]string
if form, err := c.MultipartForm(); err == nil {
fmt.Println("输出表单信息,", form)
files := form.File["5gmsg.conf"]
fmt.Println("输出files信息,", files)
for _, file := range files {
fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0])
if err := c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)); err != nil {
return c.SendString("上传失败!")
}
}
}
return c.SendString("上传成功!")
})
app.Listen(":3000")
}
```
代码执行结果:

执行上传后:

5.10 获取表单值内容信息
示例代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Post("/", func(c *fiber.Ctx) error {
//MultipartForm()。这将返回一个map[string][]string
fmt.Println("name表单值信息", c.FormValue("name"))
fmt.Println("age表单值信息", c.FormValue("age"))
return c.SendString(fmt.Sprintf("%s-%s-%s", "表单值信息为:", c.FormValue("name"), c.FormValue("age")))
})
app.Listen(":3000")
}
输出结果:

5.11 获取自定义的请求头信息
示例代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
//MultipartForm()。这将返回一个map[string][]string
c.Get("Content-Type") // "text/plain"
return c.SendString(fmt.Sprintf("%s-%s-%s", "请求头信息为:", c.Get("Content-Type"), c.Get("xiaozhong")))
})
app.Listen(":3000")
}
示例:

5.12 获取客户端请求IP信息
c.IP(), c.IPs()
示例代码:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
//MultipartForm()。这将返回一个map[string][]string
return c.SendString(fmt.Sprintf("%s-%s-%s", "请求IP信息为:", c.IP(), c.IPs()))
})
app.Listen(":3000")
}
执行截图:

5.13 判断Content-Type类型和是否XHR请求
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
c.Is("html") // true
c.Is(".html") // true
c.Is("json") // false
c.XHR() // 判断是否XHRjQuery提交
return c.SendString(fmt.Sprintf("%s-%s-%s", "请求IP信息为:", c.Is("html"), c.Is(".html"), c.Is("json")))
})
app.Listen(":3000")
}
5.14 存贮变量传递(中间件之间的信息传递)
这个其实对后续的鉴权非常有用滴,比如我的鉴权完成后,穿得TOKEN到下一个接口!
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//全局的中间件中我们的设置一个参数值信息---
app.Use(func(c *fiber.Ctx) error {
c.Locals("user", "小钟同学-变量存贮传递")
return c.Next()
})
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"name": c.Locals("user"),
"age": 2000,
})
})
app.Listen(":3000")
}
执行结果:

5.15 301或302的重定向
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//全局的中间件中我们的设置一个参数值信息---
app.Get("/coffee", func(c *fiber.Ctx) error {
return c.Redirect("/teapot")
})
app.Get("/teapot", func(c *fiber.Ctx) error {
return c.Status(fiber.StatusTeapot).SendString("我是重定向过来的!")
})
app.Listen(":3000")
}
访问地址:http://127.0.0.1:3000/coffee 执行结果:

其他示例:
app.Get("/", func(c *fiber.Ctx) error {
return c.Redirect("/foo/bar")
return c.Redirect("../login")
return c.Redirect("http://example.com")
return c.Redirect("http://example.com", 301)
})
5.16 Params参数-获取path参数
代码示例:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/user/:name", func(c *fiber.Ctx) error {
c.Params("name")
return c.SendString(fmt.Sprintf("%s-%s", "获取路径参数上的name值:", c.Params("name")))
})
app.Listen(":3000")
}
访问地址:
http://127.0.0.1:3000/user/nihao
执行结果:

复杂匹配示例:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/user/:name", func(c *fiber.Ctx) error {
c.Params("name")
return c.SendString(fmt.Sprintf("%s-%s", "获取路径参数上的name值:", c.Params("name")))
})
app.Get("/user2/*", func(c *fiber.Ctx) error {
return c.SendString(fmt.Sprintf("%s-%s-%s", "获取路径参数上的name值:", c.Params("*"), c.Params("*1")))
})
app.Listen(":3000")
}
可以访问的地址:
http://127.0.0.1:3000/user2/nihao/3453
http://127.0.0.1:3000/user2/nihao3453
http://127.0.0.1:3000/user2/nihao=3453



5.17 获取Query参数和QueryParser参数解析
获取参数示例:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/user", func(c *fiber.Ctx) error {
return c.SendString(fmt.Sprintf("%s-%s-%s", "获取路径参数上的name值:", c.Query("name"), c.Query("age")))
})
app.Listen(":3000")
}
请求返回:

示例绑定解析:
package main
import (
"github.com/gofiber/fiber/v2"
)
type Person struct {
Name string `query:"name"`
Age string `query:"age"`
}
func main() {
app := fiber.New()
app.Get("/user", func(c *fiber.Ctx) error {
p := new(Person)
if err := c.QueryParser(p); err != nil {
return err
}
return c.JSON(p)
})
app.Listen(":3000")
}
请求执行:

获取是有请求路径和参数信息:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/user", func(c *fiber.Ctx) error {
return c.SendString(fmt.Sprintf("%s-%s", "获取访问地址信息:", c.OriginalURL()))
})
app.Listen(":3000")
}
执行结果:

5.18 响应字节流数据
示例代码:
package main
import (
"bytes"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.Send([]byte("Hello, World!")) // => "Hello, World!"
})
app.Get("/user", func(c *fiber.Ctx) error {
return c.SendStream(bytes.NewReader([]byte("Hello, World!"))) // => "Hello, World!"
})
app.Listen(":3000")
}
字节流信息的追加写入:
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
c.Write([]byte("Hello, World!111")) // => "Hello, World!"
c.Write([]byte("Hello, World!222")) // => "Hello, World!"
c.Status(200)
return nil
})
执行结果:

5.19 响应码和响应体同时设置
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
c.Status(200)
return nil
})
app.Get("/ok2", func(c *fiber.Ctx) error {
return c.Status(400).SendString("Bad Request")
})
app.Get("/ok3", func(c *fiber.Ctx) error {
return c.Status(404).SendFile("./public/gopher.png")
})
app.Listen(":3000")
}
5.20 自定义404的错误响应处理
PS:关键坑点:这个的404自定义的话,你需要放到当所有的路由都注册完成后才可以天机,不然它所有的地址都会找不到!PS:关键坑点:这个的404自定义的话,你需要放到当所有的路由都注册完成后才可以天机,不然它所有的地址都会找不到!PS:关键坑点:这个的404自定义的话,你需要放到当所有的路由都注册完成后才可以天机,不然它所有的地址都会找不到!
主要还是使用中间件的方式来处理。
错误的示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
)
func main() {
app := fiber.New()
//坑点注意了!!!!!
//坑点注意了!!!!!
//坑点注意了!!!!!
app.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"code": "404",
"msg": "找不到这个地址啊!你估计是搞错地址了!",
})
})
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
// 设置全局错误
app.Use(recover.New(recover.Config{
//Next: func(c *fiber.Ctx) bool {
// return c.Query("refresh") == "true"
//},
EnableStackTrace: true,
StackTraceHandler: func(e interface{}) {
//堆栈信息
},
}))
// This panic will be catch by the middleware
app.Get("/", func(c *fiber.Ctx) error {
panic("I'm an error")
})
app.Listen(":3000")
}
正确的应该是放最后:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
)
func main() {
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": fiber.StatusInternalServerError,
"msg": err.Error()},
)
},
})
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
// 设置全局错误
app.Use(recover.New(recover.Config{
//Next: func(c *fiber.Ctx) bool {
// return c.Query("refresh") == "true"
//},
EnableStackTrace: true,
StackTraceHandler: func(e interface{}) {
//堆栈信息
},
}))
// This panic will be catch by the middleware
app.Get("/", func(c *fiber.Ctx) error {
panic("大爷!")
})
//放到最后来啊!
app.Use(func(c *fiber.Ctx) error {
fmt.Println(c)
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"code": "404",
"msg": "找不到这个地址啊!你估计是搞错地址了!",
})
})
app.Listen(":3000")
}
执行访问:

6、 Fiber 中间件篇
来自官网的文档的搬砖系列!
6.1 前言:
Fiberv1和Fiberv2的中间件有区别,
Fiberv2中的中间件必须有return c.next()才可以!
其实这个中间件和我们的所知的fastapi和gin的大致的功能是一样的,都是类似的钩子函数一样的。
在FiberV2中它自己内置了很多的中间件:
用于基础使用用户名和密码认证的中间件 - BasicAuth 缓存作用的中间件 - Cache 压缩使用的中间件件 - Compress 跨域中间件- CORS 过期设置的中间件- ETag 设置访问的Favicon图标的中间件 配置静态文件服务的一些配置信息中间件-# FileSystem 限流中间件 -# Limiter 性能分析中间件 -# Pprof 日志记录中间件 -# Logger 全局异常捕获Recover中间件 代理请求访问中间件 全局链路追踪RequestID中间件 Session处理中间件 接口请求超时限制的Timeout中间件
下面我的开始搬砖了!!!我就挑几个聊聊!一一的实践一下看看具体的中间件玩法!
6.2 BasicAuth用户名密码基础认证中间件
首先BasicAuth其实就是请求的时候,你需要提供用户名和账号,来验证!
示例代码:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
)
func main() {
app := fiber.New()
// 配置我们基础的认证的需要的用户和密码信息-----简单的配置
app.Use(basicauth.New(basicauth.Config{
Users: map[string]string{
"name": "xiaozhong",
"password": "123456",
},
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("basicauth 认证的处理成功!!")
})
app.Listen(":3000")
}
浏览器访问吧!这样明细:然后访问地址:

此时需要你提供用户名和密码:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
)
func main() {
app := fiber.New()
// 配置我们基础的认证的需要的用户和密码信息-----简单的配置
app.Use(basicauth.New(basicauth.Config{
Users: map[string]string{
"xiaozhong": "123456",
"tongxue": "123456",
},
Realm: "Forbidden",
Authorizer: func(user, pass string) bool {
if user == "xiaozhong" && pass == "123456" {
return true
}
if user == "tongxue" && pass == "123456" {
return true
}
return false
},
//认证失败,机型让它弹出窗口!!!!!
//Unauthorized: func(c *fiber.Ctx) error {
//
// return c.Status(200).SendString("basicauth 认证的失败!!!!非法访问!")
//},
ContextUsername: "_user",
ContextPassword: "_pass",
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("basicauth 认证的处理成功!!")
})
app.Listen(":3000")
}
输入用户名和密码:



6.3 跨域中间件
跨域默认配置
var ConfigDefault = Config{
Next: nil,
AllowOrigins: "*",
AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
AllowHeaders: "",
AllowCredentials: false,
ExposeHeaders: "",
MaxAge: 0,
}
跨域中间件使用
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)
func main() {
app := fiber.New()
app.Use(cors.New())
// Or extend your config for customization
app.Use(cors.New(cors.Config{
AllowOrigins: "https://gofiber.io, https://gofiber.net",
AllowHeaders: "Origin, Content-Type, Accept",
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("basicauth 认证的处理成功!!")
})
app.Listen(":3000")
}
6.4 限流中间件
限流中间件件默认配置
var ConfigDefault = Config{
Max: 5,
Expiration: 1 * time.Minute,
KeyGenerator: func(c *fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusTooManyRequests)
},
}
限流中间件使用:
示例:5秒内最多给2个访问!
package main
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
func main() {
app := fiber.New()
app.Use(limiter.New(limiter.Config{
Next: nil,
Max: 2,
// 5秒内最多给访问2个
Expiration: 5 * time.Second,
KeyGenerator: func(c *fiber.Ctx) string {
return c.Get("x-forwarded-for")
},
LimitReached: func(c *fiber.Ctx) error {
return c.Status(429).SendString("你丫的访问这么快!!!")
},
//Store: myCustomStore{}
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("basicauth 认证的处理成功!!")
})
app.Listen(":3000")
}
超过的时候:
正常的时候:

6.5 日志中间件
官网文档中提示提供的很多的参数:
甚至它还已经包括了响应体了内容了!!!不需要我fastapi那样还需要自己去写一次了!!!好像可以哟!哈哈
如,日志中可以记录的常量信息如下:
// Logger variables
const (
TagPid = "pid"
TagTime = "time"
TagReferer = "referer"
TagProtocol = "protocol"
TagIP = "ip"
TagIPs = "ips"
TagHost = "host"
TagMethod = "method"
TagPath = "path"
TagURL = "url"
TagUA = "ua"
TagLatency = "latency"
TagStatus = "status" // response status
TagResBody = "resBody" // response body
TagQueryStringParams = "queryParams" // request query parameters
TagBody = "body" // request body
TagBytesSent = "bytesSent"
TagBytesReceived = "bytesReceived"
TagRoute = "route"
TagError = "error"
TagHeader = "header:" // request header
TagQuery = "query:" // request query
TagForm = "form:" // request form
TagCookie = "cookie:" // request cookie
TagLocals = "locals:"
// colors
TagBlack = "black"
TagRed = "red"
TagGreen = "green"
TagYellow = "yellow"
TagBlue = "blue"
TagMagenta = "magenta"
TagCyan = "cyan"
TagWhite = "white"
TagReset = "reset"
)
默认的日志中间件的配置:
var ConfigDefault = Config{
Next: nil,
Format: "[${time}] ${status} - ${latency} ${method} ${path}\n",
TimeFormat: "15:04:05",
TimeZone: "Local",
TimeInterval: 500 * time.Millisecond,
Output: os.Stderr,
}
Next:是处理是否传递到下一个的,可以自定义 Format:定义日志记录的格式 TimeFormat:日志的记录的时间格式 TimeZone:日志记录的时间时区 TimeInterval:时间即那个 Output:输出到哪里,默认的是控制台
默认的日志配置
app.Use(logger.New())
然后请求接口控制台会输出日志信息:

添加全局追踪ID日志配置
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/requestid"
)
func main() {
app := fiber.New()
app.Use(requestid.New())
app.Use(logger.New(logger.Config{
Format: "${pid} ${locals:requestid} ${status} - ${method} ${path}\n",
TimeFormat: "02-Jan-2006",
TimeZone: "Asia/Shanghai",
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
app.Listen(":3000")
}
上面的示例,执行后,我完全没看到有requestid
所以继续往下试一试下入文件看看:
package main
import (
"log"
"os"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/requestid"
)
func main() {
app := fiber.New()
app.Use(requestid.New())
file, err := os.OpenFile("./req.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
defer file.Close()
app.Use(logger.New(logger.Config{
Format: "${pid} ${locals:requestid} ${status} - ${method} ${path}\n",
TimeFormat: "02-Jan-2006",
TimeZone: "Asia/Shanghai",
Output: file,
}))
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
app.Listen(":3000")
}
查看我们的日志文件/req.log:
15784 9e7bd80c-4198-4d3e-800a-7fef42810a55 200 - GET /ok1
15784 9f7bd80c-4198-4d3e-800a-7fef42810a55 200 - GET /ok1
15784 a07bd80c-4198-4d3e-800a-7fef42810a55 200 - GET /ok1
嗯嗯,算是看了!!!!
6.6 全局异常中间件
默认的配置信息:
var ConfigDefault = Config{
Next: nil,
EnableStackTrace: false,
StackTraceHandler: defaultStackTraceHandler,
}
示例:
package main
import (
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
app.Use(recover.New())
// This panic will be catch by the middleware
app.Get("/", func(c *fiber.Ctx) error {
panic("I'm an error")
})
app.Listen(":3000")
}
执行结果:

这里有疑问,我想捕获我自己定义现实怎么显示呢?一个自定义的示例:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
)
func main() {
app := fiber.New()
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
// 设置全局错误
app.Use(recover.New(recover.Config{
//Next: func(c *fiber.Ctx) bool {
// return c.Query("refresh") == "true"
//},
EnableStackTrace: true,
StackTraceHandler: func(e interface{}) {
//堆栈信息
},
}))
// This panic will be catch by the middleware
app.Get("/", func(c *fiber.Ctx) error {
panic("I'm an error")
})
app.Listen(":3000")
}
好像还是有点迷糊?如果全局的处理错误呐?会看我们的fiber.New配置项中其实有一个ErrorHandler,我们只需要在这个地方定义我们的自己的处理器就可以了!!!
自定义全局异常处理函数:
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
)
func main() {
app := fiber.New(fiber.Config{
// Override default error handler
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
//获取当前的异常响应码
code := fiber.StatusInternalServerError
// 如果有自定义的响应码的话,那么就返回自己的自定义的响应码信息
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
fmt.Println(code)
fmt.Println(err)
// 发送自定义的异常错误的响应页面
//err = ctx.Status(code).SendFile(fmt.Sprintf("./%d.html", code))
err = ctx.JSON(fiber.Map{
"code": code,
"msg": err.Error(),
})
// 如果找不到这个页面,那么就直接其他其他的异常
if err != nil {
// In case the SendFile fails
return ctx.Status(fiber.StatusInternalServerError).SendString("程序员哥哥睡眠不足,系统崩溃了!")
}
// 返回这个函数
return nil
},
})
//仅仅是设置响应码信息
app.Get("/ok1", func(c *fiber.Ctx) error {
return c.Status(200).SendString("日志记录!!!")
})
// 设置全局错误
app.Use(recover.New(recover.Config{
//Next: func(c *fiber.Ctx) bool {
// return c.Query("refresh") == "true"
//},
EnableStackTrace: true,
StackTraceHandler: func(e interface{}) {
//堆栈信息
},
}))
// This panic will be catch by the middleware
app.Get("/", func(c *fiber.Ctx) error {
panic("我是故意的!")
})
app.Listen(":3000")
}
然后访问验证:

6.7 全局异常中间件-提取出全局错误处理问题
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"msg": err.Error()},
)
},
})
3.总结
以上仅仅是个人结合官网做的一系列的实践总结梳理,如有笔误!欢迎批评指正!感谢各位大佬!
码字不易,如果觉得有点点帮助的话!要不你点个赞呗 哈哈!
码字不易,如果觉得有点点帮助的话!要不你点个赞呗 哈哈!
码字不易,如果觉得有点点帮助的话!要不你点个赞呗 哈哈!
结尾
END
简书:https://www.jianshu.com/u/d6960089b087
掘金:https://juejin.cn/user/2963939079225608
公众号:微信搜【小儿来一壶枸杞酒泡茶】
小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822




