forked from mirror/gin
Comments + IRoutes + IRouter + unexported AbortIndex
This commit is contained in:
parent
13f57702d4
commit
8f3047814e
16
auth.go
16
auth.go
|
@ -10,9 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const AuthUserKey = "user"
|
||||||
AuthUserKey = "user"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Accounts map[string]string
|
Accounts map[string]string
|
||||||
|
@ -35,8 +33,9 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements a basic Basic HTTP Authorization. It takes as arguments a map[string]string where
|
// 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
|
// 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)
|
// (see http://tools.ietf.org/html/rfc2617#section-1.2)
|
||||||
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
||||||
if realm == "" {
|
if realm == "" {
|
||||||
|
@ -59,7 +58,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements a basic Basic HTTP Authorization. It takes as argument a map[string]string where
|
// 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.
|
// the key is the user name and the value is the password.
|
||||||
func BasicAuth(accounts Accounts) HandlerFunc {
|
func BasicAuth(accounts Accounts) HandlerFunc {
|
||||||
return BasicAuthForRealm(accounts, "")
|
return BasicAuthForRealm(accounts, "")
|
||||||
|
@ -91,8 +90,7 @@ func authorizationHeader(user, password string) string {
|
||||||
func secureCompare(given, actual string) bool {
|
func secureCompare(given, actual string) bool {
|
||||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||||
} else {
|
|
||||||
/* Securely compare actual to itself to keep constant time, but always return false */
|
|
||||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
|
||||||
}
|
}
|
||||||
|
/* Securely compare actual to itself to keep constant time, but always return false */
|
||||||
|
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||||
}
|
}
|
||||||
|
|
26
context.go
26
context.go
|
@ -18,6 +18,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Content-Type MIME of the most common data formats
|
||||||
const (
|
const (
|
||||||
MIMEJSON = binding.MIMEJSON
|
MIMEJSON = binding.MIMEJSON
|
||||||
MIMEHTML = binding.MIMEHTML
|
MIMEHTML = binding.MIMEHTML
|
||||||
|
@ -28,7 +29,7 @@ const (
|
||||||
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
||||||
)
|
)
|
||||||
|
|
||||||
const AbortIndex int8 = math.MaxInt8 / 2
|
const abortIndex int8 = math.MaxInt8 / 2
|
||||||
|
|
||||||
// Context is the most important part of gin. It allows us to pass variables between middleware,
|
// 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.
|
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
||||||
|
@ -63,16 +64,18 @@ func (c *Context) reset() {
|
||||||
c.Accepted = nil
|
c.Accepted = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the current context that can be safely used outside the request's scope.
|
||||||
|
// This have to be used then the context has to be passed to a goroutine.
|
||||||
func (c *Context) Copy() *Context {
|
func (c *Context) Copy() *Context {
|
||||||
var cp Context = *c
|
var cp Context = *c
|
||||||
cp.writermem.ResponseWriter = nil
|
cp.writermem.ResponseWriter = nil
|
||||||
cp.Writer = &cp.writermem
|
cp.Writer = &cp.writermem
|
||||||
cp.index = AbortIndex
|
cp.index = abortIndex
|
||||||
cp.handlers = nil
|
cp.handlers = nil
|
||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the main handle's name. For example if the handler is "handleGetUsers()", this
|
// HandlerName returns the main handle's name. For example if the handler is "handleGetUsers()", this
|
||||||
// function will return "main.handleGetUsers"
|
// function will return "main.handleGetUsers"
|
||||||
func (c *Context) HandlerName() string {
|
func (c *Context) HandlerName() string {
|
||||||
return nameOfFunction(c.handlers.Last())
|
return nameOfFunction(c.handlers.Last())
|
||||||
|
@ -93,27 +96,27 @@ func (c *Context) Next() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns if the currect context was aborted.
|
// IsAborted returns true if the currect context was aborted.
|
||||||
func (c *Context) IsAborted() bool {
|
func (c *Context) IsAborted() bool {
|
||||||
return c.index >= AbortIndex
|
return c.index >= abortIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the system to continue calling the pending handlers in the chain.
|
// Abort stops the system to continue calling the pending handlers in the chain.
|
||||||
// Let's say you have an authorization middleware that validates if the request is authorized
|
// Let's say you have an authorization middleware that validates if the request is authorized
|
||||||
// if the authorization fails (the password does not match). This method (Abort()) should be called
|
// if the authorization fails (the password does not match). This method (Abort()) should be called
|
||||||
// in order to stop the execution of the actual handler.
|
// in order to stop the execution of the actual handler.
|
||||||
func (c *Context) Abort() {
|
func (c *Context) Abort() {
|
||||||
c.index = AbortIndex
|
c.index = abortIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// It calls Abort() and writes the headers with the specified status code.
|
// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
|
||||||
// For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401).
|
// For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401).
|
||||||
func (c *Context) AbortWithStatus(code int) {
|
func (c *Context) AbortWithStatus(code int) {
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// It calls AbortWithStatus() and Error() internally. This method stops the chain, writes the status code and
|
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and
|
||||||
// pushes the specified error to `c.Errors`.
|
// pushes the specified error to `c.Errors`.
|
||||||
// See Context.Error() for more details.
|
// See Context.Error() for more details.
|
||||||
func (c *Context) AbortWithError(code int, err error) *Error {
|
func (c *Context) AbortWithError(code int, err error) *Error {
|
||||||
|
@ -371,7 +374,7 @@ func (c *Context) Redirect(code int, location string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes some data into the body stream and updates the HTTP code.
|
// Data writes some data into the body stream and updates the HTTP code.
|
||||||
func (c *Context) Data(code int, contentType string, data []byte) {
|
func (c *Context) Data(code int, contentType string, data []byte) {
|
||||||
c.Render(code, render.Data{
|
c.Render(code, render.Data{
|
||||||
ContentType: contentType,
|
ContentType: contentType,
|
||||||
|
@ -379,11 +382,12 @@ func (c *Context) Data(code int, contentType string, data []byte) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes the specified file into the body stream in a efficient way.
|
// File writes the specified file into the body stream in a efficient way.
|
||||||
func (c *Context) File(filepath string) {
|
func (c *Context) File(filepath string) {
|
||||||
http.ServeFile(c.Writer, c.Request, filepath)
|
http.ServeFile(c.Writer, c.Request, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSEvent writes a Server-Sent Event into the body stream.
|
||||||
func (c *Context) SSEvent(name string, message interface{}) {
|
func (c *Context) SSEvent(name string, message interface{}) {
|
||||||
c.Render(-1, sse.Event{
|
c.Render(-1, sse.Event{
|
||||||
Event: name,
|
Event: name,
|
||||||
|
|
|
@ -129,7 +129,7 @@ func TestContextCopy(t *testing.T) {
|
||||||
assert.Nil(t, cp.writermem.ResponseWriter)
|
assert.Nil(t, cp.writermem.ResponseWriter)
|
||||||
assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
|
assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
|
||||||
assert.Equal(t, cp.Request, c.Request)
|
assert.Equal(t, cp.Request, c.Request)
|
||||||
assert.Equal(t, cp.index, AbortIndex)
|
assert.Equal(t, cp.index, abortIndex)
|
||||||
assert.Equal(t, cp.Keys, c.Keys)
|
assert.Equal(t, cp.Keys, c.Keys)
|
||||||
assert.Equal(t, cp.engine, c.engine)
|
assert.Equal(t, cp.engine, c.engine)
|
||||||
assert.Equal(t, cp.Params, c.Params)
|
assert.Equal(t, cp.Params, c.Params)
|
||||||
|
@ -418,7 +418,7 @@ func TestContextIsAborted(t *testing.T) {
|
||||||
c.Next()
|
c.Next()
|
||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
|
|
||||||
c.Next()
|
c.index++
|
||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +430,7 @@ func TestContextAbortWithStatus(t *testing.T) {
|
||||||
c.AbortWithStatus(401)
|
c.AbortWithStatus(401)
|
||||||
c.Writer.WriteHeaderNow()
|
c.Writer.WriteHeaderNow()
|
||||||
|
|
||||||
assert.Equal(t, c.index, AbortIndex)
|
assert.Equal(t, c.index, abortIndex)
|
||||||
assert.Equal(t, c.Writer.Status(), 401)
|
assert.Equal(t, c.Writer.Status(), 401)
|
||||||
assert.Equal(t, w.Code, 401)
|
assert.Equal(t, w.Code, 401)
|
||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
|
@ -482,7 +482,7 @@ func TestContextAbortWithError(t *testing.T) {
|
||||||
c.Writer.WriteHeaderNow()
|
c.Writer.WriteHeaderNow()
|
||||||
|
|
||||||
assert.Equal(t, w.Code, 401)
|
assert.Equal(t, w.Code, 401)
|
||||||
assert.Equal(t, c.index, AbortIndex)
|
assert.Equal(t, c.index, abortIndex)
|
||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
debug.go
7
debug.go
|
@ -9,6 +9,9 @@ import "log"
|
||||||
func init() {
|
func init() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
|
// Use SetMode(gin.Release) to switch to disable the debug mode.
|
||||||
func IsDebugging() bool {
|
func IsDebugging() bool {
|
||||||
return ginMode == debugCode
|
return ginMode == debugCode
|
||||||
}
|
}
|
||||||
|
@ -27,7 +30,7 @@ func debugPrint(format string, values ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugPrintWARNING_New() {
|
func debugPrintWARNINGNew() {
|
||||||
debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
||||||
- using env: export GIN_MODE=release
|
- using env: export GIN_MODE=release
|
||||||
- using code: gin.SetMode(gin.ReleaseMode)
|
- using code: gin.SetMode(gin.ReleaseMode)
|
||||||
|
@ -35,7 +38,7 @@ func debugPrintWARNING_New() {
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugPrintWARNING_SetHTMLTemplate() {
|
func debugPrintWARNINGSetHTMLTemplate() {
|
||||||
debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called
|
debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called
|
||||||
at initialization. ie. before any route is registered or the router is listening in a socket:
|
at initialization. ie. before any route is registered or the router is listening in a socket:
|
||||||
|
|
||||||
|
|
33
gin.go
33
gin.go
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Framework's version
|
||||||
const Version = "v1.0rc2"
|
const Version = "v1.0rc2"
|
||||||
|
|
||||||
var default404Body = []byte("404 page not found")
|
var default404Body = []byte("404 page not found")
|
||||||
|
@ -22,6 +23,7 @@ var default405Body = []byte("405 method not allowed")
|
||||||
type HandlerFunc func(*Context)
|
type HandlerFunc func(*Context)
|
||||||
type HandlersChain []HandlerFunc
|
type HandlersChain []HandlerFunc
|
||||||
|
|
||||||
|
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
||||||
func (c HandlersChain) Last() HandlerFunc {
|
func (c HandlersChain) Last() HandlerFunc {
|
||||||
length := len(c)
|
length := len(c)
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
|
@ -38,7 +40,8 @@ type (
|
||||||
Handler string
|
Handler string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
|
// Engine is the framework's instance, it contains the muxer, middlewares and configuration settings.
|
||||||
|
// Create an instance of Engine, by using New() or Default()
|
||||||
Engine struct {
|
Engine struct {
|
||||||
RouterGroup
|
RouterGroup
|
||||||
HTMLRender render.HTMLRender
|
HTMLRender render.HTMLRender
|
||||||
|
@ -78,12 +81,16 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ RoutesInterface = &Engine{}
|
var _ IRouter = &Engine{}
|
||||||
|
|
||||||
// Returns a new blank Engine instance without any middleware attached.
|
// New returns a new blank Engine instance without any middleware attached.
|
||||||
// The most basic configuration
|
// By default the configuration is:
|
||||||
|
// - RedirectTrailingSlash: true
|
||||||
|
// - RedirectFixedPath: false
|
||||||
|
// - HandleMethodNotAllowed: false
|
||||||
|
// - ForwardedByClientIP: true
|
||||||
func New() *Engine {
|
func New() *Engine {
|
||||||
debugPrintWARNING_New()
|
debugPrintWARNINGNew()
|
||||||
engine := &Engine{
|
engine := &Engine{
|
||||||
RouterGroup: RouterGroup{
|
RouterGroup: RouterGroup{
|
||||||
Handlers: nil,
|
Handlers: nil,
|
||||||
|
@ -103,7 +110,7 @@ func New() *Engine {
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a Engine instance with the Logger and Recovery already attached.
|
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||||
func Default() *Engine {
|
func Default() *Engine {
|
||||||
engine := New()
|
engine := New()
|
||||||
engine.Use(Recovery(), Logger())
|
engine.Use(Recovery(), Logger())
|
||||||
|
@ -134,7 +141,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||||
|
|
||||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
||||||
if len(engine.trees) > 0 {
|
if len(engine.trees) > 0 {
|
||||||
debugPrintWARNING_SetHTMLTemplate()
|
debugPrintWARNINGSetHTMLTemplate()
|
||||||
}
|
}
|
||||||
engine.HTMLRender = render.HTMLProduction{Template: templ}
|
engine.HTMLRender = render.HTMLProduction{Template: templ}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +161,7 @@ func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
||||||
// Attachs a global middleware to the router. ie. the middlewares attached though Use() will be
|
// Attachs a global middleware to the router. ie. the middlewares attached though Use() will be
|
||||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
// 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.
|
// For example, this is the right place for a logger or error management middleware.
|
||||||
func (engine *Engine) Use(middlewares ...HandlerFunc) routesInterface {
|
func (engine *Engine) Use(middlewares ...HandlerFunc) IRoutes {
|
||||||
engine.RouterGroup.Use(middlewares...)
|
engine.RouterGroup.Use(middlewares...)
|
||||||
engine.rebuild404Handlers()
|
engine.rebuild404Handlers()
|
||||||
engine.rebuild405Handlers()
|
engine.rebuild405Handlers()
|
||||||
|
@ -193,6 +200,8 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
||||||
root.addRoute(path, handlers)
|
root.addRoute(path, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routes returns a slice of registered routes, including some useful information, such as:
|
||||||
|
// the http method, path and the handler name.
|
||||||
func (engine *Engine) Routes() (routes RoutesInfo) {
|
func (engine *Engine) Routes() (routes RoutesInfo) {
|
||||||
for _, tree := range engine.trees {
|
for _, tree := range engine.trees {
|
||||||
routes = iterate("", tree.method, routes, tree.root)
|
routes = iterate("", tree.method, routes, tree.root)
|
||||||
|
@ -215,7 +224,7 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// The router is attached to a http.Server and starts listening and serving HTTP requests.
|
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
|
||||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
// It is a shortcut for http.ListenAndServe(addr, router)
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
||||||
func (engine *Engine) Run(addr string) (err error) {
|
func (engine *Engine) Run(addr string) (err error) {
|
||||||
|
@ -226,7 +235,7 @@ func (engine *Engine) Run(addr string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// The router is attached to a http.Server and starts listening and serving HTTPS requests.
|
// 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)
|
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
||||||
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
|
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
|
||||||
|
@ -237,8 +246,8 @@ func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// The router is attached to a http.Server and starts listening and serving HTTP requests
|
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
|
||||||
// through the specified unix socket (ie. a file)
|
// through the specified unix socket (ie. a file).
|
||||||
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
// Note: this method will block the calling goroutine undefinitelly unless an error happens.
|
||||||
func (engine *Engine) RunUnix(file string) (err error) {
|
func (engine *Engine) RunUnix(file string) (err error) {
|
||||||
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
||||||
|
|
|
@ -23,10 +23,20 @@ type (
|
||||||
http.Flusher
|
http.Flusher
|
||||||
http.CloseNotifier
|
http.CloseNotifier
|
||||||
|
|
||||||
|
// Returns the HTTP response status code of the current request.
|
||||||
Status() int
|
Status() int
|
||||||
|
|
||||||
|
// Returns the number of bytes already written into the response http body.
|
||||||
|
// See Written()
|
||||||
Size() int
|
Size() int
|
||||||
|
|
||||||
|
// Writes the string into the response body.
|
||||||
WriteString(string) (int, error)
|
WriteString(string) (int, error)
|
||||||
|
|
||||||
|
// Returns true if the response body was already written.
|
||||||
Written() bool
|
Written() bool
|
||||||
|
|
||||||
|
// Forces to write the http header (status code + headers).
|
||||||
WriteHeaderNow()
|
WriteHeaderNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,30 +12,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
RoutesInterface interface {
|
IRouter interface {
|
||||||
routesInterface
|
IRoutes
|
||||||
Group(string, ...HandlerFunc) *RouterGroup
|
Group(string, ...HandlerFunc) *RouterGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
routesInterface interface {
|
IRoutes interface {
|
||||||
Use(...HandlerFunc) routesInterface
|
Use(...HandlerFunc) IRoutes
|
||||||
|
|
||||||
Handle(string, string, ...HandlerFunc) routesInterface
|
Handle(string, string, ...HandlerFunc) IRoutes
|
||||||
Any(string, ...HandlerFunc) routesInterface
|
Any(string, ...HandlerFunc) IRoutes
|
||||||
GET(string, ...HandlerFunc) routesInterface
|
GET(string, ...HandlerFunc) IRoutes
|
||||||
POST(string, ...HandlerFunc) routesInterface
|
POST(string, ...HandlerFunc) IRoutes
|
||||||
DELETE(string, ...HandlerFunc) routesInterface
|
DELETE(string, ...HandlerFunc) IRoutes
|
||||||
PATCH(string, ...HandlerFunc) routesInterface
|
PATCH(string, ...HandlerFunc) IRoutes
|
||||||
PUT(string, ...HandlerFunc) routesInterface
|
PUT(string, ...HandlerFunc) IRoutes
|
||||||
OPTIONS(string, ...HandlerFunc) routesInterface
|
OPTIONS(string, ...HandlerFunc) IRoutes
|
||||||
HEAD(string, ...HandlerFunc) routesInterface
|
HEAD(string, ...HandlerFunc) IRoutes
|
||||||
|
|
||||||
StaticFile(string, string) routesInterface
|
StaticFile(string, string) IRoutes
|
||||||
Static(string, string) routesInterface
|
Static(string, string) IRoutes
|
||||||
StaticFS(string, http.FileSystem) routesInterface
|
StaticFS(string, http.FileSystem) IRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used internally to configure router, a RouterGroup is associated with a prefix
|
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
||||||
// and an array of handlers (middlewares)
|
// and an array of handlers (middlewares)
|
||||||
RouterGroup struct {
|
RouterGroup struct {
|
||||||
Handlers HandlersChain
|
Handlers HandlersChain
|
||||||
|
@ -45,15 +45,15 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ RoutesInterface = &RouterGroup{}
|
var _ IRouter = &RouterGroup{}
|
||||||
|
|
||||||
// Adds middlewares to the group, see example code in github.
|
// Use adds middlewares to the group, see example code in github.
|
||||||
func (group *RouterGroup) Use(middlewares ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) Use(middlewares ...HandlerFunc) IRoutes {
|
||||||
group.Handlers = append(group.Handlers, middlewares...)
|
group.Handlers = append(group.Handlers, middlewares...)
|
||||||
return group.returnObj()
|
return group.returnObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new router group. You should add all the routes that have common middlwares or the same path prefix.
|
// Group 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.
|
// For example, all the routes that use a common middlware for authorization could be grouped.
|
||||||
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
|
||||||
return &RouterGroup{
|
return &RouterGroup{
|
||||||
|
@ -63,6 +63,13 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
|
||||||
|
absolutePath := group.calculateAbsolutePath(relativePath)
|
||||||
|
handlers = group.combineHandlers(handlers)
|
||||||
|
group.engine.addRoute(httpMethod, absolutePath, handlers)
|
||||||
|
return group.returnObj()
|
||||||
|
}
|
||||||
|
|
||||||
// Handle registers a new request handle and middlewares with the given path and method.
|
// 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.
|
// 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.
|
// See the example code in github.
|
||||||
|
@ -73,14 +80,7 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
|
||||||
// This function is intended for bulk loading and to allow the usage of less
|
// 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
|
// frequently used, non-standardized or custom methods (e.g. for internal
|
||||||
// communication with a proxy).
|
// communication with a proxy).
|
||||||
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) routesInterface {
|
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
absolutePath := group.calculateAbsolutePath(relativePath)
|
|
||||||
handlers = group.combineHandlers(handlers)
|
|
||||||
group.engine.addRoute(httpMethod, absolutePath, handlers)
|
|
||||||
return group.returnObj()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) routesInterface {
|
|
||||||
if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
|
if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
|
||||||
panic("http method " + httpMethod + " is not valid")
|
panic("http method " + httpMethod + " is not valid")
|
||||||
}
|
}
|
||||||
|
@ -88,42 +88,43 @@ func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...Ha
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
// POST is a shortcut for router.Handle("POST", path, handle)
|
||||||
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("POST", relativePath, handlers)
|
return group.handle("POST", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
// GET is a shortcut for router.Handle("GET", path, handle)
|
||||||
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("GET", relativePath, handlers)
|
return group.handle("GET", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
||||||
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("DELETE", relativePath, handlers)
|
return group.handle("DELETE", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
||||||
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("PATCH", relativePath, handlers)
|
return group.handle("PATCH", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
||||||
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("PUT", relativePath, handlers)
|
return group.handle("PUT", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
||||||
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("OPTIONS", relativePath, handlers)
|
return group.handle("OPTIONS", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
||||||
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) routesInterface {
|
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("HEAD", relativePath, handlers)
|
return group.handle("HEAD", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) routesInterface {
|
// Any registers a route that matches all the HTTP methods.
|
||||||
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE
|
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE
|
||||||
|
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
group.handle("GET", relativePath, handlers)
|
group.handle("GET", relativePath, handlers)
|
||||||
group.handle("POST", relativePath, handlers)
|
group.handle("POST", relativePath, handlers)
|
||||||
group.handle("PUT", relativePath, handlers)
|
group.handle("PUT", relativePath, handlers)
|
||||||
|
@ -136,7 +137,9 @@ func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) rout
|
||||||
return group.returnObj()
|
return group.returnObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (group *RouterGroup) StaticFile(relativePath, filepath string) routesInterface {
|
// StaticFile registers a single route in order to server a single file of the local filesystem.
|
||||||
|
// router.StaticFile("favicon.ico", "./resources/favicon.ico")
|
||||||
|
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
|
||||||
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
||||||
panic("URL parameters can not be used when serving a static file")
|
panic("URL parameters can not be used when serving a static file")
|
||||||
}
|
}
|
||||||
|
@ -154,11 +157,13 @@ func (group *RouterGroup) StaticFile(relativePath, filepath string) routesInterf
|
||||||
// To use the operating system's file system implementation,
|
// To use the operating system's file system implementation,
|
||||||
// use :
|
// use :
|
||||||
// router.Static("/static", "/var/www")
|
// router.Static("/static", "/var/www")
|
||||||
func (group *RouterGroup) Static(relativePath, root string) routesInterface {
|
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
|
||||||
return group.StaticFS(relativePath, Dir(root, false))
|
return group.StaticFS(relativePath, Dir(root, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) routesInterface {
|
// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
|
||||||
|
// Gin by default user: gin.Dir()
|
||||||
|
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
|
||||||
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
||||||
panic("URL parameters can not be used when serving a static folder")
|
panic("URL parameters can not be used when serving a static folder")
|
||||||
}
|
}
|
||||||
|
@ -185,7 +190,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS
|
||||||
|
|
||||||
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
|
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
|
||||||
finalSize := len(group.Handlers) + len(handlers)
|
finalSize := len(group.Handlers) + len(handlers)
|
||||||
if finalSize >= int(AbortIndex) {
|
if finalSize >= int(abortIndex) {
|
||||||
panic("too many handlers")
|
panic("too many handlers")
|
||||||
}
|
}
|
||||||
mergedHandlers := make(HandlersChain, finalSize)
|
mergedHandlers := make(HandlersChain, finalSize)
|
||||||
|
@ -198,10 +203,9 @@ func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
|
||||||
return joinPaths(group.BasePath, relativePath)
|
return joinPaths(group.BasePath, relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (group *RouterGroup) returnObj() routesInterface {
|
func (group *RouterGroup) returnObj() IRoutes {
|
||||||
if group.root {
|
if group.root {
|
||||||
return group.engine
|
return group.engine
|
||||||
} else {
|
|
||||||
return group
|
|
||||||
}
|
}
|
||||||
|
return group
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ func TestRouterGroupPipeline(t *testing.T) {
|
||||||
testRoutesInterface(t, v1)
|
testRoutesInterface(t, v1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRoutesInterface(t *testing.T, r RoutesInterface) {
|
func testRoutesInterface(t *testing.T, r IRoutes) {
|
||||||
handler := func(c *Context) {}
|
handler := func(c *Context) {}
|
||||||
assert.Equal(t, r, r.Use(handler))
|
assert.Equal(t, r, r.Use(handler))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue