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

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

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

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

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

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

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

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

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

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

Read our documentation on viewing source code .

Loading