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
// IRouter defines all router handle interface includes single and group router.
15
type IRouter interface {
16
	IRoutes
17
	Group(string, ...HandlerFunc) *RouterGroup
18
}
19

20
// IRoutes defines all router handle interface.
21
type IRoutes interface {
22
	Use(...HandlerFunc) IRoutes
23

24
	Handle(string, string, ...HandlerFunc) IRoutes
25
	Any(string, ...HandlerFunc) IRoutes
26
	GET(string, ...HandlerFunc) IRoutes
27
	POST(string, ...HandlerFunc) IRoutes
28
	DELETE(string, ...HandlerFunc) IRoutes
29
	PATCH(string, ...HandlerFunc) IRoutes
30
	PUT(string, ...HandlerFunc) IRoutes
31
	OPTIONS(string, ...HandlerFunc) IRoutes
32
	HEAD(string, ...HandlerFunc) IRoutes
33

34
	StaticFile(string, string) IRoutes
35
	Static(string, string) IRoutes
36
	StaticFS(string, http.FileSystem) IRoutes
37
}
38

39
// RouterGroup is used internally to configure router, a RouterGroup is associated with
40
// a prefix and an array of handlers (middleware).
41
type RouterGroup struct {
42
	Handlers HandlersChain
43
	basePath string
44
	engine   *Engine
45
	root     bool
46
}
47

48
var _ IRouter = &RouterGroup{}
49

50
// Use adds middleware to the group, see example code in GitHub.
51
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
52 14
	group.Handlers = append(group.Handlers, middleware...)
53 14
	return group.returnObj()
54
}
55

56
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
57
// For example, all the routes that use a common middleware for authorization could be grouped.
58
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
59 14
	return &RouterGroup{
60 14
		Handlers: group.combineHandlers(handlers),
61 14
		basePath: group.calculateAbsolutePath(relativePath),
62 14
		engine:   group.engine,
63
	}
64
}
65

66
// BasePath returns the base path of router group.
67
// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
68
func (group *RouterGroup) BasePath() string {
69 14
	return group.basePath
70
}
71

72
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
73 14
	absolutePath := group.calculateAbsolutePath(relativePath)
74 14
	handlers = group.combineHandlers(handlers)
75 14
	group.engine.addRoute(httpMethod, absolutePath, handlers)
76 14
	return group.returnObj()
77
}
78

79
// Handle registers a new request handle and middleware with the given path and method.
80
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
81
// See the example code in GitHub.
82
//
83
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
84
// functions can be used.
85
//
86
// This function is intended for bulk loading and to allow the usage of less
87
// frequently used, non-standardized or custom methods (e.g. for internal
88
// communication with a proxy).
89
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
90 14
	if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
91 14
		panic("http method " + httpMethod + " is not valid")
92
	}
93 14
	return group.handle(httpMethod, relativePath, handlers)
94
}
95

96
// POST is a shortcut for router.Handle("POST", path, handle).
97
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
98 14
	return group.handle(http.MethodPost, relativePath, handlers)
99
}
100

101
// GET is a shortcut for router.Handle("GET", path, handle).
102
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
103 14
	return group.handle(http.MethodGet, relativePath, handlers)
104
}
105

106
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
107
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
108 14
	return group.handle(http.MethodDelete, relativePath, handlers)
109
}
110

111
// PATCH is a shortcut for router.Handle("PATCH", path, handle).
112
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
113 14
	return group.handle(http.MethodPatch, relativePath, handlers)
114
}
115

116
// PUT is a shortcut for router.Handle("PUT", path, handle).
117
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
118 14
	return group.handle(http.MethodPut, relativePath, handlers)
119
}
120

121
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
122
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
123 14
	return group.handle(http.MethodOptions, relativePath, handlers)
124
}
125

126
// HEAD is a shortcut for router.Handle("HEAD", path, handle).
127
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
128 14
	return group.handle(http.MethodHead, relativePath, handlers)
129
}
130

131
// Any registers a route that matches all the HTTP methods.
132
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
133
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
134 14
	group.handle(http.MethodGet, relativePath, handlers)
135 14
	group.handle(http.MethodPost, relativePath, handlers)
136 14
	group.handle(http.MethodPut, relativePath, handlers)
137 14
	group.handle(http.MethodPatch, relativePath, handlers)
138 14
	group.handle(http.MethodHead, relativePath, handlers)
139 14
	group.handle(http.MethodOptions, relativePath, handlers)
140 14
	group.handle(http.MethodDelete, relativePath, handlers)
141 14
	group.handle(http.MethodConnect, relativePath, handlers)
142 14
	group.handle(http.MethodTrace, relativePath, handlers)
143 14
	return group.returnObj()
144
}
145

146
// StaticFile registers a single route in order to serve a single file of the local filesystem.
147
// router.StaticFile("favicon.ico", "./resources/favicon.ico")
148
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
149 14
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
150 14
		panic("URL parameters can not be used when serving a static file")
151
	}
152 14
	handler := func(c *Context) {
153 14
		c.File(filepath)
154
	}
155 14
	group.GET(relativePath, handler)
156 14
	group.HEAD(relativePath, handler)
157 14
	return group.returnObj()
158
}
159

160
// Static serves files from the given file system root.
161
// Internally a http.FileServer is used, therefore http.NotFound is used instead
162
// of the Router's NotFound handler.
163
// To use the operating system's file system implementation,
164
// use :
165
//     router.Static("/static", "/var/www")
166
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
167 14
	return group.StaticFS(relativePath, Dir(root, false))
168
}
169

170
// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
171
// Gin by default user: gin.Dir()
172
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
173 14
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
174 14
		panic("URL parameters can not be used when serving a static folder")
175
	}
176 14
	handler := group.createStaticHandler(relativePath, fs)
177 14
	urlPattern := path.Join(relativePath, "/*filepath")
178

179
	// Register GET and HEAD handlers
180 14
	group.GET(urlPattern, handler)
181 14
	group.HEAD(urlPattern, handler)
182 14
	return group.returnObj()
183
}
184

185
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
186 14
	absolutePath := group.calculateAbsolutePath(relativePath)
187 14
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
188

189 14
	return func(c *Context) {
190 14
		if _, noListing := fs.(*onlyFilesFS); noListing {
191 14
			c.Writer.WriteHeader(http.StatusNotFound)
192
		}
193

194 14
		file := c.Param("filepath")
195
		// Check if file exists and/or if we have permission to access it
196 14
		f, err := fs.Open(file)
197 14
		if err != nil {
198 14
			c.Writer.WriteHeader(http.StatusNotFound)
199 14
			c.handlers = group.engine.noRoute
200
			// Reset index
201 14
			c.index = -1
202 14
			return
203
		}
204 14
		f.Close()
205

206 14
		fileServer.ServeHTTP(c.Writer, c.Request)
207
	}
208
}
209

210
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
211 14
	finalSize := len(group.Handlers) + len(handlers)
212 14
	if finalSize >= int(abortIndex) {
213 14
		panic("too many handlers")
214
	}
215 14
	mergedHandlers := make(HandlersChain, finalSize)
216 14
	copy(mergedHandlers, group.Handlers)
217 14
	copy(mergedHandlers[len(group.Handlers):], handlers)
218 14
	return mergedHandlers
219
}
220

221
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
222 14
	return joinPaths(group.basePath, relativePath)
223
}
224

225
func (group *RouterGroup) returnObj() IRoutes {
226 14
	if group.root {
227 14
		return group.engine
228
	}
229 14
	return group
230
}

Read our documentation on viewing source code .

Loading