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

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

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

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

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

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

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

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

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

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

Read our documentation on viewing source code .

Loading