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 5package gin 6 7import ( 8 "net/http" 9 "path" 10 "regexp" 11 "strings" 12) 13 14type IRouter interface { 15 IRoutes 16 Group(string, ...HandlerFunc) *RouterGroup 17} 18 19type IRoutes interface { 20 Use(...HandlerFunc) IRoutes 21 22 Handle(string, string, ...HandlerFunc) IRoutes 23 Any(string, ...HandlerFunc) IRoutes 24 GET(string, ...HandlerFunc) IRoutes 25 POST(string, ...HandlerFunc) IRoutes 26 DELETE(string, ...HandlerFunc) IRoutes 27 PATCH(string, ...HandlerFunc) IRoutes 28 PUT(string, ...HandlerFunc) IRoutes 29 OPTIONS(string, ...HandlerFunc) IRoutes 30 HEAD(string, ...HandlerFunc) IRoutes 31 32 StaticFile(string, string) IRoutes 33 Static(string, string) IRoutes 34 StaticFS(string, http.FileSystem) IRoutes 35} 36 37// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix 38// and an array of handlers (middleware). 39type RouterGroup struct { 40 Handlers HandlersChain 41 basePath string 42 engine *Engine 43 root bool 44} 45 46var _ IRouter = &RouterGroup{} 47 48// Use adds middleware to the group, see example code in github. 49func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { 50 group.Handlers = append(group.Handlers, middleware...) 51 return group.returnObj() 52} 53 54// Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix. 55// For example, all the routes that use a common middlware for authorization could be grouped. 56func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup { 57 return &RouterGroup{ 58 Handlers: group.combineHandlers(handlers), 59 basePath: group.calculateAbsolutePath(relativePath), 60 engine: group.engine, 61 } 62} 63 64func (group *RouterGroup) BasePath() string { 65 return group.basePath 66} 67 68func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { 69 absolutePath := group.calculateAbsolutePath(relativePath) 70 handlers = group.combineHandlers(handlers) 71 group.engine.addRoute(httpMethod, absolutePath, handlers) 72 return group.returnObj() 73} 74 75// Handle registers a new request handle and middleware with the given path and method. 76// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes. 77// See the example code in github. 78// 79// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut 80// functions can be used. 81// 82// This function is intended for bulk loading and to allow the usage of less 83// frequently used, non-standardized or custom methods (e.g. for internal 84// communication with a proxy). 85func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes { 86 if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil { 87 panic("http method " + httpMethod + " is not valid") 88 } 89 return group.handle(httpMethod, relativePath, handlers) 90} 91 92// POST is a shortcut for router.Handle("POST", path, handle). 93func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes { 94 return group.handle("POST", relativePath, handlers) 95} 96 97// GET is a shortcut for router.Handle("GET", path, handle). 98func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { 99 return group.handle("GET", relativePath, handlers) 100} 101 102// DELETE is a shortcut for router.Handle("DELETE", path, handle). 103func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { 104 return group.handle("DELETE", relativePath, handlers) 105} 106 107// PATCH is a shortcut for router.Handle("PATCH", path, handle). 108func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { 109 return group.handle("PATCH", relativePath, handlers) 110} 111 112// PUT is a shortcut for router.Handle("PUT", path, handle). 113func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes { 114 return group.handle("PUT", relativePath, handlers) 115} 116 117// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle). 118func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { 119 return group.handle("OPTIONS", relativePath, handlers) 120} 121 122// HEAD is a shortcut for router.Handle("HEAD", path, handle). 123func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { 124 return group.handle("HEAD", relativePath, handlers) 125} 126 127// Any registers a route that matches all the HTTP methods. 128// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. 129func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { 130 group.handle("GET", relativePath, handlers) 131 group.handle("POST", relativePath, handlers) 132 group.handle("PUT", relativePath, handlers) 133 group.handle("PATCH", relativePath, handlers) 134 group.handle("HEAD", relativePath, handlers) 135 group.handle("OPTIONS", relativePath, handlers) 136 group.handle("DELETE", relativePath, handlers) 137 group.handle("CONNECT", relativePath, handlers) 138 group.handle("TRACE", relativePath, handlers) 139 return group.returnObj() 140} 141 142// StaticFile registers a single route in order to serve a single file of the local filesystem. 143// router.StaticFile("favicon.ico", "./resources/favicon.ico") 144func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { 145 if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { 146 panic("URL parameters can not be used when serving a static file") 147 } 148 handler := func(c *Context) { 149 c.File(filepath) 150 } 151 group.GET(relativePath, handler) 152 group.HEAD(relativePath, handler) 153 return group.returnObj() 154} 155 156// Static serves files from the given file system root. 157// Internally a http.FileServer is used, therefore http.NotFound is used instead 158// of the Router's NotFound handler. 159// To use the operating system's file system implementation, 160// use : 161// router.Static("/static", "/var/www") 162func (group *RouterGroup) Static(relativePath, root string) IRoutes { 163 return group.StaticFS(relativePath, Dir(root, false)) 164} 165 166// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead. 167// Gin by default user: gin.Dir() 168func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes { 169 if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { 170 panic("URL parameters can not be used when serving a static folder") 171 } 172 handler := group.createStaticHandler(relativePath, fs) 173 urlPattern := path.Join(relativePath, "/*filepath") 174 175 // Register GET and HEAD handlers 176 group.GET(urlPattern, handler) 177 group.HEAD(urlPattern, handler) 178 return group.returnObj() 179} 180 181func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc { 182 absolutePath := group.calculateAbsolutePath(relativePath) 183 fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) 184 _, nolisting := fs.(*onlyfilesFS) 185 return func(c *Context) { 186 if nolisting { 187 c.Writer.WriteHeader(http.StatusNotFound) 188 } 189 fileServer.ServeHTTP(c.Writer, c.Request) 190 } 191} 192 193func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { 194 finalSize := len(group.Handlers) + len(handlers) 195 if finalSize >= int(abortIndex) { 196 panic("too many handlers") 197 } 198 mergedHandlers := make(HandlersChain, finalSize) 199 copy(mergedHandlers, group.Handlers) 200 copy(mergedHandlers[len(group.Handlers):], handlers) 201 return mergedHandlers 202} 203 204func (group *RouterGroup) calculateAbsolutePath(relativePath string) string { 205 return joinPaths(group.basePath, relativePath) 206} 207 208func (group *RouterGroup) returnObj() IRoutes { 209 if group.root { 210 return group.engine 211 } 212 return group 213} 214