README ¶
Gin Web Framework
Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.
Contents
- Gin Web Framework
- Contents
- Installation
- Quick start
- Benchmarks
- Gin v1. stable
- Build with jsoniter
- API Examples
- Using GET, POST, PUT, PATCH, DELETE and OPTIONS
- Parameters in path
- Querystring parameters
- Multipart/Urlencoded Form
- Another example: query + post form
- Map as querystring or postform parameters
- Upload files
- Grouping routes
- Blank Gin without middleware by default
- Using middleware
- How to write log file
- Custom Log Format
- Controlling Log output coloring
- Model binding and validation
- Custom Validators
- Only Bind Query String
- Bind Query String or Post Data
- Bind Uri
- Bind Header
- Bind HTML checkboxes
- Multipart/Urlencoded binding
- XML, JSON, YAML and ProtoBuf rendering
- Serving static files
- Serving data from file
- Serving data from reader
- HTML rendering
- Multitemplate
- Redirects
- Custom Middleware
- Using BasicAuth() middleware
- Goroutines inside a middleware
- Custom HTTP configuration
- Support Let's Encrypt
- Run multiple service using Gin
- Graceful shutdown or restart
- Build a single binary with templates
- Bind form-data request with custom struct
- Try to bind body into different structs
- http2 server push
- Define format for the log of routes
- Set and get a cookie
- Testing
- Users
Installation
To install Gin package, you need to install Go and set your Go workspace first.
- The first need Go installed (version 1.12+ is required), then you can use the below Go command to install Gin.
$ go get -u github.com/nujikazo/gin
- Import it in your code:
import "github.com/nujikazo/gin"
- (Optional) Import
net/http
. This is required for example if using constants such ashttp.StatusOK
.
import "net/http"
Quick start
# assume the following codes in example.go file
$ cat example.go
package main
import "github.com/nujikazo/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
# run example.go and visit 0.0.0.0:8080/ping (for windows "localhost:8080/ping") on browser
$ go run example.go
Benchmarks
Gin uses a custom version of HttpRouter
Benchmark name | (1) | (2) | (3) | (4) |
---|---|---|---|---|
BenchmarkGin_GithubAll | 43550 | 27364 ns/op | 0 B/op | 0 allocs/op |
BenchmarkAce_GithubAll | 40543 | 29670 ns/op | 0 B/op | 0 allocs/op |
BenchmarkAero_GithubAll | 57632 | 20648 ns/op | 0 B/op | 0 allocs/op |
BenchmarkBear_GithubAll | 9234 | 216179 ns/op | 86448 B/op | 943 allocs/op |
BenchmarkBeego_GithubAll | 7407 | 243496 ns/op | 71456 B/op | 609 allocs/op |
BenchmarkBone_GithubAll | 420 | 2922835 ns/op | 720160 B/op | 8620 allocs/op |
BenchmarkChi_GithubAll | 7620 | 238331 ns/op | 87696 B/op | 609 allocs/op |
BenchmarkDenco_GithubAll | 18355 | 64494 ns/op | 20224 B/op | 167 allocs/op |
BenchmarkEcho_GithubAll | 31251 | 38479 ns/op | 0 B/op | 0 allocs/op |
BenchmarkGocraftWeb_GithubAll | 4117 | 300062 ns/op | 131656 B/op | 1686 allocs/op |
BenchmarkGoji_GithubAll | 3274 | 416158 ns/op | 56112 B/op | 334 allocs/op |
BenchmarkGojiv2_GithubAll | 1402 | 870518 ns/op | 352720 B/op | 4321 allocs/op |
BenchmarkGoJsonRest_GithubAll | 2976 | 401507 ns/op | 134371 B/op | 2737 allocs/op |
BenchmarkGoRestful_GithubAll | 410 | 2913158 ns/op | 910144 B/op | 2938 allocs/op |
BenchmarkGorillaMux_GithubAll | 346 | 3384987 ns/op | 251650 B/op | 1994 allocs/op |
BenchmarkGowwwRouter_GithubAll | 10000 | 143025 ns/op | 72144 B/op | 501 allocs/op |
BenchmarkHttpRouter_GithubAll | 55938 | 21360 ns/op | 0 B/op | 0 allocs/op |
BenchmarkHttpTreeMux_GithubAll | 10000 | 153944 ns/op | 65856 B/op | 671 allocs/op |
BenchmarkKocha_GithubAll | 10000 | 106315 ns/op | 23304 B/op | 843 allocs/op |
BenchmarkLARS_GithubAll | 47779 | 25084 ns/op | 0 B/op | 0 allocs/op |
BenchmarkMacaron_GithubAll | 3266 | 371907 ns/op | 149409 B/op | 1624 allocs/op |
BenchmarkMartini_GithubAll | 331 | 3444706 ns/op | 226551 B/op | 2325 allocs/op |
BenchmarkPat_GithubAll | 273 | 4381818 ns/op | 1483152 B/op | 26963 allocs/op |
BenchmarkPossum_GithubAll | 10000 | 164367 ns/op | 84448 B/op | 609 allocs/op |
BenchmarkR2router_GithubAll | 10000 | 160220 ns/op | 77328 B/op | 979 allocs/op |
BenchmarkRivet_GithubAll | 14625 | 82453 ns/op | 16272 B/op | 167 allocs/op |
BenchmarkTango_GithubAll | 6255 | 279611 ns/op | 63826 B/op | 1618 allocs/op |
BenchmarkTigerTonic_GithubAll | 2008 | 687874 ns/op | 193856 B/op | 4474 allocs/op |
BenchmarkTraffic_GithubAll | 355 | 3478508 ns/op | 820744 B/op | 14114 allocs/op |
BenchmarkVulcan_GithubAll | 6885 | 193333 ns/op | 19894 B/op | 609 allocs/op |
- (1): Total Repetitions achieved in constant time, higher means more confident result
- (2): Single Repetition Duration (ns/op), lower is better
- (3): Heap Memory (B/op), lower is better
- (4): Average Allocations per Repetition (allocs/op), lower is better
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.
Build with jsoniter
Gin uses encoding/json
as default json package but you can change to jsoniter by build from other tags.
$ go build -tags=jsoniter .
API Examples
You can find a number of ready-to-run examples at Gin examples repository.
Using GET, POST, PUT, PATCH, DELETE and 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
}
Parameters in path
func main() {
router := gin.Default()
// This handler will match /user/john but will not match /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)
})
// For each matched request Context will hold the route definition
router.POST("/user/:name/*action", func(c *gin.Context) {
c.FullPath() == "/user/:name/*action" // true
})
router.Run(":8080")
}
Querystring parameters
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 Form
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")
}
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
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
Map as querystring or postform parameters
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded
names[first]=thinkerou&names[second]=tianou
func main() {
router := gin.Default()
router.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
fmt.Printf("ids: %v; names: %v", ids, names)
})
router.Run(":8080")
}
ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou]
Upload files
Single file
References issue #774 and detail example code.
file.Filename
SHOULD NOT be trusted. See Content-Disposition
on MDN and #1693
The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done.
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
How to curl
:
curl -X POST http://localhost:8080/upload \
-F "file=@/Users/appleboy/test.zip" \
-H "Content-Type: multipart/form-data"
Multiple files
See the detail example code.
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"]
for _, file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}
How to curl
:
curl -X POST http://localhost:8080/upload \
-F "upload[]=@/Users/appleboy/test1.zip" \
-F "upload[]=@/Users/appleboy/test2.zip" \
-H "Content-Type: multipart/form-data"
Grouping routes
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")
}
Blank Gin without middleware by default
Use
r := gin.New()
instead of
// Default With the Logger and Recovery middleware already attached
r := gin.Default()
Using middleware
func main() {
// Creates a router without any middleware by default
r := gin.New()
// Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one.
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 serve on 0.0.0.0:8080
r.Run(":8080")
}
Custom Recovery behavior
func main() {
// Creates a router without any middleware by default
r := gin.New()
// Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one.
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(string); ok {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
c.AbortWithStatus(http.StatusInternalServerError)
}))
r.GET("/panic", func(c *gin.Context) {
// panic with a string -- the custom middleware could save this to a database or report it to the user
panic("foo")
})
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "ohai")
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
How to write log file
func main() {
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// Use the following code if you need to write the logs to file and console at the same time.
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
Custom Log Format
func main() {
router := gin.New()
// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// your custom format
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
Sample Output
::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
Controlling Log output coloring
By default, logs output on console should be colorized depending on the detected TTY.
Never colorize logs:
func main() {
// Disable log's color
gin.DisableConsoleColor()
// Creates a gin router with default middleware:
// logger and recovery (crash-free) middleware
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
Always colorize logs:
func main() {
// Force log's color
gin.ForceConsoleColor()
// Creates a gin router with default middleware:
// logger and recovery (crash-free) middleware
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
Model binding and validation
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
Gin uses go-playground/validator/v10 for validation. Check the full docs on tags usage here.
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"
.
Also, Gin provides two sets of methods for binding:
- Type - Must bind
- Methods -
Bind
,BindJSON
,BindXML
,BindQuery
,BindYAML
,BindHeader
- Behavior - These methods use
MustBindWith
under the hood. If there is a binding error, the request is aborted withc.AbortWithError(400, err).SetType(ErrorTypeBind)
. This sets the response status code to 400 and theContent-Type
header is set totext/plain; charset=utf-8
. Note that if you try to set the response code after this, it will result in a warning[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422
. If you wish to have greater control over the behavior, consider using theShouldBind
equivalent method.
- Methods -
- Type - Should bind
- Methods -
ShouldBind
,ShouldBindJSON
,ShouldBindXML
,ShouldBindQuery
,ShouldBindYAML
,ShouldBindHeader
- Behavior - These methods use
ShouldBindWith
under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
- Methods -
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 MustBindWith
or ShouldBindWith
.
You can also specify that specific fields are required. If a field is decorated with binding:"required"
and has a empty value when binding, an error will be returned.
// Binding from JSON
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"})
})
// Example for binding XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <user>user</user>
// <password>123</password>
// </root>)
router.POST("/loginXML", func(c *gin.Context) {
var xml Login
if err := c.ShouldBindXML(&xml); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if xml.User != "manu" || xml.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// 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 err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if form.User != "manu" || form.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")
}
Sample request
$ curl -v -X POST \
http://localhost:8080/loginJSON \
-H 'content-type: application/json' \
-d '{ "user": "manu" }'
> POST /loginJSON HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
> content-type: application/json
> Content-Length: 18
>
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Fri, 04 Aug 2017 03:51:31 GMT
< Content-Length: 100
<
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
Skip validate
When running the above example using the above the curl
command, it returns error. Because the example use binding:"required"
for Password
. If use binding:"-"
for Password
, then it will not return error when running the above example again.
Custom Validators
It is also possible to register custom validators. See the example code.
package main
import (
"net/http"
"time"
"github.com/nujikazo/gin"
"github.com/nujikazo/gin/binding"
"github.com/go-playground/validator/v10"
)
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}
func main() {
route := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17"
{"message":"Booking dates are valid!"}
$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09"
{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}
$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10"
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}%
Struct level validations can also be registered this way. See the struct-lvl-validation example to learn more.
Only Bind Query String
ShouldBindQuery
function only binds the query params and not the post data. See the detail information.
package main
import (
"log"
"github.com/nujikazo/gin"
)
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
}
func main() {
route := gin.Default()
route.Any("/testing", startPage)
route.Run(":8085")
}
func startPage(c *gin.Context) {
var person Person
if c.ShouldBindQuery(&person) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
}
c.String(200, "Success")
}
Bind Query String or Post Data
See the detail information.
package main
import (
"log"
"time"
"github.com/nujikazo/gin"
)
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
UnixTime time.Time `form:"unixTime" time_format:"unix"`
}
func main() {
route := gin.Default()
route.GET("/testing", startPage)
route.Run(":8085")
}
func startPage(c *gin.Context) {
var person Person
// If `GET`, only `Form` binding engine (`query`) used.
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
// See more at https://github.com/nujikazo/gin/blob/master/binding/binding.go#L48
if c.ShouldBind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
log.Println(person.Birthday)
log.Println(person.CreateTime)
log.Println(person.UnixTime)
}
c.String(200, "Success")
}
Test it with:
$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
Bind Uri
See the detail information.
package main
import "github.com/nujikazo/gin"
type Person struct {
ID string `uri:"id" binding:"required,uuid"`
Name string `uri:"name" binding:"required"`
}
func main() {
route := gin.Default()
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run(":8088")
}
Test it with:
$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
$ curl -v localhost:8088/thinkerou/not-uuid
Bind Header
package main
import (
"fmt"
"github.com/nujikazo/gin"
)
type testHeader struct {
Rate int `header:"Rate"`
Domain string `header:"Domain"`
}
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
h := testHeader{}
if err := c.ShouldBindHeader(&h); err != nil {
c.JSON(200, err)
}
fmt.Printf("%#v\n", h)
c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
})
r.Run()
// client
// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/
// output
// {"Domain":"music","Rate":300}
}
Bind HTML checkboxes
See the detail information
main.go
...
type myForm struct {
Colors []string `form:"colors[]"`
}
...
func formHandler(c *gin.Context) {
var fakeForm myForm
c.ShouldBind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
}
...
form.html
<form action="/" method="POST">
<p>Check some colors</p>
<label for="red">Red</label>
<input type="checkbox" name="colors[]" value="red" id="red">
<label for="green">Green</label>
<input type="checkbox" name="colors[]" value="green" id="green">
<label for="blue">Blue</label>
<input type="checkbox" name="colors[]" value="blue" id="blue">
<input type="submit">
</form>
result:
{"color":["red","green","blue"]}
Multipart/Urlencoded binding
type ProfileForm struct {
Name string `form:"name" binding:"required"`
Avatar *multipart.FileHeader `form:"avatar" binding:"required"`
// or for multiple files
// Avatars []*multipart.FileHeader `form:"avatar" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/profile", func(c *gin.Context) {
// you can bind multipart form with explicit binding declaration:
// c.ShouldBindWith(&form, binding.Form)
// or you can simply use autobinding with ShouldBind method:
var form ProfileForm
// in this case proper binding will be automatically selected
if err := c.ShouldBind(&form); err != nil {
c.String(http.StatusBadRequest, "bad request")
return
}
err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename)
if err != nil {
c.String(http.StatusInternalServerError, "unknown error")
return
}
// db.Save(&form)
c.String(http.StatusOK, "ok")
})
router.Run(":8080")
}
Test it with:
$ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile
XML, JSON, YAML and ProtoBuf rendering
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})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// The specific definition of protobuf is written in the testdata/protoexample file.
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// Note that data becomes binary data in the response
// Will output protoexample.Test protobuf serialized data
c.ProtoBuf(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
SecureJSON
Using SecureJSON to prevent json hijacking. Default prepends "while(1),"
to response body if the given struct is array values.
func main() {
r := gin.Default()
// You can also use your own secure json prefix
// r.SecureJsonPrefix(")]}',\n")
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"lena", "austin", "foo"}
// Will output : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK, names)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
JSONP
Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
func main() {
r := gin.Default()
r.GET("/JSONP", func(c *gin.Context) {
data := gin.H{
"foo": "bar",
}
//callback is x
// Will output : x({\"foo\":\"bar\"})
c.JSONP(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
// client
// curl http://127.0.0.1:8080/JSONP?callback=x
}
AsciiJSON
Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII characters.
func main() {
r := gin.Default()
r.GET("/someJSON", func(c *gin.Context) {
data := gin.H{
"lang": "GO语言",
"tag": "<br>",
}
// will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
c.AsciiJSON(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
PureJSON
Normally, JSON replaces special HTML characters with their unicode entities, e.g. <
becomes \u003c
. If you want to encode such characters literally, you can use PureJSON instead.
This feature is unavailable in Go 1.6 and lower.
func main() {
r := gin.Default()
// Serves unicode entities
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// Serves literal characters
r.GET("/purejson", func(c *gin.Context) {
c.PureJSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
Serving static files
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 serve on 0.0.0.0:8080
router.Run(":8080")
}
Serving data from file
func main() {
router := gin.Default()
router.GET("/local/file", func(c *gin.Context) {
c.File("local/file.go")
})
var fs http.FileSystem = // ...
router.GET("/fs/file", func(c *gin.Context) {
c.FileFromFS("fs/file.go", fs)
})
}
Serving data from reader
func main() {
router := gin.Default()
router.GET("/someDataFromReader", func(c *gin.Context) {
response, err := http.Get("https://raw.githubusercontent.com/nujikazo/logo/master/color.png")
if err != nil || response.StatusCode != http.StatusOK {
c.Status(http.StatusServiceUnavailable)
return
}
reader := response.Body
defer reader.Close()
contentLength := response.ContentLength
contentType := response.Header.Get("Content-Type")
extraHeaders := map[string]string{
"Content-Disposition": `attachment; filename="gopher.png"`,
}
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
})
router.Run(":8080")
}
HTML rendering
Using LoadHTMLGlob() or LoadHTMLFiles()
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>
Using templates with same name in different directories
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 }}
Custom Template renderer
You can also use your own html template render
import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}
Custom Delimiters
You may use custom delims
r := gin.Default()
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates")
Custom Template Funcs
See the detail example code.
main.go
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/nujikazo/gin"
)
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d%02d/%02d", year, month, day)
}
func main() {
router := gin.Default()
router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLFiles("./testdata/template/raw.tmpl")
router.GET("/raw", func(c *gin.Context) {
c.HTML(http.StatusOK, "raw.tmpl", gin.H{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8080")
}
raw.tmpl
Date: {[{.now | formatAsDate}]}
Result:
Date: 2017/07/01
Multitemplate
Gin allow by default use only one html.Template. Check a multitemplate render for using features like go 1.6 block template
.
Redirects
Issuing a HTTP redirect is easy. Both internal and external locations are supported.
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
Issuing a HTTP redirect from POST. Refer to issue: #444
r.POST("/test", func(c *gin.Context) {
c.Redirect(http.StatusFound, "/foo")
})
Issuing a Router redirect, use HandleContext
like below.
r.GET("/test", func(c *gin.Context) {
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
})
Custom Middleware
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")
}
Using BasicAuth() middleware
// 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 serve on 0.0.0.0:8080
r.Run(":8080")
}
Goroutines inside a middleware
When starting new Goroutines inside a middleware or handler, you SHOULD NOT use the original context inside it, you have to use a read-only 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 serve on 0.0.0.0:8080
r.Run(":8080")
}
Custom HTTP configuration
Use http.ListenAndServe()
directly, like this:
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()
}
Support Let's Encrypt
example for 1-line LetsEncrypt HTTPS servers.
package main
import (
"log"
"github.com/nujikazo/autotls"
"github.com/nujikazo/gin"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
}
example for custom autocert manager.
package main
import (
"log"
"github.com/nujikazo/autotls"
"github.com/nujikazo/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
Cache: autocert.DirCache("/var/www/.cache"),
}
log.Fatal(autotls.RunWithManager(r, &m))
}
Run multiple service using Gin
See the question and try the following example:
package main
import (
"log"
"net/http"
"time"
"github.com/nujikazo/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)
}
}
Graceful shutdown or restart
There are a few approaches you can use to perform a graceful shutdown or restart. You can make use of third-party packages specifically built for that, or you can manually do the same with the functions and methods from the built-in packages.
Third-party packages
We can use fvbock/endless to replace the default ListenAndServe
. Refer to issue #296 for more details.
router := gin.Default()
router.GET("/", handler)
// [...]
endless.ListenAndServe(":4242", router)
Alternatives:
- manners: A polite Go HTTP server that shuts down gracefully.
- graceful: Graceful is a Go package enabling graceful shutdown of an http.Handler server.
- grace: Graceful restart & zero downtime deploy for Go servers.
Manually
In case you are using Go 1.8 or a later version, you may not need to use those libraries. Consider using http.Server
's built-in Shutdown() method for graceful shutdowns. The example below describes its usage, and we've got more examples using gin here.
// +build go1.8
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/nujikazo/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,
}
// Initializing the server in a goroutine so that
// it won't block the graceful shutdown handling below
go func() {
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)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// The context is used to inform the server it has 5 seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}
Build a single binary with templates
You can build a server into a single binary containing templates by using 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-builder
func loadTemplate() (*template.Template, error) {
t := template.New("")
for name, file := range Assets.Files {
defer file.Close()
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
}
See a complete example in the https://github.com/nujikazo/examples/tree/master/assets-in-binary
directory.
Bind form-data request with custom struct
The follow example using custom struct:
type StructA struct {
FieldA string `form:"field_a"`
}
type StructB struct {
NestedStruct StructA
FieldB string `form:"field_b"`
}
type StructC struct {
NestedStructPointer *StructA
FieldC string `form:"field_c"`
}
type StructD struct {
NestedAnonyStruct struct {
FieldX string `form:"field_x"`
}
FieldD string `form:"field_d"`
}
func GetDataB(c *gin.Context) {
var b StructB
c.Bind(&b)
c.JSON(200, gin.H{
"a": b.NestedStruct,
"b": b.FieldB,
})
}
func GetDataC(c *gin.Context) {
var b StructC
c.Bind(&b)
c.JSON(200, gin.H{
"a": b.NestedStructPointer,
"c": b.FieldC,
})
}
func GetDataD(c *gin.Context) {
var b StructD
c.Bind(&b)
c.JSON(200, gin.H{
"x": b.NestedAnonyStruct,
"d": b.FieldD,
})
}
func main() {
r := gin.Default()
r.GET("/getb", GetDataB)
r.GET("/getc", GetDataC)
r.GET("/getd", GetDataD)
r.Run()
}
Using the command curl
command result:
$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":{"FieldA":"hello"},"b":"world"}
$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
{"a":{"FieldA":"hello"},"c":"world"}
$ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
{"d":"world","x":{"FieldX":"hello"}}
Try to bind body into different structs
The normal methods for binding request body consumes c.Request.Body
and they
cannot be called multiple times.
type formA struct {
Foo string `json:"foo" xml:"foo" binding:"required"`
}
type formB struct {
Bar string `json:"bar" xml:"bar" binding:"required"`
}
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
// This c.ShouldBind consumes c.Request.Body and it cannot be reused.
if errA := c.ShouldBind(&objA); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
// Always an error is occurred by this because c.Request.Body is EOF now.
} else if errB := c.ShouldBind(&objB); errB == nil {
c.String(http.StatusOK, `the body should be formB`)
} else {
...
}
}
For this, you can use c.ShouldBindBodyWith
.
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
// This reads c.Request.Body and stores the result into the context.
if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
// At this time, it reuses body stored in the context.
} else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
c.String(http.StatusOK, `the body should be formB JSON`)
// And it can accepts other formats
} else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
c.String(http.StatusOK, `the body should be formB XML`)
} else {
...
}
}
c.ShouldBindBodyWith
stores body into the context before binding. This has a slight impact to performance, so you should not use this method if you are enough to call binding at once.- This feature is only needed for some formats --
JSON
,XML
,MsgPack
,ProtoBuf
. For other formats,Query
,Form
,FormPost
,FormMultipart
, can be called byc.ShouldBind()
multiple times without any damage to performance (See #1341).
http2 server push
http.Pusher is supported only go1.8+. See the golang blog for detail information.
package main
import (
"html/template"
"log"
"github.com/nujikazo/gin"
)
var html = template.Must(template.New("https").Parse(`
<html>
<head>
<title>Https Test</title>
<script src="/assets/app.js"></script>
</head>
<body>
<h1 style="color:red;">Welcome, Ginner!</h1>
</body>
</html>
`))
func main() {
r := gin.Default()
r.Static("/assets", "./assets")
r.SetHTMLTemplate(html)
r.GET("/", func(c *gin.Context) {
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push
if err := pusher.Push("/assets/app.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
}
}
c.HTML(200, "https", gin.H{
"status": "success",
})
})
// Listen and Server in https://127.0.0.1:8080
r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
}
Define format for the log of routes
The default log of routes is:
[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)
If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with gin.DebugPrintRouteFunc
.
In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs.
import (
"log"
"net/http"
"github.com/nujikazo/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()
}
Set and get a cookie
import (
"fmt"
"github.com/nujikazo/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()
}
Testing
The net/http/httptest
package is preferable way for HTTP testing.
package main
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
return r
}
func main() {
r := setupRouter()
r.Run(":8080")
}
Test for code example above:
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
Users
Awesome project lists using Gin web framework.
- gorush: A push notification server written in Go.
- fnproject: The container native, cloud agnostic serverless platform.
- photoprism: Personal photo management powered by Go and Google TensorFlow.
- krakend: Ultra performant API Gateway with middlewares.
- picfit: An image resizing server written in Go.
- brigade: Event-based Scripting for Kubernetes.
- dkron: Distributed, fault tolerant job scheduling system.
Documentation ¶
Overview ¶
Package gin implements a HTTP web framework called gin.
See https://nujikazo.com/ for more information about gin.
Index ¶
- Constants
- Variables
- func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine)
- func Dir(root string, listDirectory bool) http.FileSystem
- func DisableBindValidation()
- func DisableConsoleColor()
- func EnableJsonDecoderDisallowUnknownFields()
- func EnableJsonDecoderUseNumber()
- func ForceConsoleColor()
- func IsDebugging() bool
- func Mode() string
- func SetMode(value string)
- type Accounts
- type Context
- func (c *Context) Abort()
- func (c *Context) AbortWithError(code int, err error) *Error
- func (c *Context) AbortWithStatus(code int)
- func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
- func (c *Context) AsciiJSON(code int, obj interface{})
- func (c *Context) Bind(obj interface{}) error
- func (c *Context) BindHeader(obj interface{}) error
- func (c *Context) BindJSON(obj interface{}) error
- func (c *Context) BindQuery(obj interface{}) error
- func (c *Context) BindUri(obj interface{}) error
- func (c *Context) BindWith(obj interface{}, b binding.Binding) error
- func (c *Context) BindXML(obj interface{}) error
- func (c *Context) BindYAML(obj interface{}) error
- func (c *Context) ClientIP() string
- func (c *Context) ContentType() string
- func (c *Context) Cookie(name string) (string, error)
- func (c *Context) Copy() *Context
- func (c *Context) Data(code int, contentType string, data []byte)
- func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, ...)
- func (c *Context) Deadline() (deadline time.Time, ok bool)
- func (c *Context) DefaultPostForm(key, defaultValue string) string
- func (c *Context) DefaultQuery(key, defaultValue string) string
- func (c *Context) Done() <-chan struct{}
- func (c *Context) Err() error
- func (c *Context) Error(err error) *Error
- func (c *Context) File(filepath string)
- func (c *Context) FileAttachment(filepath, filename string)
- func (c *Context) FileFromFS(filepath string, fs http.FileSystem)
- func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
- func (c *Context) FullPath() string
- func (c *Context) Get(key string) (value interface{}, exists bool)
- func (c *Context) GetBool(key string) (b bool)
- func (c *Context) GetDuration(key string) (d time.Duration)
- func (c *Context) GetFloat64(key string) (f64 float64)
- func (c *Context) GetHeader(key string) string
- func (c *Context) GetInt(key string) (i int)
- func (c *Context) GetInt64(key string) (i64 int64)
- func (c *Context) GetPostForm(key string) (string, bool)
- func (c *Context) GetPostFormArray(key string) ([]string, bool)
- func (c *Context) GetPostFormMap(key string) (map[string]string, bool)
- func (c *Context) GetQuery(key string) (string, bool)
- func (c *Context) GetQueryArray(key string) ([]string, bool)
- func (c *Context) GetQueryMap(key string) (map[string]string, bool)
- func (c *Context) GetRawData() ([]byte, error)
- func (c *Context) GetString(key string) (s string)
- func (c *Context) GetStringMap(key string) (sm map[string]interface{})
- func (c *Context) GetStringMapString(key string) (sms map[string]string)
- func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
- func (c *Context) GetStringSlice(key string) (ss []string)
- func (c *Context) GetTime(key string) (t time.Time)
- func (c *Context) GetUint(key string) (ui uint)
- func (c *Context) GetUint64(key string) (ui64 uint64)
- func (c *Context) HTML(code int, name string, obj interface{})
- func (c *Context) Handler() HandlerFunc
- func (c *Context) HandlerName() string
- func (c *Context) HandlerNames() []string
- func (c *Context) Header(key, value string)
- func (c *Context) IndentedJSON(code int, obj interface{})
- func (c *Context) IsAborted() bool
- func (c *Context) IsWebsocket() bool
- func (c *Context) JSON(code int, obj interface{})
- func (c *Context) JSONP(code int, obj interface{})
- func (c *Context) MultipartForm() (*multipart.Form, error)
- func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error
- func (c *Context) MustGet(key string) interface{}
- func (c *Context) Negotiate(code int, config Negotiate)
- func (c *Context) NegotiateFormat(offered ...string) string
- func (c *Context) Next()
- func (c *Context) Param(key string) string
- func (c *Context) PostForm(key string) string
- func (c *Context) PostFormArray(key string) []string
- func (c *Context) PostFormMap(key string) map[string]string
- func (c *Context) ProtoBuf(code int, obj interface{})
- func (c *Context) PureJSON(code int, obj interface{})
- func (c *Context) Query(key string) string
- func (c *Context) QueryArray(key string) []string
- func (c *Context) QueryMap(key string) map[string]string
- func (c *Context) Redirect(code int, location string)
- func (c *Context) Render(code int, r render.Render)
- func (c *Context) SSEvent(name string, message interface{})
- func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
- func (c *Context) SecureJSON(code int, obj interface{})
- func (c *Context) Set(key string, value interface{})
- func (c *Context) SetAccepted(formats ...string)
- func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
- func (c *Context) SetSameSite(samesite http.SameSite)
- func (c *Context) ShouldBind(obj interface{}) error
- func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error)
- func (c *Context) ShouldBindHeader(obj interface{}) error
- func (c *Context) ShouldBindJSON(obj interface{}) error
- func (c *Context) ShouldBindQuery(obj interface{}) error
- func (c *Context) ShouldBindUri(obj interface{}) error
- func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error
- func (c *Context) ShouldBindXML(obj interface{}) error
- func (c *Context) ShouldBindYAML(obj interface{}) error
- func (c *Context) Status(code int)
- func (c *Context) Stream(step func(w io.Writer) bool) bool
- func (c *Context) String(code int, format string, values ...interface{})
- func (c *Context) Value(key interface{}) interface{}
- func (c *Context) XML(code int, obj interface{})
- func (c *Context) YAML(code int, obj interface{})
- type Engine
- func (engine *Engine) Delims(left, right string) *Engine
- func (engine *Engine) HandleContext(c *Context)
- func (engine *Engine) LoadHTMLFiles(files ...string)
- func (engine *Engine) LoadHTMLGlob(pattern string)
- func (engine *Engine) NoMethod(handlers ...HandlerFunc)
- func (engine *Engine) NoRoute(handlers ...HandlerFunc)
- func (engine *Engine) Routes() (routes RoutesInfo)
- func (engine *Engine) Run(addr ...string) (err error)
- func (engine *Engine) RunFd(fd int) (err error)
- func (engine *Engine) RunListener(listener net.Listener) (err error)
- func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error)
- func (engine *Engine) RunUnix(file string) (err error)
- func (engine *Engine) SecureJsonPrefix(prefix string) *Engine
- func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (engine *Engine) SetFuncMap(funcMap template.FuncMap)
- func (engine *Engine) SetHTMLTemplate(templ *template.Template)
- func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
- type Error
- type ErrorType
- type H
- type HandlerFunc
- func BasicAuth(accounts Accounts) HandlerFunc
- func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
- func Bind(val interface{}) HandlerFunc
- func CustomRecovery(handle RecoveryFunc) HandlerFunc
- func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc
- func ErrorLogger() HandlerFunc
- func ErrorLoggerT(typ ErrorType) HandlerFunc
- func Logger() HandlerFunc
- func LoggerWithConfig(conf LoggerConfig) HandlerFunc
- func LoggerWithFormatter(f LogFormatter) HandlerFunc
- func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
- func Recovery() HandlerFunc
- func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc
- func WrapF(f http.HandlerFunc) HandlerFunc
- func WrapH(h http.Handler) HandlerFunc
- type HandlersChain
- type IRouter
- type IRoutes
- type LogFormatter
- type LogFormatterParams
- type LoggerConfig
- type Negotiate
- type Param
- type Params
- type RecoveryFunc
- type ResponseWriter
- type RouteInfo
- type RouterGroup
- func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) BasePath() string
- func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup
- func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes
- func (group *RouterGroup) Static(relativePath, root string) IRoutes
- func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes
- func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes
- func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes
- type RoutesInfo
Constants ¶
const ( MIMEJSON = binding.MIMEJSON MIMEHTML = binding.MIMEHTML MIMEXML = binding.MIMEXML MIMEXML2 = binding.MIMEXML2 MIMEPlain = binding.MIMEPlain MIMEPOSTForm = binding.MIMEPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEYAML = binding.MIMEYAML )
Content-Type MIME of the most common data formats.
const ( // DebugMode indicates gin mode is debug. DebugMode = "debug" // ReleaseMode indicates gin mode is release. ReleaseMode = "release" // TestMode indicates gin mode is test. TestMode = "test" )
const AuthUserKey = "user"
AuthUserKey is the cookie name for user credential in basic auth.
const BindKey = "_nujikazo/gin/bindkey"
BindKey indicates a default bind key.
const BodyBytesKey = "_nujikazo/gin/bodybyteskey"
BodyBytesKey indicates a default body bytes key.
const EnvGinMode = "GIN_MODE"
EnvGinMode indicates environment name for gin mode.
const Version = "v1.6.3"
Version is the current gin framework's version.
Variables ¶
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
DebugPrintRouteFunc indicates debug log output format.
var DefaultErrorWriter io.Writer = os.Stderr
DefaultErrorWriter is the default io.Writer used by Gin to debug errors
var DefaultWriter io.Writer = os.Stdout
DefaultWriter is the default io.Writer used by Gin for debug output and middleware output like Logger() or Recovery(). Note that both Logger and Recovery provides custom ways to configure their output io.Writer. To support coloring in Windows use:
import "github.com/mattn/go-colorable" gin.DefaultWriter = colorable.NewColorableStdout()
Functions ¶
func CreateTestContext ¶
func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine)
CreateTestContext returns a fresh engine and context for testing purposes
func Dir ¶
func Dir(root string, listDirectory bool) http.FileSystem
Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally in router.Static(). if listDirectory == true, then it works the same as http.Dir() otherwise it returns a filesystem that prevents http.FileServer() to list the directory files.
func DisableBindValidation ¶
func DisableBindValidation()
DisableBindValidation closes the default validator.
func DisableConsoleColor ¶
func DisableConsoleColor()
DisableConsoleColor disables color output in the console.
func EnableJsonDecoderDisallowUnknownFields ¶
func EnableJsonDecoderDisallowUnknownFields()
EnableJsonDecoderDisallowUnknownFields sets true for binding.EnableDecoderDisallowUnknownFields to call the DisallowUnknownFields method on the JSON Decoder instance.
func EnableJsonDecoderUseNumber ¶
func EnableJsonDecoderUseNumber()
EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumber to call the UseNumber method on the JSON Decoder instance.
func ForceConsoleColor ¶
func ForceConsoleColor()
ForceConsoleColor force color output in the console.
func IsDebugging ¶
func IsDebugging() bool
IsDebugging returns true if the framework is running in debug mode. Use SetMode(gin.ReleaseMode) to disable debug mode.
Types ¶
type Context ¶
type Context struct { Request *http.Request Writer ResponseWriter Params Params // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string // contains filtered or unexported fields }
Context is the most important part of gin. It allows us to pass variables between middleware, manage the flow, validate the JSON of a request and render a JSON response for example.
func (*Context) Abort ¶
func (c *Context) Abort()
Abort prevents pending handlers from being called. Note that this will not stop the current handler. Let's say you have an authorization middleware that validates that the current request is authorized. If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers for this request are not called.
func (*Context) AbortWithError ¶
AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. See Context.Error() for more details.
func (*Context) AbortWithStatus ¶
AbortWithStatus calls `Abort()` and writes the headers with the specified status code. For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).
func (*Context) AbortWithStatusJSON ¶
AbortWithStatusJSON calls `Abort()` and then `JSON` internally. This method stops the chain, writes the status code and return a JSON body. It also sets the Content-Type as "application/json".
func (*Context) AsciiJSON ¶
AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. It also sets the Content-Type as "application/json".
func (*Context) Bind ¶
Bind checks the Content-Type to select a binding engine automatically, Depending the "Content-Type" header different bindings are used:
"application/json" --> JSON binding "application/xml" --> XML binding
otherwise --> returns an error. It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer. It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
func (*Context) BindHeader ¶
BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
func (*Context) BindUri ¶
BindUri binds the passed struct pointer using binding.Uri. It will abort the request with HTTP 400 if any error occurs.
func (*Context) BindWith ¶
BindWith binds the passed struct pointer using the specified binding engine. See the binding package.
func (*Context) ClientIP ¶
ClientIP implements a best effort algorithm to return the real client IP, it parses X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy. Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
func (*Context) ContentType ¶
ContentType returns the Content-Type header of the request.
func (*Context) Cookie ¶
Cookie returns the named cookie provided in the request or ErrNoCookie if not found. And return the named cookie is unescaped. If multiple cookies match the given name, only one cookie will be returned.
func (*Context) Copy ¶
Copy returns a copy of the current context that can be safely used outside the request's scope. This has to be used when the context has to be passed to a goroutine.
func (*Context) DataFromReader ¶
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)
DataFromReader writes the specified reader into the body stream and updates the HTTP code.
func (*Context) Deadline ¶
Deadline always returns that there is no deadline (ok==false), maybe you want to use Request.Context().Deadline() instead.
func (*Context) DefaultPostForm ¶
DefaultPostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns the specified defaultValue string. See: PostForm() and GetPostForm() for further information.
func (*Context) DefaultQuery ¶
DefaultQuery returns the keyed url query value if it exists, otherwise it returns the specified defaultValue string. See: Query() and GetQuery() for further information.
GET /?name=Manu&lastname= c.DefaultQuery("name", "unknown") == "Manu" c.DefaultQuery("id", "none") == "none" c.DefaultQuery("lastname", "none") == ""
func (*Context) Done ¶
func (c *Context) Done() <-chan struct{}
Done always returns nil (chan which will wait forever), if you want to abort your work when the connection was closed you should use Request.Context().Done() instead.
func (*Context) Err ¶
Err always returns nil, maybe you want to use Request.Context().Err() instead.
func (*Context) Error ¶
Error attaches an error to the current context. The error is pushed to a list of errors. It's a good idea to call Error for each error that occurred during the resolution of a request. A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response. Error will panic if err is nil.
func (*Context) FileAttachment ¶
FileAttachment writes the specified file into the body stream in an efficient way On the client side, the file will typically be downloaded with the given filename
func (*Context) FileFromFS ¶
func (c *Context) FileFromFS(filepath string, fs http.FileSystem)
FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.
func (*Context) FormFile ¶
func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
FormFile returns the first file for the provided form key.
func (*Context) FullPath ¶
FullPath returns a matched route full path. For not found routes returns an empty string.
router.GET("/user/:id", func(c *gin.Context) { c.FullPath() == "/user/:id" // true })
func (*Context) Get ¶
Get returns the value for the given key, ie: (value, true). If the value does not exists it returns (nil, false)
func (*Context) GetDuration ¶
GetDuration returns the value associated with the key as a duration.
func (*Context) GetFloat64 ¶
GetFloat64 returns the value associated with the key as a float64.
func (*Context) GetPostForm ¶
GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded form or multipart form when it exists `(value, true)` (even when the value is an empty string), otherwise it returns ("", false). For example, during a PATCH request to update the user's email:
email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" email= --> ("", true) := GetPostForm("email") // set email to "" --> ("", false) := GetPostForm("email") // do nothing with email
func (*Context) GetPostFormArray ¶
GetPostFormArray returns a slice of strings for a given form key, plus a boolean value whether at least one value exists for the given key.
func (*Context) GetPostFormMap ¶
GetPostFormMap returns a map for a given form key, plus a boolean value whether at least one value exists for the given key.
func (*Context) GetQuery ¶
GetQuery is like Query(), it returns the keyed url query value if it exists `(value, true)` (even when the value is an empty string), otherwise it returns `("", false)`. It is shortcut for `c.Request.URL.Query().Get(key)`
GET /?name=Manu&lastname= ("Manu", true) == c.GetQuery("name") ("", false) == c.GetQuery("id") ("", true) == c.GetQuery("lastname")
func (*Context) GetQueryArray ¶
GetQueryArray returns a slice of strings for a given query key, plus a boolean value whether at least one value exists for the given key.
func (*Context) GetQueryMap ¶
GetQueryMap returns a map for a given query key, plus a boolean value whether at least one value exists for the given key.
func (*Context) GetRawData ¶
GetRawData return stream data.
func (*Context) GetStringMap ¶
GetStringMap returns the value associated with the key as a map of interfaces.
func (*Context) GetStringMapString ¶
GetStringMapString returns the value associated with the key as a map of strings.
func (*Context) GetStringMapStringSlice ¶
GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func (*Context) GetStringSlice ¶
GetStringSlice returns the value associated with the key as a slice of strings.
func (*Context) GetUint64 ¶
GetUint64 returns the value associated with the key as an unsigned integer.
func (*Context) HTML ¶
HTML renders the HTTP template specified by its file name. It also updates the HTTP code and sets the Content-Type as "text/html". See http://golang.org/doc/articles/wiki/
func (*Context) HandlerName ¶
HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this function will return "main.handleGetUsers".
func (*Context) HandlerNames ¶
HandlerNames returns a list of all registered handlers for this context in descending order, following the semantics of HandlerName()
func (*Context) Header ¶
Header is a intelligent shortcut for c.Writer.Header().Set(key, value). It writes a header in the response. If value == "", this method removes the header `c.Writer.Header().Del(key)`
func (*Context) IndentedJSON ¶
IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. It also sets the Content-Type as "application/json". WARNING: we recommend to use this only for development purposes since printing pretty JSON is more CPU and bandwidth consuming. Use Context.JSON() instead.
func (*Context) IsWebsocket ¶
IsWebsocket returns true if the request headers indicate that a websocket handshake is being initiated by the client.
func (*Context) JSON ¶
JSON serializes the given struct as JSON into the response body. It also sets the Content-Type as "application/json".
func (*Context) JSONP ¶
JSONP serializes the given struct as JSON into the response body. It adds padding to response body to request data from a server residing in a different domain than the client. It also sets the Content-Type as "application/javascript".
func (*Context) MultipartForm ¶
MultipartForm is the parsed multipart form, including file uploads.
func (*Context) MustBindWith ¶
MustBindWith binds the passed struct pointer using the specified binding engine. It will abort the request with HTTP 400 if any error occurs. See the binding package.
func (*Context) MustGet ¶
MustGet returns the value for the given key if it exists, otherwise it panics.
func (*Context) NegotiateFormat ¶
NegotiateFormat returns an acceptable Accept format.
func (*Context) Next ¶
func (c *Context) Next()
Next should be used only inside middleware. It executes the pending handlers in the chain inside the calling handler. See example in GitHub.
func (*Context) Param ¶
Param returns the value of the URL param. It is a shortcut for c.Params.ByName(key)
router.GET("/user/:id", func(c *gin.Context) { // a GET request to /user/john id := c.Param("id") // id == "john" })
func (*Context) PostForm ¶
PostForm returns the specified key from a POST urlencoded form or multipart form when it exists, otherwise it returns an empty string `("")`.
func (*Context) PostFormArray ¶
PostFormArray returns a slice of strings for a given form key. The length of the slice depends on the number of params with the given key.
func (*Context) PostFormMap ¶
PostFormMap returns a map for a given form key.
func (*Context) PureJSON ¶
PureJSON serializes the given struct as JSON into the response body. PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (*Context) Query ¶
Query returns the keyed url query value if it exists, otherwise it returns an empty string `("")`. It is shortcut for `c.Request.URL.Query().Get(key)`
GET /path?id=1234&name=Manu&value= c.Query("id") == "1234" c.Query("name") == "Manu" c.Query("value") == "" c.Query("wtf") == ""
func (*Context) QueryArray ¶
QueryArray returns a slice of strings for a given query key. The length of the slice depends on the number of params with the given key.
func (*Context) SaveUploadedFile ¶
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
SaveUploadedFile uploads the form file to specific dst.
func (*Context) SecureJSON ¶
SecureJSON serializes the given struct as Secure JSON into the response body. Default prepends "while(1)," to response body if the given struct is array values. It also sets the Content-Type as "application/json".
func (*Context) Set ¶
Set is used to store a new key/value pair exclusively for this context. It also lazy initializes c.Keys if it was not used previously.
func (*Context) SetAccepted ¶
SetAccepted sets Accept header data.
func (*Context) SetCookie ¶
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
SetCookie adds a Set-Cookie header to the ResponseWriter's headers. The provided cookie must have a valid Name. Invalid cookies may be silently dropped.
func (*Context) SetSameSite ¶
SetSameSite with cookie
func (*Context) ShouldBind ¶
ShouldBind checks the Content-Type to select a binding engine automatically, Depending the "Content-Type" header different bindings are used:
"application/json" --> JSON binding "application/xml" --> XML binding
otherwise --> returns an error It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer. Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid.
func (*Context) ShouldBindBodyWith ¶
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error)
ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request body into the context, and reuse when it is called again.
NOTE: This method reads the body before binding. So you should use ShouldBindWith for better performance if you need to call only once.
func (*Context) ShouldBindHeader ¶
ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
func (*Context) ShouldBindJSON ¶
ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (*Context) ShouldBindQuery ¶
ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func (*Context) ShouldBindUri ¶
ShouldBindUri binds the passed struct pointer using the specified binding engine.
func (*Context) ShouldBindWith ¶
ShouldBindWith binds the passed struct pointer using the specified binding engine. See the binding package.
func (*Context) ShouldBindXML ¶
ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
func (*Context) ShouldBindYAML ¶
ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
func (*Context) Stream ¶
Stream sends a streaming response and returns a boolean indicates "Is client disconnected in middle of stream"
func (*Context) Value ¶
func (c *Context) Value(key interface{}) interface{}
Value returns the value associated with this context for key, or nil if no value is associated with key. Successive calls to Value with the same key returns the same result.
type Engine ¶
type Engine struct { RouterGroup // Enables automatic redirection if the current route can't be matched but a // handler for the path with (without) the trailing slash exists. // For example if /foo/ is requested but a route only exists for /foo, the // client is redirected to /foo with http status code 301 for GET requests // and 307 for all other request methods. RedirectTrailingSlash bool // If enabled, the router tries to fix the current request path, if no // handle is registered for it. // First superfluous path elements like ../ or // are removed. // Afterwards the router does a case-insensitive lookup of the cleaned path. // If a handle can be found for this route, the router makes a redirection // to the corrected path with status code 301 for GET requests and 307 for // all other request methods. // For example /FOO and /..//Foo could be redirected to /foo. // RedirectTrailingSlash is independent of this option. RedirectFixedPath bool // If enabled, the router checks if another method is allowed for the // current route, if the current request can not be routed. // If this is the case, the request is answered with 'Method Not Allowed' // and HTTP status code 405. // If no other Method is allowed, the request is delegated to the NotFound // handler. HandleMethodNotAllowed bool ForwardedByClientIP bool // #726 #755 If enabled, it will thrust some headers starting with // 'X-AppEngine...' for better integration with that PaaS. AppEngine bool // If enabled, the url.RawPath will be used to find parameters. UseRawPath bool // If true, the path value will be unescaped. // If UseRawPath is false (by default), the UnescapePathValues effectively is true, // as url.Path gonna be used, which is already unescaped. UnescapePathValues bool // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm // method call. MaxMultipartMemory int64 // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. // See the PR #1817 and issue #1644 RemoveExtraSlash bool HTMLRender render.HTMLRender FuncMap template.FuncMap // contains filtered or unexported fields }
Engine is the framework's instance, it contains the muxer, middleware and configuration settings. Create an instance of Engine, by using New() or Default()
func Default ¶
func Default() *Engine
Default returns an Engine instance with the Logger and Recovery middleware already attached.
func New ¶
func New() *Engine
New returns a new blank Engine instance without any middleware attached. By default the configuration is: - RedirectTrailingSlash: true - RedirectFixedPath: false - HandleMethodNotAllowed: false - ForwardedByClientIP: true - UseRawPath: false - UnescapePathValues: true
func (*Engine) HandleContext ¶
HandleContext re-enter a context that has been rewritten. This can be done by setting c.Request.URL.Path to your new target. Disclaimer: You can loop yourself to death with this, use wisely.
func (*Engine) LoadHTMLFiles ¶
LoadHTMLFiles loads a slice of HTML files and associates the result with HTML renderer.
func (*Engine) LoadHTMLGlob ¶
LoadHTMLGlob loads HTML files identified by glob pattern and associates the result with HTML renderer.
func (*Engine) NoMethod ¶
func (engine *Engine) NoMethod(handlers ...HandlerFunc)
NoMethod sets the handlers called when... TODO.
func (*Engine) NoRoute ¶
func (engine *Engine) NoRoute(handlers ...HandlerFunc)
NoRoute adds handlers for NoRoute. It return a 404 code by default.
func (*Engine) Routes ¶
func (engine *Engine) Routes() (routes RoutesInfo)
Routes returns a slice of registered routes, including some useful information, such as: the http method, path and the handler name.
func (*Engine) Run ¶
Run attaches the router to a http.Server and starts listening and serving HTTP requests. It is a shortcut for http.ListenAndServe(addr, router) Note: this method will block the calling goroutine indefinitely unless an error happens.
func (*Engine) RunFd ¶
RunFd attaches the router to a http.Server and starts listening and serving HTTP requests through the specified file descriptor. Note: this method will block the calling goroutine indefinitely unless an error happens.
func (*Engine) RunListener ¶
RunListener attaches the router to a http.Server and starts listening and serving HTTP requests through the specified net.Listener
func (*Engine) RunTLS ¶
RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests. It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) Note: this method will block the calling goroutine indefinitely unless an error happens.
func (*Engine) RunUnix ¶
RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests through the specified unix socket (ie. a file). Note: this method will block the calling goroutine indefinitely unless an error happens.
func (*Engine) SecureJsonPrefix ¶
SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
func (*Engine) ServeHTTP ¶
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP conforms to the http.Handler interface.
func (*Engine) SetFuncMap ¶
SetFuncMap sets the FuncMap used for template.FuncMap.
func (*Engine) SetHTMLTemplate ¶
SetHTMLTemplate associate a template with HTML renderer.
func (*Engine) Use ¶
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
Use attaches a global middleware to the router. ie. the middleware attached though Use() will be included in the handlers chain for every single request. Even 404, 405, static files... For example, this is the right place for a logger or error management middleware.
type Error ¶
Error represents a error's specification.
func (*Error) MarshalJSON ¶
MarshalJSON implements the json.Marshaller interface.
type ErrorType ¶
type ErrorType uint64
ErrorType is an unsigned 64-bit error code as defined in the gin spec.
const ( // ErrorTypeBind is used when Context.Bind() fails. ErrorTypeBind ErrorType = 1 << 63 // ErrorTypeRender is used when Context.Render() fails. ErrorTypeRender ErrorType = 1 << 62 // ErrorTypePrivate indicates a private error. ErrorTypePrivate ErrorType = 1 << 0 // ErrorTypePublic indicates a public error. ErrorTypePublic ErrorType = 1 << 1 // ErrorTypeAny indicates any other error. ErrorTypeAny ErrorType = 1<<64 - 1 // ErrorTypeNu indicates any other error. ErrorTypeNu = 2 )
type H ¶
type H map[string]interface{}
H is a shortcut for map[string]interface{}
func (H) MarshalXML ¶
MarshalXML allows type H to be used with xml.Marshal.
type HandlerFunc ¶
type HandlerFunc func(*Context)
HandlerFunc defines the handler used by gin middleware as return value.
func BasicAuth ¶
func BasicAuth(accounts Accounts) HandlerFunc
BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where the key is the user name and the value is the password.
func BasicAuthForRealm ¶
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where the key is the user name and the value is the password, as well as the name of the Realm. If the realm is empty, "Authorization Required" will be used by default. (see http://tools.ietf.org/html/rfc2617#section-1.2)
func Bind ¶
func Bind(val interface{}) HandlerFunc
Bind is a helper function for given interface object and returns a Gin middleware.
func CustomRecovery ¶
func CustomRecovery(handle RecoveryFunc) HandlerFunc
CustomRecovery returns a middleware that recovers from any panics and calls the provided handle func to handle it.
func CustomRecoveryWithWriter ¶
func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc
CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it.
func ErrorLogger ¶
func ErrorLogger() HandlerFunc
ErrorLogger returns a handlerfunc for any error type.
func ErrorLoggerT ¶
func ErrorLoggerT(typ ErrorType) HandlerFunc
ErrorLoggerT returns a handlerfunc for a given error type.
func Logger ¶
func Logger() HandlerFunc
Logger instances a Logger middleware that will write the logs to gin.DefaultWriter. By default gin.DefaultWriter = os.Stdout.
func LoggerWithConfig ¶
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
LoggerWithConfig instance a Logger middleware with config.
func LoggerWithFormatter ¶
func LoggerWithFormatter(f LogFormatter) HandlerFunc
LoggerWithFormatter instance a Logger middleware with the specified log format function.
func LoggerWithWriter ¶
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
LoggerWithWriter instance a Logger middleware with the specified writer buffer. Example: os.Stdout, a file opened in write mode, a socket...
func Recovery ¶
func Recovery() HandlerFunc
Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
func RecoveryWithWriter ¶
func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc
RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
func WrapF ¶
func WrapF(f http.HandlerFunc) HandlerFunc
WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
func WrapH ¶
func WrapH(h http.Handler) HandlerFunc
WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
type HandlersChain ¶
type HandlersChain []HandlerFunc
HandlersChain defines a HandlerFunc array.
func (HandlersChain) Last ¶
func (c HandlersChain) Last() HandlerFunc
Last returns the last handler in the chain. ie. the last handler is the main one.
type IRouter ¶
type IRouter interface { IRoutes Group(string, ...HandlerFunc) *RouterGroup }
IRouter defines all router handle interface includes single and group router.
type IRoutes ¶
type IRoutes interface { Use(...HandlerFunc) IRoutes Handle(string, string, ...HandlerFunc) IRoutes Any(string, ...HandlerFunc) IRoutes GET(string, ...HandlerFunc) IRoutes POST(string, ...HandlerFunc) IRoutes DELETE(string, ...HandlerFunc) IRoutes PATCH(string, ...HandlerFunc) IRoutes PUT(string, ...HandlerFunc) IRoutes OPTIONS(string, ...HandlerFunc) IRoutes HEAD(string, ...HandlerFunc) IRoutes StaticFile(string, string) IRoutes Static(string, string) IRoutes StaticFS(string, http.FileSystem) IRoutes }
IRoutes defines all router handle interface.
type LogFormatter ¶
type LogFormatter func(params LogFormatterParams) string
LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
type LogFormatterParams ¶
type LogFormatterParams struct { Request *http.Request // TimeStamp shows the time after the server returns a response. TimeStamp time.Time // StatusCode is HTTP response code. StatusCode int // Latency is how much time the server cost to process a certain request. Latency time.Duration // ClientIP equals Context's ClientIP method. ClientIP string // Method is the HTTP method given to the request. Method string // Path is a path the client requests. Path string // ErrorMessage is set if error has occurred in processing the request. ErrorMessage string // BodySize is the size of the Response Body BodySize int // Keys are the keys set on the request's context. Keys map[string]interface{} // contains filtered or unexported fields }
LogFormatterParams is the structure any formatter will be handed when time to log comes
func (*LogFormatterParams) IsOutputColor ¶
func (p *LogFormatterParams) IsOutputColor() bool
IsOutputColor indicates whether can colors be outputted to the log.
func (*LogFormatterParams) MethodColor ¶
func (p *LogFormatterParams) MethodColor() string
MethodColor is the ANSI color for appropriately logging http method to a terminal.
func (*LogFormatterParams) ResetColor ¶
func (p *LogFormatterParams) ResetColor() string
ResetColor resets all escape attributes.
func (*LogFormatterParams) StatusCodeColor ¶
func (p *LogFormatterParams) StatusCodeColor() string
StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
type LoggerConfig ¶
type LoggerConfig struct { // Optional. Default value is gin.defaultLogFormatter Formatter LogFormatter // Output is a writer where logs are written. // Optional. Default value is gin.DefaultWriter. Output io.Writer // SkipPaths is a url path array which logs are not written. // Optional. SkipPaths []string }
LoggerConfig defines the config for Logger middleware.
type Negotiate ¶
type Negotiate struct { Offered []string HTMLName string HTMLData interface{} JSONData interface{} XMLData interface{} YAMLData interface{} Data interface{} }
Negotiate contains all negotiations data.
type Params ¶
type Params []Param
Params is a Param-slice, as returned by the router. The slice is ordered, the first URL parameter is also the first slice value. It is therefore safe to read values by the index.
type RecoveryFunc ¶
type RecoveryFunc func(c *Context, err interface{})
RecoveryFunc defines the function passable to CustomRecovery.
type ResponseWriter ¶
type ResponseWriter interface { http.ResponseWriter http.Hijacker http.Flusher http.CloseNotifier // Returns the HTTP response status code of the current request. Status() int // Returns the number of bytes already written into the response http body. // See Written() Size() int // Writes the string into the response body. WriteString(string) (int, error) // Returns true if the response body was already written. Written() bool // Forces to write the http header (status code + headers). WriteHeaderNow() // get the http.Pusher for server push Pusher() http.Pusher }
ResponseWriter ...
type RouteInfo ¶
type RouteInfo struct { Method string Path string Handler string HandlerFunc HandlerFunc }
RouteInfo represents a request route's specification which contains method and path and its handler.
type RouterGroup ¶
type RouterGroup struct { Handlers HandlersChain // contains filtered or unexported fields }
RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix and an array of handlers (middleware).
func (*RouterGroup) Any ¶
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes
Any registers a route that matches all the HTTP methods. GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
func (*RouterGroup) BasePath ¶
func (group *RouterGroup) BasePath() string
BasePath returns the base path of router group. For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
func (*RouterGroup) DELETE ¶
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes
DELETE is a shortcut for router.Handle("DELETE", path, handle).
func (*RouterGroup) GET ¶
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
GET is a shortcut for router.Handle("GET", path, handle).
func (*RouterGroup) Group ¶
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup
Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix. For example, all the routes that use a common middleware for authorization could be grouped.
func (*RouterGroup) HEAD ¶
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes
HEAD is a shortcut for router.Handle("HEAD", path, handle).
func (*RouterGroup) Handle ¶
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
Handle registers a new request handle and middleware with the given path and method. The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes. See the example code in GitHub.
For GET, POST, PUT, PATCH and DELETE requests the respective shortcut functions can be used.
This function is intended for bulk loading and to allow the usage of less frequently used, non-standardized or custom methods (e.g. for internal communication with a proxy).
func (*RouterGroup) OPTIONS ¶
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes
OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
func (*RouterGroup) PATCH ¶
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes
PATCH is a shortcut for router.Handle("PATCH", path, handle).
func (*RouterGroup) POST ¶
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes
POST is a shortcut for router.Handle("POST", path, handle).
func (*RouterGroup) PUT ¶
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes
PUT is a shortcut for router.Handle("PUT", path, handle).
func (*RouterGroup) Static ¶
func (group *RouterGroup) Static(relativePath, root string) IRoutes
Static serves files from the given file system root. Internally a http.FileServer is used, therefore http.NotFound is used instead of the Router's NotFound handler. To use the operating system's file system implementation, use :
router.Static("/static", "/var/www")
func (*RouterGroup) StaticFS ¶
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes
StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead. Gin by default user: gin.Dir()
func (*RouterGroup) StaticFile ¶
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes
StaticFile registers a single route in order to serve a single file of the local filesystem. router.StaticFile("favicon.ico", "./resources/favicon.ico")
func (*RouterGroup) Use ¶
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes
Use adds middleware to the group, see example code in GitHub.