gin

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2015 License: MIT, MIT Imports: 24 Imported by: 0

README

#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 thanks to httprouter. If you need performance and good productivity, you will love Gin.

Gin console logger

$ cat test.go
package main

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

func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.String(200, "hello world")
	})
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	router.POST("/submit", func(c *gin.Context) {
		c.String(401, "not authorized")
	})
	router.PUT("/error", func(c *gin.Context) {
		c.String(500, "and error hapenned :(")
	})
	router.Run(":8080")
}

##Gin is new, will it be supported?

Yes, Gin is an internal tool of Manu and Javi for many of our projects/start-ups. We developed it and we are going to continue using and improve it.

##Roadmap for v1.0

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

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"

##Community If you'd like to help out with the project, there's a mailing list and IRC channel where Gin discussions normally happen.

##API Examples

Create most basic PING/PONG HTTP endpoint
package main

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()
	
	// This handler will match /user/john but will not match neither /user/ or /user
	r.GET("/user/:name", func(c *gin.Context) {
		name := c.Params.ByName("name")
		message := "Hello "+name
		c.String(200, message)
	})

	// However, this one will match /user/john/ and also /user/john/send
	// If no other routers match /user/john, it will redirect to /user/join/
	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")
}

###Form parameters

func main() {
	r := gin.Default()
	
	// This will respond to urls like search?firstname=Jane&lastname=Doe
	r.GET("/search", func(c *gin.Context) {
		// You need to call ParseForm() on the request to receive url and form params first
		c.Request.ParseForm()
		
		firstname := c.Request.Form.Get("firstname")
		lastname := c.Request.Form.get("lastname")

		message := "Hello "+ firstname + lastname
		c.String(200, message)
	})
	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")
}
Model binding and validation

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

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

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

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

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

// Binding from form values
type LoginForm struct {
    User     string `form:"user" binding:"required"`
    Password string `form:"password" binding:"required"`   
}

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

    // Example for binding JSON ({"user": "manu", "password": "123"})
	r.POST("/loginJSON", func(c *gin.Context) {
		var json LoginJSON

        c.Bind(&json) // This will infer what binder to use depending on the content-type header.
        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"})
        }
	})

    // Example for binding a HTML form (user=manu&password=123)
    r.POST("/loginHTML", func(c *gin.Context) {
        var form LoginForm

        c.BindWith(&form, binding.Form) // You can also specify which binder to use. We support binding.Form, binding.JSON and binding.XML.
        if form.User == "manu" && form.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 shortcut 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")
}

####Serving static files

Use Engine.ServeFiles(path string, root http.FileSystem):

func main() {
    r := gin.Default()
    r.Static("/assets", "./assets")

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

Note: this will use httpNotFound instead of the Router's NotFound handler.

####HTML rendering

Using LoadHTMLTemplates()

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("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")
}
<h1>
	{{ .title }}
</h1>

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.SetHTMLTemplate(html)

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

Issuing a HTTP redirect is easy:

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

Both internal and external locations are supported.

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.MustGet(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.Request.URL.Path)
		}()
	})


	r.GET("/long_sync", func(c *gin.Context) {
		// simulate a long task with time.Sleep(). 5 seconds
		time.Sleep(5 * time.Second)

		// since we are NOT using a goroutine, we do not have to copy the context
		log.Println("Done! in path " + c.Request.URL.Path)
	})

    // Listen and server on 0.0.0.0:8080
    r.Run(":8080")
}
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()
}

Documentation

Index

Constants

View Source
const (
	ErrorTypeInternal = 1 << iota
	ErrorTypeExternal = 1 << iota
	ErrorTypeAll      = 0xffffffff
)
View Source
const (
	AbortIndex   = math.MaxInt8 / 2
	MIMEJSON     = "application/json"
	MIMEHTML     = "text/html"
	MIMEXML      = "application/xml"
	MIMEXML2     = "text/xml"
	MIMEPlain    = "text/plain"
	MIMEPOSTForm = "application/x-www-form-urlencoded"
)
View Source
const (
	DebugMode   string = "debug"
	ReleaseMode string = "release"
	TestMode    string = "test"
)
View Source
const (
	AuthUserKey = "user"
)
View Source
const GIN_MODE = "GIN_MODE"
View Source
const (
	NoWritten = -1
)

Variables

This section is empty.

Functions

func IsDebugging added in v0.5.1

func IsDebugging() bool

func Mode added in v0.5.1

func Mode() string

func SetMode

func SetMode(value string)

Types

type Accounts

type Accounts map[string]string

type Context

