Gin是Golang实现的一种web框架,基于httprouter
贴地址 https://github.com/gin-gonic/gin
快速开始
1下载安装
go get github.com/gin-gonic/gin
2代码导入
import "github.com/gin-gonic/gin"
API示例
func main() { // Creates a gin router with default middleware: // logger and recovery (crash-free) middleware router := gin.Default() router.GET("/someGet", getting) router.POST("/somePost", posting) router.PUT("/somePut", putting) router.DELETE("/someDelete", deleting) router.PATCH("/somePatch", patching) router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options) // By default it serves on :8080 unless a // PORT environment variable was defined. router.Run() // router.Run(":3000") for a hard coded port}复制代码
路径参数
func main() { router := gin.Default() // This handler will match /user/john but will not match neither /user/ or /user router.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) // However, this one will match /user/john/ and also /user/john/send // If no other routers match /user/john, it will redirect to /user/john/ router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") message := name + " is " + action c.String(http.StatusOK, message) }) router.Run(":8080")}复制代码
查询字段
router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") //c.Request.URL.Query().Get("lastname") c.String(http.StatusOK, "Hello %s %s", firstname, lastname) })复制代码
Multipart/Urlencoded表单提交
router.POST("/form_post", func(c *gin.Context) { message := c.PostForm("message") nick := c.DefaultPostForm("nick", "anonymous") c.JSON(200, gin.H{ "status": "posted", "message": message, "nick": nick, }) })复制代码
示例:查询参数+POST表单提交
POST /post?id=1234&page=1 HTTP/1.1Content-Type: application/x-www-form-urlencodedname=manu&message=this_is_great复制代码
func main() { router := gin.Default() router.POST("/post", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080")}复制代码
id: 1234; page: 1; name: manu; message: this_is_great 复制代码
示例:上传文件
func main() { router := gin.Default() router.POST("/upload", func(c *gin.Context) { file, header , err := c.Request.FormFile("upload") //拿到上传的文件的信息 filename := header.Filename fmt.Println(header.Filename) out, err := os.Create("./tmp/"+filename+".png") if err != nil { log.Fatal(err) } defer out.Close() _, err = io.Copy(out, file) //拷贝上传的文件信息到新建的out文件中 if err != nil { log.Fatal(err) } }) router.Run(":8080")}复制代码
分组路由
func main() { router := gin.Default() // Simple group: v1 v1 := router.Group("/v1") { v1.POST("/login", loginEndpoint) //路由分组,访问地址变为:/v1/login 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")}复制代码
使用中间件
func main() { // Creates a router without any middleware by default r := gin.New() // Global middleware //全局中间件 r.Use(gin.Logger()) r.Use(gin.Recovery()) // Per route middleware, you can add as many as you desire. r.GET("/benchmark", MyBenchLogger(), benchEndpoint) //单个router中间件 authorized := r.Group("/") // per group middleware! in this case we use the custom created // AuthRequired() middleware just in the "authorized" group. authorized.Use(AuthRequired()) { authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint) authorized.POST("/read", readEndpoint) // nested group testing := authorized.Group("testing") testing.GET("/analytics", analyticsEndpoint) } // Listen and server on 0.0.0.0:8080 r.Run(":8080")}复制代码
model binding与验证
要绑定一个请求body到某个类型,可以使用model binding。目前支持JSON、XML以及from格式(foo=bar&boo=baz)的绑定
- BindJSON(&json)
- Bind(&form)
c.JSON(http.StatusUnauthorized,gin.H{"status":"unauthorized"})
// Binding from JSONtype Login struct { User string `form:"user" json:"user" binding:"required"` Password string `form:"password" json:"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 c.BindJSON(&json) == nil { //point1 if json.User == "manu" && json.Password == "123" { c.JSON(http.StatusOK, gin.H{ "status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{ "status": "unauthorized"}) } } }) // Example for binding a HTML form (user=manu&password=123) router.POST("/loginForm", func(c *gin.Context) { var form Login // This will infer what binder to use depending on the content-type header. if c.Bind(&form) == nil { if form.User == "manu" && form.Password == "123" { c.JSON(http.StatusOK, gin.H{ "status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{ "status": "unauthorized"}) } } }) // Listen and server on 0.0.0.0:8080 router.Run(":8080")}复制代码
Multipart/Urlencoded表单请求方式的绑定
测试命令:
curl -v --form user=user --form password=password http://localhost:8080/login 复制代码
package mainimport ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding")type LoginForm struct { User string `form:"user" binding:"required"` Password string `form:"password" binding:"required"`}func main() { router := gin.Default() router.POST("/login", func(c *gin.Context) { // you can bind multipart form with explicit binding declaration: // c.BindWith(&form, binding.Form) // or you can simply use autobinding with Bind method: var form LoginForm // in this case proper binding will be automatically selected if c.Bind(&form) == nil { if form.User == "user" && form.Password == "password" { c.JSON(200, gin.H{ "status": "you are logged in"}) } else { c.JSON(401, gin.H{ "status": "unauthorized"}) } } }) router.Run(":8080")} 复制代码
XML和JSON的渲染 输出返回c.JSON() c.XML()
func main() { r := gin.Default() // gin.H is a shortcut for map[string]interface{} r.GET("/someJSON", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "hey", "status": http.StatusOK}) }) r.GET("/moreJSON", func(c *gin.Context) { // You also can use a struct var msg struct { Name string `json:"user"` Message string Number int } msg.Name = "Lena" msg.Message = "hey" msg.Number = 123 // Note that msg.Name becomes "user" in the JSON // Will output : { "user": "Lena", "Message": "hey", "Number": 123} c.JSON(http.StatusOK, msg) }) r.GET("/someXML", func(c *gin.Context) { c.XML(http.StatusOK, gin.H{ "message": "hey", "status": http.StatusOK}) }) // Listen and server on 0.0.0.0:8080 r.Run(":8080")}复制代码
处理静态文件请求
func main() { router := gin.Default() router.Static("/assets", "./assets") router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFile("/favicon.ico", "./resources/favicon.ico") router.Run(":8080")}复制代码
HTML模板渲染
LoadHTMLGlob("/")
c.HTML()
func main() { router := gin.Default() router.LoadHTMLGlob("templates/*") //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") router.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "Main website", }) }) router.Run(":8080")} 复制代码
{ { .title }}
复制代码
用自己的末班
import "html/template"func main() { router := gin.Default() html := template.Must(template.ParseFiles("file1", "file2")) router.SetHTMLTemplate(html) router.Run(":8080")}复制代码
用go-assets,编译一个服务端模板 https://github.com/jessevdk/go-assets
func main() { r := gin.New() t, err := loadTemplate() if err != nil { panic(err) } r.SetHTMLTemplate(t) r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "/html/index.tmpl",nil) }) r.Run(":8080")}// loadTemplate loads templates embedded by go-assets-builderfunc loadTemplate() (*template.Template, error) { t := template.New("") for name, file := range Assets.Files { if file.IsDir() || !strings.HasSuffix(name, ".tmpl") { continue } h, err := ioutil.ReadAll(file) if err != nil { return nil, err } t, err = t.New(name).Parse(string(h)) if err != nil { return nil, err } } return t, nil}复制代码
重定向
非常方便
r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")})复制代码
定制中间件
核心c.Next()
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 server on 0.0.0.0:8080 r.Run(":8080")}复制代码
中间件中的Goroutines
在middleware或者handler中使用goroutine时,不能直接使用gin.Context,只能用它的一份拷贝。 cCp := c.Copy()
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 server on 0.0.0.0:8080 r.Run(":8080")}复制代码
自定义HTTP配置
直接使用http.ListenAndServer(),示例
func main() { router := gin.Default() http.ListenAndServe(":8080", router)}复制代码
或者
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()}复制代码
平滑重启或关闭
库名fvbock/endless, 来代替ListenAndServe
router := gin.Default()router.GET("/", handler)// [...]endless.ListenAndServe(":4242", router)复制代码
替代endless,可选择的有...,写一个得了
graceful
import ( "context" "log" "net/http" "os" "os/signal" "time" "github.com/gin-gonic/gin")func main() { router := gin.Default() router.GET("/", func(c *gin.Context) { time.Sleep(5 * time.Second) c.String(http.StatusOK, "Welcome Gin Server") }) srv := &http.Server{ Addr: ":8080", Handler: router, } go func() { // service connections if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } }() // Wait for interrupt signal to gracefully shutdown the server with // a timeout of 5 seconds. quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) <-quit log.Println("Shutdown Server ...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown:", err) } log.Println("Server exiting")}复制代码