翻译自: https://github.com/gin-gonic/gin/blob/develop/README.md

Gin Web框架





branch=master">



Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了相似martini但更好性能(路由性能约快40倍)的API服务. 假设你希望构建一个高性能的生产环境,你会喜欢上使用 Gin。

$ cat test.go
package main

import "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的这个定制版本号来构建。

查看所有測试

Benchmark name (1) (2) (3) (4)
BenchmarkAce_GithubAll 10000 109482 13792 167
BenchmarkBear_GithubAll 10000 287490 79952 943
BenchmarkBeego_GithubAll 3000 562184 146272 2092
BenchmarkBone_GithubAll 500 2578716 648016 8119
BenchmarkDenco_GithubAll 20000 94955 20224 167
BenchmarkEcho_GithubAll 30000 58705 0 0
BenchmarkGin_GithubAll 30000 50991 0 0
BenchmarkGocraftWeb_GithubAll 5000 449648 133280 1889
BenchmarkGoji_GithubAll 2000 689748 56113 334
BenchmarkGoJsonRest_GithubAll 5000 537769 135995 2940
BenchmarkGoRestful_GithubAll 100 18410628 797236 7725
BenchmarkGorillaMux_GithubAll 200 8036360 153137 1791
BenchmarkHttpRouter_GithubAll 20000 63506 13792 167
BenchmarkHttpTreeMux_GithubAll 10000 165927 56112 334
BenchmarkKocha_GithubAll 10000 171362 23304 843
BenchmarkMacaron_GithubAll 2000 817008 224960 2315
BenchmarkMartini_GithubAll 100 12609209 237952 2686
BenchmarkPat_GithubAll 300 4830398 1504101 32222
BenchmarkPossum_GithubAll 10000 301716 97440 812
BenchmarkR2router_GithubAll 10000 270691 77328 1182
BenchmarkRevel_GithubAll 1000 1491919 345553 5918
BenchmarkRivet_GithubAll 10000 283860 84272 1079
BenchmarkTango_GithubAll 5000 473821 87078 2470
BenchmarkTigerTonic_GithubAll 2000 1120131 241088 6052
BenchmarkTraffic_GithubAll 200 8708979 2664762 22390
BenchmarkVulcan_GithubAll 5000 353392 19894 609
BenchmarkZeus_GithubAll 2000 944234 300688 2648

(1): 总反复次数

(2): 单次请求耗时 (ns/op)

(3): 堆内存大小 (B/op)

(4): 单次请求内存分配数 (allocs/op)

Gin v1. stable

  • [x] 零分配路由.
  • [x] 从路由到写请求, 依旧为最快的路由器和框架.
  • [x] 完备的单元測试套件.
  • [x] Battle tested.(?

    )

  • [x] API冻结, 新的release版不会影响现有的代码.

高速開始

  1. 下载并安装Gin:

    $ go get github.com/gin-gonic/gin
  2. 在代码中import进来:

    import "github.com/gin-gonic/gin"
  3. (可选) 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.1
Content-Type: application/x-www-form-urlencoded name=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 JSON
type 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 main

import (
"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 data
var 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框架简单介绍的更多相关文章

  1. Python三大web框架简单介绍

    Django 是重量级框架:它封装的的功能常丰富非常多所以它是重量级,Django的文档最完善.市场占有率最高.招聘职位最多.Django提供全套的解决方案(full-stack framework ...

  2. Django - Django框架 简单介绍

    Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码 ...

  3. Go语言Web框架gwk介绍 3

    Go语言Web框架gwk介绍 (三)   上一篇忘了ChanResult ChanResult 可以用来模拟BigPipe,定义如下 type ChanResult struct { Wait syn ...

  4. Go语言Web框架gwk介绍2

    Go语言Web框架gwk介绍 (二) HttpResult 凡是实现了HttpResult接口的对象,都可以作为gwk返回Web客户端的内容.HttpResult接口定义非常简单,只有一个方法: ty ...

  5. Go语言Web框架gwk介绍 1

    Go语言Web框架gwk介绍 (一)   今天看到Golang排名到前30名了,看来关注的人越来越多了,接下来几天详细介绍Golang一个web开发框架GWK. 现在博客园支持markdown格式发布 ...

  6. Web Services简单介绍

    Web Services简单介绍 Web Services入门 一.Web Services简介 1.什么是Web Services? Web Services 是应用程序组件 Web Service ...

  7. Go语言Web框架gwk介绍4

    Go语言Web框架gwk介绍 (四)   事件 gwk支持事件系统,但并没有硬编码有哪些事件,而是采用了比较松散的定义方式. 订阅事件有两种方式: 调用On函数或者OnFunc函数 func On(m ...

  8. jQuery系列 第一章 jQuery框架简单介绍

    第一章 jQuery框架简单介绍 1.1 jQuery简介 jQuery是一款优秀的javaScript库(框架),该框架凭借简洁的语法和跨平台的兼容性,极大的简化了开发人员对HTML文档,DOM,事 ...

  9. Python -- Scrapy 框架简单介绍(Scrapy 安装及项目创建)

    Python -- Scrapy 框架简单介绍 最近在学习python 爬虫,先后了解学习urllib.urllib2.requests等,后来发现爬虫也有很多框架,而推荐学习最多就是Scrapy框架 ...

随机推荐

  1. SecureCRT导出服务器列表或配置文件

    说明:SecureCRT没有Xshell那么简单有直接导出的功能,但是可以通过技巧的方式来操作. 1.打开SecureCRT,点击菜单栏的[Opitions]->[Global Opitions ...

  2. C# 格式化 中文星期 显示

    最近有些小忙,直接贴代码吧, /// <summary> /// 获取系统的星期 /// </summary> /// <param name="dt" ...

  3. eos智能合约与主进程交互

    eos智能合约与主进程交互 1.启动wasm 参考eos智能合约执行流程.md 2.智能合约调用主进程api 如何实现wasm代码与eos宿主交互还需要摸索! 大致:在wasm_interface.c ...

  4. JS请求报错:Unexpected token T in JSON at position 0

    <?php /* 最近做一个ajax validate表单验证提交的代码,在ajax提交的时候 JS请求报错:Unexpected token T in JSON at position 0 描 ...

  5. nginx phase handler的原理和选择

    nginx phase handler的原理和选择 PHASE HANDLER的种类 nginx在接收并解析完请求行.请求头之后.就会依次调用各个phase handler. phase handle ...

  6. 即将到来的Android N,将具备这些新特性

    原文转自:http://www.leiphone.com/news/201602/pSRQAuAjMFJITqHe.html         原创 訾竣喆 即将到来的Android N,将具备这些新特 ...

  7. 用PROXYCHAINS实现SSH全局代理

    NUX下可以实现SSH全局代理的软件有tsocks和proxychains两种,但是个人感觉proxychains要更加稳定简单. $ yum install proxychains # vim /e ...

  8. apache TIME_WAIT解决办法

    最近发现apache与负载均衡器的的连接数过多,而且大部分都是TIME_WAIT,调整apache2.conf后也没效果,最后百度到如下解决方案 通过调整内核参数解决 vi /etc/sysctl.c ...

  9. 0x…… is not a valid instance ID怎么解决

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. java:可变参数(转载)

    http://12477787.blog.51cto.com/12467787/1887843 Java在1.5之后允许方法使用可变参数,可变参数的好处在于:它允许传递0个或者多个参数.比如原来有一段 ...