type Context struct {
	Request *http.Request
	Writer  ResponseWriter
	Keys    map[string]interface{}
	Errors  errorMsgs
	Params  httprouter.Params
	Engine  *Engine
	// 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()

Forces the system to do not continue calling the pending handlers in the chain.

func (*Context) AbortWithStatus added in v0.5.1

func (c *Context) AbortWithStatus(code int)

Same than AbortWithStatus() but also writes the specified response status code. For example, the first handler checks if the request is authorized. If it's not, context.AbortWithStatus(401) should be called.

func (*Context) Bind

func (c *Context) Bind(obj interface{}) bool

This function 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 else --> returns an error if 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 ParseBody() but this method also writes a 400 error if the json is not valid.

func (*Context) BindWith

func (c *Context) BindWith(obj interface{}, b binding.Binding) bool

func (*Context) ClientIP added in v0.5.1

func (c *Context) ClientIP() string

func (*Context) Copy

func (c *Context) Copy() *Context

func (*Context) Data

func (c *Context) Data(code int, contentType string, data []byte)

Writes some data into the body stream and updates the HTTP code.

func (*Context) EnsureBody

func (c *Context) EnsureBody(item interface{}) bool

DEPRECATED, use Bind() instead. Like ParseBody() but this method also writes a 400 error if the json is not valid.

func (*Context) Error

func (c *Context) Error(err error, meta interface{})

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.

func (*Context) ErrorTyped

func (c *Context) ErrorTyped(err error, typ uint32, meta interface{})

func (*Context) Fail

func (c *Context) Fail(code int, err error)

Fail is the same as Abort plus an error message. Calling `context.Fail(500, err)` is equivalent to: ``` context.Error("Operation aborted", err) context.AbortWithStatus(500) ```

func (*Context) File

func (c *Context) File(filepath string)

Writes the specified file into the body stream

func (*Context) Get

func (c *Context) Get(key string) (interface{}, error)

Get returns the value for the given key or an error if the key does not exist.

func (*Context) HTML

func (c *Context) HTML(code int, name string, obj interface{})

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) JSON

func (c *Context) JSON(code int, obj interface{})

Serializes the given struct as JSON into the response body in a fast and efficient way. It also sets the Content-Type as "application/json".

func (*Context) LastError

func (c *Context) LastError() error

func (*Context) MustGet

func (c *Context) MustGet(key string) interface{}

MustGet returns the value for the given key or panics if the value doesn't exist.

func (*Context) Negotiate added in v0.5.1

func (c *Context) Negotiate(code int, config Negotiate)

func (*Context) NegotiateFormat added in v0.5.1

func (c *Context) NegotiateFormat(offered ...string) string

func (*Context) Next

func (c *Context) Next()

Next should be used only in the middlewares. It executes the pending handlers in the chain inside the calling handler. See example in github.

func (*Context) ParseBody

func (c *Context) ParseBody(item interface{}) error

DEPRECATED use bindings directly Parses the body content as a JSON input. It decodes the json payload into the struct specified as a pointer.

func (*Context) Redirect

func (c *Context) Redirect(code int, location string)

Returns a HTTP redirect to the specific location.

func (*Context) Render

func (c *Context) Render(code int, render render.Render, obj ...interface{})

func (*Context) Set

func (c *Context) Set(key string, item interface{})

Sets a new pair key/value just for the specified context. It also lazy initializes the hashmap.

func (*Context) SetAccepted added in v0.5.1

func (c *Context) SetAccepted(formats ...string)

func (*Context) String

func (c *Context) String(code int, format string, values ...interface{})

Writes the given string into the response body and sets the Content-Type to "text/plain".

func (*Context) XML

func (c *Context) XML(code int, obj interface{})

Serializes the given struct as XML into the response body in a fast and efficient way. It also sets the Content-Type as "application/xml".

type Engine

type Engine struct {
	*RouterGroup
	HTMLRender     render.Render
	Default404Body []byte
	// contains filtered or unexported fields
}

Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.

func Default

func Default() *Engine

Returns a Engine instance with the Logger and Recovery already attached.

func New

func New() *Engine

Returns a new blank Engine instance without any middleware attached. The most basic configuration

func (*Engine) LoadHTMLFiles

func (engine *Engine) LoadHTMLFiles(files ...string)

func (*Engine) LoadHTMLGlob

func (engine *Engine) LoadHTMLGlob(pattern string)

func (*Engine) LoadHTMLTemplates

func (engine *Engine) LoadHTMLTemplates(pattern string)

DEPRECATED use gin.LoadHTMLGlob() or gin.LoadHTMLFiles() instead

func (*Engine) NoRoute

