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
// 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 8
	group.Handlers = append(group.Handlers, middleware...)
53 8
	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 8
	return &RouterGroup{
60 8
		Handlers: group.combineHandlers(handlers),
61 8
		basePath: group.calculateAbsolutePath(relativePath),
62 8
		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 8
	return group.basePath
70
}
71

72
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
73 8
	absolutePath := group.calculateAbsolutePath(relativePath)
74 8
	handlers = group.combineHandlers(handlers)
75 8
	group.engine.addRoute(httpMethod, absolutePath, handlers)
76 8
	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 8
	if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
91 8
		panic("http method " + httpMethod + " is not valid")
92
	}
93 8
	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 8
	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 8
	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 8
	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 8
	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 8
	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 8
	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 8
	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 8
	group.handle(http.MethodGet, relativePath, handlers)
135 8
	group.handle(http.MethodPost, relativePath, handlers)
136 8
	group.handle(http.MethodPut, relativePath, handlers)
137 8
	group.handle(http.MethodPatch, relativePath, handlers)
138 8
	group.handle(http.MethodHead, relativePath, handlers)
139 8
	group.handle(http.MethodOptions, relativePath, handlers)
140 8
	group.handle(http.MethodDelete, relativePath, handlers)
141 8
	group.handle(http.MethodConnect, relativePath, handlers)
142 8
	group.handle(http.MethodTrace, relativePath, handlers)
143 8
	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 8
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
150 8
		panic("URL parameters can not be used when serving a static file")
151
	}
152 8
	handler := func(c *Context) {
153 8
		c.File(filepath)
154
	}
155 8
	group.GET(relativePath, handler)
156 8
	group.HEAD(relativePath, handler)
157 8
	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 8
	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 8
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
174 8
		panic("URL parameters can not be used when serving a static folder")
175
	}
176 8
	handler := group.createStaticHandler(relativePath, fs)
177 8
	urlPattern := path.Join(relativePath, "/*filepath")
178

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

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

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

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

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

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

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

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

Read our documentation on viewing source code .

Loading