Skip to content

Latest commit

History

751 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

#Gin Web Framework Build StatusCoverage StatusGo Report CardGoDocJoin the chat at https://gitter.im/gin-gonic/gin

Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.

Gin console logger

$ cat test.go
package main
import"github.com/gin-gonic/gin"funcmain() {
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
}

Benchmarks

Gin uses a custom version of HttpRouter

See all benchmarks

Benchmark name(1)(2)(3)(4)
BenchmarkAce_GithubAll1000010948213792167
BenchmarkBear_GithubAll1000028749079952943
BenchmarkBeego_GithubAll30005621841462722092
BenchmarkBone_GithubAll50025787166480168119
BenchmarkDenco_GithubAll200009495520224167
BenchmarkEcho_GithubAll300005870500
BenchmarkGin_GithubAll300005099100
BenchmarkGocraftWeb_GithubAll50004496481332801889
BenchmarkGoji_GithubAll200068974856113334
BenchmarkGoJsonRest_GithubAll50005377691359952940
BenchmarkGoRestful_GithubAll100184106287972367725
BenchmarkGorillaMux_GithubAll20080363601531371791
BenchmarkHttpRouter_GithubAll200006350613792167
BenchmarkHttpTreeMux_GithubAll1000016592756112334
BenchmarkKocha_GithubAll1000017136223304843
BenchmarkMacaron_GithubAll20008170082249602315
BenchmarkMartini_GithubAll100126092092379522686
BenchmarkPat_GithubAll3004830398150410132222
BenchmarkPossum_GithubAll1000030171697440812
BenchmarkR2router_GithubAll10000270691773281182
BenchmarkRevel_GithubAll100014919193455535918
BenchmarkRivet_GithubAll10000283860842721079
BenchmarkTango_GithubAll5000473821870782470
BenchmarkTigerTonic_GithubAll200011201312410886052
BenchmarkTraffic_GithubAll2008708979266476222390
BenchmarkVulcan_GithubAll500035339219894609
BenchmarkZeus_GithubAll20009442343006882648

(1): Total Repetitions
(2): Single Repetition Duration (ns/op)
(3): Heap Memory (B/op)
(4): Average Allocations per Repetition (allocs/op)

Gin v1. stable

  • Zero allocation router.
  • Still the fastest http router and framework. From routing to writing.
  • Complete suite of unit tests
  • Battle tested
  • API frozen, new releases will not break your code.

Start using it

  1. Download and install it:

    $ go get gopkg.in/gin-gonic/gin.v1
  2. Import it in your code:

    import"gopkg.in/gin-gonic/gin.v1"
  3. (Optional) Import net/http. This is required for example if using constants such as http.StatusOK.

    import"net/http"

API Examples

Using GET, POST, PUT, PATCH, DELETE and OPTIONS

funcmain() {
// Creates a gin router with default middleware:// logger and recovery (crash-free) middlewarerouter:=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
}

Parameters in path

funcmain() {
router:=gin.Default()
// This handler will match /user/john but will not match neither /user/ or /userrouter.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 "+actionc.String(http.StatusOK, message)
})
router.Run(":8080")
}

Querystring parameters