func (engine *Engine) NoRoute(handlers ...HandlerFunc)

Adds handlers for NoRoute. It return a 404 code by default.

func (*Engine) NotFound404

func (engine *Engine) NotFound404(handlers ...HandlerFunc)

DEPRECATED. Use NoRoute() instead

func (*Engine) Run

func (engine *Engine) Run(addr string) error

func (*Engine) RunTLS

func (engine *Engine) RunTLS(addr string, cert string, key string) error

func (*Engine) ServeFiles

func (engine *Engine) ServeFiles(path string, root http.FileSystem)

DEPRECATED use gin.Static() instead ServeFiles serves files from the given file system root. The path must end with "/*filepath", files are then served from the local path /defined/root/dir/*filepath. For example if root is "/etc" and *filepath is "passwd", the local file "/etc/passwd" would be served. 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 http.Dir:

router.ServeFiles("/src/*filepath", http.Dir("/var/www"))

func (*Engine) ServeHTTP

func (engine *Engine) ServeHTTP(writer http.ResponseWriter, request *http.Request)

ServeHTTP makes the router implement the http.Handler interface.

func (*Engine) SetHTMLTemplate

func (engine *Engine) SetHTMLTemplate(templ *template.Template)

func (*Engine) Use

func (engine *Engine) Use(middlewares ...HandlerFunc)

type H

type H map[string]interface{}

func (H) MarshalXML

func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error

Allows type H to be used with xml.Marshal

type HandlerFunc

type HandlerFunc func(*Context)

func BasicAuth

func BasicAuth(accounts Accounts) HandlerFunc

Implements a basic Basic HTTP Authorization. It takes as argument a map[string]string where the key is the user name and the value is the password.

func ErrorLogger

func ErrorLogger() HandlerFunc

func ErrorLoggerT

func ErrorLoggerT(typ uint32) HandlerFunc

func ForwardedFor added in v0.5.1

func ForwardedFor(proxies ...interface{}) HandlerFunc

the ForwardedFor middleware unwraps the X-Forwarded-For headers, be careful to only use this middleware if you've got servers in front of this server. The list with (known) proxies and local ips are being filtered out of the forwarded for list, giving the last not local ip being the real client ip.

func Logger

func Logger() HandlerFunc

func Recovery

func Recovery() HandlerFunc

Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. While Martini is in development mode, Recovery will also output the panic as HTML.

type Negotiate added in v0.5.1

type Negotiate struct {
	Offered  []string
	HTMLPath string
	HTMLData interface{}
	JSONData interface{}
	XMLData  interface{}
	Data     interface{}
}

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter
	http.Hijacker
	http.Flusher
	http.CloseNotifier

	Status() int
	Size() int
	Written() bool
	WriteHeaderNow()
}

type RouterGroup

type RouterGroup struct {
	Handlers []HandlerFunc
	// contains filtered or unexported fields
}

Used internally to configure router, a RouterGroup is associated with a prefix and an array of handlers (middlewares)

func (*RouterGroup) DELETE

func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc)

DELETE is a shortcut for router.Handle("DELETE", path, handle)

func (*RouterGroup) GET

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc)

GET is a shortcut for router.Handle("GET", path, handle)

func (*RouterGroup) Group

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup

Creates a new router group. You should add all the routes that have common middlwares or the same path prefix. For example, all the routes that use a common middlware for authorization could be grouped.

func (*RouterGroup) HEAD

func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc)

HEAD is a shortcut for router.Handle("HEAD", path, handle)

func (*RouterGroup) Handle

func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers []HandlerFunc)

Handle registers a new request handle and middlewares with the given path and method. The last handler should be the real handler, the other ones should be middlewares 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 (group *RouterGroup) LINK(relativePath string, handlers ...HandlerFunc)

LINK is a shortcut for router.Handle("LINK", path, handle)

func (*RouterGroup) OPTIONS

func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc)

OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)

func (*RouterGroup) PATCH

func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc)

PATCH is a shortcut for router.Handle("PATCH", path, handle)

func (*RouterGroup) POST

func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc)

POST is a shortcut for router.Handle("POST", path, handle)

func (*RouterGroup) PUT

func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc)

PUT is a shortcut for router.Handle("PUT", path, handle)

func (*RouterGroup) Static

func (group *RouterGroup) Static(relativePath, root string)

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 (group *RouterGroup) UNLINK(relativePath string, handlers ...HandlerFunc)

UNLINK is a shortcut for router.Handle("UNLINK", path, handle)

func (*RouterGroup) Use

func (group *RouterGroup) Use(middlewares ...HandlerFunc)

Adds middlewares to the group, see example code in github.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL