Gin Web框架简介
来源:互联网 发布:家庭网络布线 编辑:程序博客网 时间:2024/04/28 13:55
翻译自: https://github.com/gin-gonic/gin/blob/develop/README.md
Gin Web框架
Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务. 如果你希望构建一个高性能的生产环境,你会喜欢上使用 Gin。
$ cat test.go
package mainimport "github.com/gin-gonic/gin"func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and server on 0.0.0.0:8080}
基准测试
Gin基于HttpRouter的这个定制版本来构建。
查看全部测试
(1): 总重复次数
(2): 单次请求耗时 (ns/op)
(3): 堆内存大小 (B/op)
(4): 单次请求内存分配数 (allocs/op)
Gin v1. stable
- [x] 零分配路由.
- [x] 从路由到写请求, 依然为最快的路由器和框架.
- [x] 完备的单元测试套件.
- [x] Battle tested.(?)
- [x] API冻结, 新的release版不会影响现有的代码.
快速开始
下载并安装Gin:
$ go get github.com/gin-gonic/gin
在代码中import进来:
import "github.com/gin-gonic/gin"
(可选) Import
net/http
. 如果用到诸如http.StatusOK
的常量, 需要引入该包:import "net/http"
API示例
使用 GET, POST, PUT, PATCH, DELETE 以及 OPTIONS
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")}
查询字符串参数
func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") c.String(http.StatusOK, "Hello %s %s", firstname, lastname) }) router.Run(":8080")}
Multipart/Urlencoded表单提交
func main() { router := gin.Default() 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, }) }) router.Run(":8080")}
更多示例: 查询参数 + 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
更多示例: 上传文件
参考问题 #548.
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) 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.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默认配置
使用
r := gin.New()
来代替
r := gin.Default()
使用中间件
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) // Authorization group // authorized := r.Group("/", AuthRequired()) // exactly the same as: 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)的绑定。
所有你想要绑定的域(field), 需要你设置对应的绑定标识。 例如, 要绑定到JSON, 则这样声明json:"fieldname"
。
使用Bind方法时, Gin会尝试通过Content-Type头部来推定绑定的类型(如json还是form)。而如果你明确知道要绑定的类型, 可以使用BindWith方法。
你也可以指定哪些filed需要绑定。 如果某个filed像这样声明: binding:"required"
, 那么在进行绑定时如果发现是空值(注: 是请求中不存在该field名?), 当前的请求会失败并收到错误提示。
// 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 { 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表单请求方式的绑定
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")}
使用以下命令测试:
$ curl -v --form user=user --form password=password http://localhost:8080/login
XML和JSON的渲染
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") // Listen and server on 0.0.0.0:8080 router.Run(":8080")}
HTML模板渲染
Using LoadHTMLTemplates()
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")}
templates/index.tmpl
<html> <h1> {{ .title }} </h1></html>
使用不同路径下但相同文件名的模板
func main() { router := gin.Default() router.LoadHTMLGlob("templates/**/*") router.GET("/posts/index", func(c *gin.Context) { c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ "title": "Posts", }) }) router.GET("/users/index", func(c *gin.Context) { c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ "title": "Users", }) }) router.Run(":8080")}
templates/posts/index.tmpl
{{ define "posts/index.tmpl" }}<html><h1> {{ .title }}</h1><p>Using posts/index.tmpl</p></html>{{ end }}
templates/users/index.tmpl
{{ define "users/index.tmpl" }}<html><h1> {{ .title }}</h1><p>Using users/index.tmpl</p></html>{{ end }}
你也可以使用自己的渲染模板
import "html/template"func main() { router := gin.Default() html := template.Must(template.ParseFiles("file1", "file2")) router.SetHTMLTemplate(html) router.Run(":8080")}
重定向
实现HTTP重定向并不麻烦:
r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")})
内部或外部的地址都是支持的。
定制中间件
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")}
使用 BasicAuth() 中间件
// simulate some private datavar secrets = gin.H{ "foo": gin.H{"email": "foo@bar.com", "phone": "123433"}, "austin": gin.H{"email": "austin@example.com", "phone": "666"}, "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},}func main() { r := gin.Default() // Group using gin.BasicAuth() middleware // gin.Accounts is a shortcut for map[string]string authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{ "foo": "bar", "austin": "1234", "lena": "hello2", "manu": "4321", })) // /admin/secrets endpoint // hit "localhost:8080/admin/secrets authorized.GET("/secrets", func(c *gin.Context) { // get user, it was set by the BasicAuth middleware user := c.MustGet(gin.AuthUserKey).(string) if secret, ok := secrets[user]; ok { c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) } else { c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("}) } }) // Listen and server on 0.0.0.0:8080 r.Run(":8080")}
中间件中的Goroutines
在一个middleware或handler中使用goroutine时, 你不能直接使用源gin.Context, 而只能使用它的一份只读拷贝。
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.ListenAndServe()
, 示例:
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
,详情请参考#296。
router := gin.Default()router.GET("/", handler)// [...]endless.ListenAndServe(":4242", router)
或者, 除使用endless之外的方法:
- manners: 一种平滑关闭自己的Go HTTP服务。
- Gin Web框架简介
- Go语言web框架 gin
- gin框架
- go语言web框架gin 从请求中取参数
- Gin框架初识
- Gin 框架源码笔记
- golang-gin框架笔记
- golang开发框架之gin
- Go实战--通过gin-gonic框架搭建restful api服务(github.com/gin-gonic/gin)
- httprouter框架 (Gin使用的路由框架)
- Spring Web 框架简介
- Java Web框架简介
- Python WEB框架简介
- Web框架:Spring简介
- web框架简介
- web框架简介
- web框架简介
- web框架简介
- 分析iOS Crash文件:符号化iOS Crash文件的3种方法
- springMVC常用注解
- Hadoop 一:Hadoop版本
- Guava代码学习笔记
- iOS陆哥开发笔记(二十) (Quartz2D屏幕截图)
- Gin Web框架简介
- HDU 5661 Claris and XOR
- C++11 标准新特性: 右值引用与转移语义
- 流行的android组件大全
- 一百以内的素数
- shiny分级更新dataframe数值实现
- Spark学习
- 深搜-n皇后问题 1019
- iOS陆哥开发笔记(二十一) (百度地图框架配置)