Sem descrição

Manu Mtz-Almeida 1abc373aff Some work around benchmarks há 10 anos atrás
examples 8f3f3597f3 Merge branch 'master' of https://github.com/ngerakines/gin into ngerakines-master há 10 anos atrás
.travis.yml 48911116ce removing CI for go v1.0 há 10 anos atrás
LICENSE b6bd5b0d9f Add MIT license há 10 anos atrás
README.md 8f3f3597f3 Merge branch 'master' of https://github.com/ngerakines/gin into ngerakines-master há 10 anos atrás
auth.go 661398ca53 Nicer BasicAuth API há 10 anos atrás
benchmarks_test.go 1abc373aff Some work around benchmarks há 10 anos atrás
gin.go 8f3f3597f3 Merge branch 'master' of https://github.com/ngerakines/gin into ngerakines-master há 10 anos atrás
logger.go 78536abb58 Better logger há 10 anos atrás
recovery.go fa80b047f9 The Recovery() middleware should not print the errors (only panics) há 10 anos atrás
response_writer.go d42aa6d868 Fixes wrap around http.ResponseWriter há 10 anos atrás
validation.go 40dc444270 Renames ErrorRender() to ErrorLogger() há 10 anos atrás

README.md

#Gin Web Framework

GoDoc Build Status

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

##Gin is new, will it be supported?

Yes, Gin is an internal project of my upcoming startup. We developed it and we are going to continue using and improve it.

##Roadmap for v0.2

  • Performance improments, reduce allocation and garbage collection overhead
  • Fix bugs
  • Ask our designer for a cool logo
  • Add tons of unit tests and benchmarks
  • Improve logging system
  • Improve JSON/XML validation using bindings
  • Improve XML support
  • Improve documentation
  • Add more cool middlewares, for example redis catching (this also helps developers to understand the framework)
  • Continuous integration

Start using it

Obviously, you need to have Git and Go! already installed to run Gin.
Run this in your terminal

go get github.com/gin-gonic/gin

Then import it in your Go! code:

import "github.com/gin-gonic/gin"

##API Examples

Create most basic PING/PONG HTTP endpoint

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context){
        c.String(200, "pong")
    })
    
    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

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

func main() {
    // Creates a gin router + logger and recovery (crash-free) middlewares
    r := gin.Default()
    
    r.GET("/someGet", getting)
    r.POST("/somePost", posting)
    r.PUT("/somePut", putting)
    r.DELETE("/someDelete", deleting)
    r.PATCH("/somePatch", patching)
    r.HEAD("/someHead", head)
    r.OPTIONS("/someOptions", options)

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

Parameters in path

func main() {
    r := gin.Default()
    
    r.GET("/user/:name", func(c *gin.Context) {
        name := c.Params.ByName("name")
        message := "Hello "+name
        c.String(200, message)
    })

    r.GET("/user/:name/:action", func(c *gin.Context) {
        name := c.Params.ByName("name")
        action := c.Params.ByName("action")
        message := name + " is " + action
        c.String(200, message)
    })

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

Grouping routes

func main() {
    r := gin.Default()
    
    // Simple group: v1
    v1 := r.Group("/v1")
    {
        v1.POST("/login", loginEndpoint)
        v1.POST("/submit", submitEndpoint)
        v1.POST("/read", readEndpoint)
    }
    
    // Simple group: v2
    v2 := r.Group("/v2")
    {
        v2.POST("/login", loginEndpoint)
        v2.POST("/submit", submitEndpoint)
        v2.POST("/read", readEndpoint)
    }

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

Blank Gin without middlewares by default

Use

r := gin.New()

instead of

r := gin.Default()

Using middlewares

func main() {
    // Creates a router without any middleware by default
    r := gin.New()
    
    // Global middlewares
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    
    // Per route middlewares, you can add as many as you desire.
    r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

    // Authorization group
    // authorized := r.Group("/", AuthRequired())
    // exactly the same than:
    authorized := r.Group("/")
    // per group middlewares! in this case we use the custom created
    // AuthRequired() middleware just in the "authorized" group.
    authorized.Use(AuthRequired())
    {
        authorized.POST("/login", loginEndpoint)
        authorized.POST("/submit", submitEndpoint)
        authorized.POST("/read", readEndpoint)
        
        // nested group
        testing := authorized.Group("testing")
        testing.GET("/analytics", analyticsEndpoint)
    }
   
    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

JSON parsing and validation

type LoginJSON struct {
    User     string `json:"user" binding:"required"`
    Password string `json:"password" binding:"required"`
}

func main() {
    r := gin.Default()
    
    r.POST("/login", func(c *gin.Context) {
        var json LoginJSON
        
        // If EnsureBody returns false, it will write automatically the error
        // in the HTTP stream and return a 400 error. If you want custom error 
        // handling you should use: c.ParseBody(interface{}) error
        if c.EnsureBody(&json) {
            if json.User=="manu" && json.Password=="123" {
                c.JSON(200, gin.H{"status": "you are logged in"})
            }else{
                c.JSON(401, gin.H{"status": "unauthorized"})
            }
        }
    })

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

XML, and JSON rendering

func main() {
    r := gin.Default()
    
    // gin.H is a shortcup for map[string]interface{}
    r.GET("/someJSON", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "hey", "status": 200})
    })
    
    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(200, msg)
    })
    
    r.GET("/someXML", func(c *gin.Context) {
        c.XML(200, gin.H{"message": "hey", "status": 200})
    })

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

####HTML rendering

Using LoadHTMLTemplates()

func main() {
    r := gin.Default()
    r.LoadHTMLTemplates("templates/*")
    r.GET("/index", func(c *gin.Context) {
        obj := gin.H{"title": "Main website"}
        c.HTML(200, "index.tmpl", obj)
    })

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

You can also use your own html template render

import "html/template"
func main() {
    r := gin.Default()
    html := template.Must(template.ParseFiles("file1", "file2"))
    r.HTMLTemplates = html

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

Custom Middlewares

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()

        // Set example variable
        c.Set("example", "12345")

        // before request

        c.Next()

        // after request
        latency := time.Since(t)
        log.Print(latency)

        // access the status we are sending
        status := c.Writer.Status()
        log.Println(status)
    }
}

func main() {
    r := gin.New()
    r.Use(Logger())

    r.GET("/test", func(c *gin.Context){
        example := c.MustGet("example").(string)

        // it would print: "12345"
        log.Println(example)
    })

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}

Using BasicAuth() middleware

// similate 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 setted by the BasicAuth middleware
		user := c.Get(gin.AuthUserKey).(string)
		if secret, ok := secrets[user]; ok {
			c.JSON(200, gin.H{"user": user, "secret": secret})
		} else {
			c.JSON(200, gin.H{"user": user, "secret": "NO SECRET :("})
		}
	})

	// Listen and server on 0.0.0.0:8080
	r.Run(":8080")
}

Goroutines inside a middleware

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

func main() {
	r := gin.Default()

	r.GET("/long_async", func(c *gin.Context) {
		// create copy to be used inside the goroutine
		c_cp := c.Copy()
		go func() {
			// simulate a long task with time.Sleep(). 5 seconds
			time.Sleep(5 * time.Second)

			// note than you are using the copied context "c_cp", IMPORTANT
			log.Println("Done! in path " + c_cp.Req.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.Req.URL.Path)
	})

    // Listen and server 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()
}