...

Source file src/github.com/gin-gonic/gin/routergroup.go

Documentation: github.com/gin-gonic/gin

     1  // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
     2  // Use of this source code is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gin
     6  
     7  import (
     8  	"net/http"
     9  	"path"
    10  	"regexp"
    11  	"strings"
    12  )
    13  
    14  var (
    15  	// regEnLetter matches english letters for http method name
    16  	regEnLetter = regexp.MustCompile("^[A-Z]+$")
    17  
    18  	// anyMethods for RouterGroup Any method
    19  	anyMethods = []string{
    20  		http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
    21  		http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
    22  		http.MethodTrace,
    23  	}
    24  )
    25  
    26  // IRouter defines all router handle interface includes single and group router.
    27  type IRouter interface {
    28  	IRoutes
    29  	Group(string, ...HandlerFunc) *RouterGroup
    30  }
    31  
    32  // IRoutes defines all router handle interface.
    33  type IRoutes interface {
    34  	Use(...HandlerFunc) IRoutes
    35  
    36  	Handle(string, string, ...HandlerFunc) IRoutes
    37  	Any(string, ...HandlerFunc) IRoutes
    38  	GET(string, ...HandlerFunc) IRoutes
    39  	POST(string, ...HandlerFunc) IRoutes
    40  	DELETE(string, ...HandlerFunc) IRoutes
    41  	PATCH(string, ...HandlerFunc) IRoutes
    42  	PUT(string, ...HandlerFunc) IRoutes
    43  	OPTIONS(string, ...HandlerFunc) IRoutes
    44  	HEAD(string, ...HandlerFunc) IRoutes
    45  	Match([]string, string, ...HandlerFunc) IRoutes
    46  
    47  	StaticFile(string, string) IRoutes
    48  	StaticFileFS(string, string, http.FileSystem) IRoutes
    49  	Static(string, string) IRoutes
    50  	StaticFS(string, http.FileSystem) IRoutes
    51  }
    52  
    53  // RouterGroup is used internally to configure router, a RouterGroup is associated with
    54  // a prefix and an array of handlers (middleware).
    55  type RouterGroup struct {
    56  	Handlers HandlersChain
    57  	basePath string
    58  	engine   *Engine
    59  	root     bool
    60  }
    61  
    62  var _ IRouter = (*RouterGroup)(nil)
    63  
    64  // Use adds middleware to the group, see example code in GitHub.
    65  func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    66  	group.Handlers = append(group.Handlers, middleware...)
    67  	return group.returnObj()
    68  }
    69  
    70  // Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
    71  // For example, all the routes that use a common middleware for authorization could be grouped.
    72  func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
    73  	return &RouterGroup{
    74  		Handlers: group.combineHandlers(handlers),
    75  		basePath: group.calculateAbsolutePath(relativePath),
    76  		engine:   group.engine,
    77  	}
    78  }
    79  
    80  // BasePath returns the base path of router group.
    81  // For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
    82  func (group *RouterGroup) BasePath() string {
    83  	return group.basePath
    84  }
    85  
    86  func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    87  	absolutePath := group.calculateAbsolutePath(relativePath)
    88  	handlers = group.combineHandlers(handlers)
    89  	group.engine.addRoute(httpMethod, absolutePath, handlers)
    90  	return group.returnObj()
    91  }
    92  
    93  // Handle registers a new request handle and middleware with the given path and method.
    94  // The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
    95  // See the example code in GitHub.
    96  //
    97  // For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
    98  // functions can be used.
    99  //
   100  // This function is intended for bulk loading and to allow the usage of less
   101  // frequently used, non-standardized or custom methods (e.g. for internal
   102  // communication with a proxy).
   103  func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
   104  	if matched := regEnLetter.MatchString(httpMethod); !matched {
   105  		panic("http method " + httpMethod + " is not valid")
   106  	}
   107  	return group.handle(httpMethod, relativePath, handlers)
   108  }
   109  
   110  // POST is a shortcut for router.Handle("POST", path, handlers).
   111  func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
   112  	return group.handle(http.MethodPost, relativePath, handlers)
   113  }
   114  
   115  // GET is a shortcut for router.Handle("GET", path, handlers).
   116  func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
   117  	return group.handle(http.MethodGet, relativePath, handlers)
   118  }
   119  
   120  // DELETE is a shortcut for router.Handle("DELETE", path, handlers).
   121  func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
   122  	return group.handle(http.MethodDelete, relativePath, handlers)
   123  }
   124  
   125  // PATCH is a shortcut for router.Handle("PATCH", path, handlers).
   126  func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
   127  	return group.handle(http.MethodPatch, relativePath, handlers)
   128  }
   129  
   130  // PUT is a shortcut for router.Handle("PUT", path, handlers).
   131  func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
   132  	return group.handle(http.MethodPut, relativePath, handlers)
   133  }
   134  
   135  // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers).
   136  func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
   137  	return group.handle(http.MethodOptions, relativePath, handlers)
   138  }
   139  
   140  // HEAD is a shortcut for router.Handle("HEAD", path, handlers).
   141  func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
   142  	return group.handle(http.MethodHead, relativePath, handlers)
   143  }
   144  
   145  // Any registers a route that matches all the HTTP methods.
   146  // GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
   147  func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
   148  	for _, method := range anyMethods {
   149  		group.handle(method, relativePath, handlers)
   150  	}
   151  
   152  	return group.returnObj()
   153  }
   154  
   155  // Match registers a route that matches the specified methods that you declared.
   156  func (group *RouterGroup) Match(methods []string, relativePath string, handlers ...HandlerFunc) IRoutes {
   157  	for _, method := range methods {
   158  		group.handle(method, relativePath, handlers)
   159  	}
   160  
   161  	return group.returnObj()
   162  }
   163  
   164  // StaticFile registers a single route in order to serve a single file of the local filesystem.
   165  // router.StaticFile("favicon.ico", "./resources/favicon.ico")
   166  func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
   167  	return group.staticFileHandler(relativePath, func(c *Context) {
   168  		c.File(filepath)
   169  	})
   170  }
   171  
   172  // StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead..
   173  // router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false})
   174  // Gin by default uses: gin.Dir()
   175  func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes {
   176  	return group.staticFileHandler(relativePath, func(c *Context) {
   177  		c.FileFromFS(filepath, fs)
   178  	})
   179  }
   180  
   181  func (group *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes {
   182  	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
   183  		panic("URL parameters can not be used when serving a static file")
   184  	}
   185  	group.GET(relativePath, handler)
   186  	group.HEAD(relativePath, handler)
   187  	return group.returnObj()
   188  }
   189  
   190  // Static serves files from the given file system root.
   191  // Internally a http.FileServer is used, therefore http.NotFound is used instead
   192  // of the Router's NotFound handler.
   193  // To use the operating system's file system implementation,
   194  // use :
   195  //
   196  //	router.Static("/static", "/var/www")
   197  func (group *RouterGroup) Static(relativePath, root string) IRoutes {
   198  	return group.StaticFS(relativePath, Dir(root, false))
   199  }
   200  
   201  // StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
   202  // Gin by default uses: gin.Dir()
   203  func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
   204  	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
   205  		panic("URL parameters can not be used when serving a static folder")
   206  	}
   207  	handler := group.createStaticHandler(relativePath, fs)
   208  	urlPattern := path.Join(relativePath, "/*filepath")
   209  
   210  	// Register GET and HEAD handlers
   211  	group.GET(urlPattern, handler)
   212  	group.HEAD(urlPattern, handler)
   213  	return group.returnObj()
   214  }
   215  
   216  func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
   217  	absolutePath := group.calculateAbsolutePath(relativePath)
   218  	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
   219  
   220  	return func(c *Context) {
   221  		if _, noListing := fs.(*onlyFilesFS); noListing {
   222  			c.Writer.WriteHeader(http.StatusNotFound)
   223  		}
   224  
   225  		file := c.Param("filepath")
   226  		// Check if file exists and/or if we have permission to access it
   227  		f, err := fs.Open(file)
   228  		if err != nil {
   229  			c.Writer.WriteHeader(http.StatusNotFound)
   230  			c.handlers = group.engine.noRoute
   231  			// Reset index
   232  			c.index = -1
   233  			return
   234  		}
   235  		f.Close()
   236  
   237  		fileServer.ServeHTTP(c.Writer, c.Request)
   238  	}
   239  }
   240  
   241  func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
   242  	finalSize := len(group.Handlers) + len(handlers)
   243  	assert1(finalSize < int(abortIndex), "too many handlers")
   244  	mergedHandlers := make(HandlersChain, finalSize)
   245  	copy(mergedHandlers, group.Handlers)
   246  	copy(mergedHandlers[len(group.Handlers):], handlers)
   247  	return mergedHandlers
   248  }
   249  
   250  func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
   251  	return joinPaths(group.basePath, relativePath)
   252  }
   253  
   254  func (group *RouterGroup) returnObj() IRoutes {
   255  	if group.root {
   256  		return group.engine
   257  	}
   258  	return group
   259  }
   260  

View as plain text