gin框架学习笔记
官网review
- gin 是用go编写的web框架,由于httprputer(基于radix树路由)速度快了40倍,支持中间件,路由组处理,json等多方式验证,内置了json/xml/html等渲染,是一个易于使用的go 框架
- 如果是用常量,比如http.statusOk impport “net/http”
- gin使用默认的encoding/json作为默认的json包,但是可以通过其他标签构建改变他 (jsoniter: https://github.com/json-iterator/go)
Replace(性能更棒)
import "encoding/json"
json.Marshal(&data)
with
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Marshal(&data)
Replace
import "encoding/json"
json.Unmarshal(input, &data)
with
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
json.Unmarshal(input, &data)
4.gin默认开启msgPack渲染功能,关闭这个功能通过建立指定nomsgpack标签
go build -tags=nomsgpack .
5.参数
// path
name := c.Param("name")
// query
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname")
// body
// Content-Type: application/x-www-form-urlencoded
message := c.PostForm("message")
6.中间件
// 默认创建一个没有任何中间件的路由器
r := gin . New ()
r.Use(gin.Logger())
- 模型绑定和验证
将请求body绑定请求类型,支持绑定,json,xml,yaml和
Gin 使用go-playground/validator/v10进行验证(https://github.com/go-playground/validator)
__NOTE: __ 需要绑定相应的标签在所有的字段上,如果想要绑定的话
gin tingeing2种设置方式去绑定(一定要绑定和应该要绑定) - 一定要绑定
方法:Bind, BindJSON, BindXML, BindQuery, BindYAML, BindHeader
行为: 如没有绑定将导致请求中指,并且使用c.AbortWithError(400, err).SetType(ErrorTypeBind) - 应该要绑定
方法: ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML, ShouldBindHeader
行为:这些方法ShouldBindWith在后台使用。如果存在绑定错误,则返回错误,开发人员有责任适当地处理请求和错误。
// 默认创建一个没有任何中间件的路由器
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
// Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.User != "manu" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
绑定query和body
type Person struct {
Name string `form:"name" json:"name"`
Address string `form:"address" json:"address"`
}
func main() {
route := gin.Default()
route.GET("/testing", startPage)
route.Run(":8085")
}
func startPage(c *gin.Context) {
var person Person
if c.Bind(&person) == nil {
log.Println("====== Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
}
if c.BindJSON(&person) == nil {
log.Println("====== Bind By JSON ======")
log.Println(person.Name)
log.Println(person.Address)
}
c.String(200, "Success")
}
8.重定向
内部和外部发送请求都是简单的,内部和外部都是支持的
会有坑需要注意(https://github.com/gin-gonic/gin/issues/444)
// 外部
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
// 内部
r.POST("/test", func(c *gin.Context) {
c.Redirect(http.StatusFound, "/foo")
})
9.中间件
- 简单中间件(默认gin.Default()/自定义gin.New())
// 自定义中间件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// Set example variable
c.Set("example", "12345")
// before request
c.Next()
// after request
latency := time.Since(t)
log.Print(latency)
// access the status we are sending
status := c.Writer.Status()
log.Println(status)
}
}
func main() {
r := gin.New()
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
example := c.MustGet("example").(string)
// it would print: "12345"
log.Println(example)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
// 默认中间件
func main() {
r := gin.Default()
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
10.Goroutines中间件
当内部使用一个新的在中间件Goroutines处理程序时,您不应该使用其中的原始上下文,您必须使用只读副本。
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// create copy to be used inside the goroutine
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// note that you are using the copied context "cCp", IMPORTANT
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c *gin.Context) {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// since we are NOT using a goroutine, we do not have to copy the context
log.Println("Done! in path " + c.Request.URL.Path)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
11.自定义 HTTP 配置
func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
or
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
12.使用 Gin 运行多项服务
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
var (
g errgroup.Group
)
func router01() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 01",
},
)
})
return e
}
func router02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 02",
},
)
})
return e
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: router01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
err := server01.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
return err
})
g.Go(func() error {
err := server02.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
return err
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
12.定义路由日志格式
路由的默认日志是:
[GIN-debug] POST /foo --> main.main.func1 (3 handlers)
[GIN-debug] GET /bar --> main.main.func2 (3 handlers)
[GIN-debug] GET /status --> main.main.func3 (3 handlers)
修改日志格式使用,gin.DebugPrintRouteFunc函数
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
}
r.POST("/foo", func(c *gin.Context) {
c.JSON(http.StatusOK, "foo")
})
r.GET("/bar", func(c *gin.Context) {
c.JSON(http.StatusOK, "bar")
})
r.GET("/status", func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
})
// Listen and Server in http://0.0.0.0:8080
r.Run()
}
设置获取cookie
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/cookie", func(c *gin.Context) {
cookie, err := c.Cookie("gin_cookie")
if err != nil {
cookie = "NotSet"
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
}
fmt.Printf("Cookie value: %s \n", cookie)
})
router.Run()
}