funcmain() {
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=Doerouter.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 Form

funcmain() {
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")
}

Another example: query + post form

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great
funcmain() {
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

Another example: upload file

References issue #548.

funcmain() {
router:=gin.Default()
router.POST("/upload", func(c*gin.Context) {
file, header , err:=c.Request.FormFile("upload")
filename:=header.Filenamefmt.Println(header.Filename)
out, err:=os.Create("./tmp/"+filename+".png")
iferr!=nil {
log.Fatal(err)
}
deferout.Close()
_, err=io.Copy(out, file)
iferr!=nil {
log.Fatal(err)
} })
router.Run(":8080")
}

Grouping routes

funcmain() {
router:=gin.Default()
// Simple group: v1v1:=router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2v2:=router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}

Blank Gin without middleware by default

Use

r:=gin.New()

instead of

r:=gin.Default()

Using middleware

funcmain() {
// Creates a router without any middleware by defaultr:=gin.New()
// Global middlewarer.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 grouptesting:=authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// Listen and server on 0.0.0.0:8080r.Run(":8080")
}

Model binding and validation

To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set json:"fieldname".

When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use BindWith.

You can also specify that specific fields are required. If a field is decorated with binding:"required" and has a empty value when binding, the current request will fail with an error.

// Binding from JSONtypeLoginstruct {
Userstring`form:"user" json:"user" binding:"required"`Passwordstring`form:"password" json:"password" binding:"required"`
}
funcmain() {
router:=gin.Default()
// Example for binding JSON ({"user": "manu", "password": "123"})router.POST("/loginJSON", func(c*gin.Context) {
varjsonLoginifc.BindJSON(&json) ==nil {
ifjson.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) {
varformLogin// This will infer what binder to use depending on the content-type header.ifc.Bind(&form) ==nil {
ifform.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:8080router.Run(":8080")
}

###Multipart/Urlencoded binding

package main
import (
"github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding"
)
typeLoginFormstruct {
Userstring`form:"user" binding:"required"`Passwordstring`form:"password" binding:"required"`
}
funcmain() {
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:varformLoginForm// in this case proper binding will be automatically selectedifc.Bind(&form) ==nil {
ifform.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")
}

Test it with:

$ curl -v --form user=user --form password=password http://localhost:8080/login

XML, JSON and YAML rendering

funcmain() {
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 structvarmsgstruct {
Namestring`json:"user"`MessagestringNumberint
}
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})
})
r.GET("/someYAML", func(c*gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
// Listen and server on 0.0.0.0:8080r.Run(":8080")
}

####Serving static files

funcmain() {
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:8080router.Run(":8080")
}

####HTML rendering

Using LoadHTMLTemplates()

funcmain() {
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>

Using templates with same name in different directories

funcmain() {
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 }}

You can also use your own html template render

import"html/template"funcmain() {
router:=gin.Default()
html:=template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}

Redirects

Issuing a HTTP redirect is easy:

r.GET("/test", func(c*gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})

Both internal and external locations are supported.

Custom Middleware

funcLogger() gin.HandlerFunc {
returnfunc(c*gin.Context) {
t:=time.Now()
// Set example variablec.Set("example", "12345")
// before requestc.Next()
// after requestlatency:=time.Since(t)
log.Print(latency)
// access the status we are sendingstatus:=c.Writer.Status()
log.Println(status)
}
}
funcmain() {
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:8080r.Run(":8080")
}

Using BasicAuth() middleware

// simulate some private datavarsecrets= 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"},
}
funcmain() {
r:=gin.Default()
// Group using gin.BasicAuth() middleware// gin.Accounts is a shortcut for map[string]stringauthorized:=r.Group("/admin", gin.BasicAuth(gin.Accounts{
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
}))
// /admin/secrets endpoint// hit "localhost:8080/admin/secretsauthorized.GET("/secrets", func(c*gin.Context) {
// get user, it was set by the BasicAuth middlewareuser:=c.MustGet(gin.AuthUserKey).(string)
ifsecret, 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:8080r.Run(":8080")
}

Goroutines inside a middleware

When starting inside a middleware or handler, you SHOULD NOT use the original context inside it, you have to use a read-only copy.

funcmain() {
r:=gin.Default()
r.GET("/long_async", func(c*gin.Context) {
// create copy to be used inside the goroutinecCp:=c.Copy()
gofunc() {
// simulate a long task with time.Sleep(). 5 secondstime.Sleep(5*time.Second)
// note that you are using the copied context "cCp", IMPORTANTlog.Println("Done! in path "+cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c*gin.Context) {
// simulate a long task with time.Sleep(). 5 secondstime.Sleep(5*time.Second)
// since we are NOT using a goroutine, we do not have to copy the contextlog.Println("Done! in path "+c.Request.URL.Path)
})
// Listen and server on 0.0.0.0:8080r.Run(":8080")
}

Custom HTTP configuration

Use http.ListenAndServe() directly, like this:

funcmain() {
router:=gin.Default()
http.ListenAndServe(":8080", router)
}

or

funcmain() {
router:=gin.Default()
s:=&http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10*time.Second,
WriteTimeout: 10*time.Second,
MaxHeaderBytes: 1<<20,
}
s.ListenAndServe()
}

Graceful restart or stop

Do you want to graceful restart or stop your web server? There are some ways this can be done.

We can use fvbock/endless to replace the default ListenAndServe. Refer issue #296 for more details.

router:=gin.Default()
router.GET("/", handler)
// [...]endless.ListenAndServe(":4242", router)

An alternative to endless:

  • manners: A polite Go HTTP server that shuts down gracefully.

Contributing

  • With issues:
    • Use the search tool before opening a new issue.
    • Please provide source code and commit sha if you found a bug.
    • Review existing issues and provide feedback or react to them.
  • With pull requests:
    • Open your pull request against develop
    • Your pull request should have no more than two commits, if not you should squash them.
    • It should pass all tests in the available continuous integrations systems such as TravisCI.
    • You should add/modify tests to cover your proposed code changes.
    • If your pull request contains a new feature, please document it on the README.

Example

Awesome project lists using Gin web framework.

  • drone: Drone is a Continuous Delivery platform built on Docker, written in Go
  • gorush: A push notification server written in Go.

About

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages