router.go 24.6 KB
Newer Older
A
astaxie 已提交
1
// Copyright 2014 beego Author. All Rights Reserved.
A
astaxie 已提交
2
//
A
astaxie 已提交
3 4 5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
A
astaxie 已提交
6
//
A
astaxie 已提交
7
//      http://www.apache.org/licenses/LICENSE-2.0
A
astaxie 已提交
8
//
A
astaxie 已提交
9 10 11 12 13 14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

A
fix bug  
astaxie 已提交
15 16 17
package beego

import (
A
astaxie 已提交
18
	"fmt"
A
fix bug  
astaxie 已提交
19
	"net/http"
A
astaxie 已提交
20
	"os"
A
astaxie 已提交
21
	"path"
A
astaxie 已提交
22
	"path/filepath"
A
fix bug  
astaxie 已提交
23 24
	"reflect"
	"runtime"
A
astaxie 已提交
25
	"strconv"
A
fix bug  
astaxie 已提交
26
	"strings"
27
	"time"
A
astaxie 已提交
28 29 30

	beecontext "github.com/astaxie/beego/context"
	"github.com/astaxie/beego/toolbox"
31
	"github.com/astaxie/beego/utils"
A
fix bug  
astaxie 已提交
32 33
)

A
astaxie 已提交
34
// default filter execution points
35
const (
A
astaxie 已提交
36 37
	BeforeStatic = iota
	BeforeRouter
38 39 40 41 42
	BeforeExec
	AfterExec
	FinishRouter
)

A
astaxie 已提交
43 44 45 46 47 48
const (
	routerTypeBeego = iota
	routerTypeRESTFul
	routerTypeHandler
)

49
var (
A
astaxie 已提交
50
	// HTTPMETHOD list the supported http methods.
A
astaxie 已提交
51 52 53 54 55 56 57 58 59 60 61
	HTTPMETHOD = map[string]string{
		"GET":     "GET",
		"POST":    "POST",
		"PUT":     "PUT",
		"DELETE":  "DELETE",
		"PATCH":   "PATCH",
		"OPTIONS": "OPTIONS",
		"HEAD":    "HEAD",
		"TRACE":   "TRACE",
		"CONNECT": "CONNECT",
	}
A
astaxie 已提交
62
	// these beego.Controller's methods shouldn't reflect to AutoRouter
J
JessonChan 已提交
63 64 65 66 67 68 69
	exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
		"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp",
		"ServeXml", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
		"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
		"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
		"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
		"GetControllerAndAction"}
A
astaxie 已提交
70

A
astaxie 已提交
71 72 73
	urlPlaceholder = "{{placeholder}}"
	// DefaultAccessLogFilter will skip the accesslog if return true
	DefaultAccessLogFilter FilterHandler = &logFilter{}
74
)
A
astaxie 已提交
75

A
astaxie 已提交
76
// FilterHandler is an interface for
A
astaxie 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89
type FilterHandler interface {
	Filter(*beecontext.Context) bool
}

// default log filter static file will not show
type logFilter struct {
}

func (l *logFilter) Filter(ctx *beecontext.Context) bool {
	requestPath := path.Clean(ctx.Input.Request.URL.Path)
	if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
		return true
	}
A
astaxie 已提交
90
	for prefix := range StaticDir {
A
astaxie 已提交
91 92 93 94 95 96 97
		if strings.HasPrefix(requestPath, prefix) {
			return true
		}
	}
	return false
}

A
astaxie 已提交
98
// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
U
unphp 已提交
99 100 101 102
func ExceptMethodAppend(action string) {
	exceptMethod = append(exceptMethod, action)
}

A
fix bug  
astaxie 已提交
103
type controllerInfo struct {
104
	pattern        string
A
fix bug  
astaxie 已提交
105
	controllerType reflect.Type
A
astaxie 已提交
106
	methods        map[string]string
A
astaxie 已提交
107
	handler        http.Handler
J
JessonChan 已提交
108
	runFunction    FilterFunc
A
astaxie 已提交
109
	routerType     int
A
fix bug  
astaxie 已提交
110 111
}

J
JessonChan 已提交
112 113
// ControllerRegister containers registered router rules, controller handlers and filters.
type ControllerRegister struct {
A
astaxie 已提交
114
	routers      map[string]*Tree
A
astaxie 已提交
115 116
	enableFilter bool
	filters      map[int][]*FilterRouter
A
fix bug  
astaxie 已提交
117 118
}

J
JessonChan 已提交
119 120 121
// NewControllerRegister returns a new ControllerRegister.
func NewControllerRegister() *ControllerRegister {
	return &ControllerRegister{
A
astaxie 已提交
122
		routers: make(map[string]*Tree),
A
astaxie 已提交
123
		filters: make(map[int][]*FilterRouter),
A
astaxie 已提交
124
	}
A
fix bug  
astaxie 已提交
125 126
}

J
JessonChan 已提交
127
// Add controller handler and pattern rules to ControllerRegister.
128 129 130 131 132 133 134 135 136
// usage:
//	default methods is the same name as method
//	Add("/user",&UserController{})
//	Add("/api/list",&RestController{},"*:ListFood")
//	Add("/api/create",&RestController{},"post:CreateFood")
//	Add("/api/update",&RestController{},"put:UpdateFood")
//	Add("/api/delete",&RestController{},"delete:DeleteFood")
//	Add("/api",&RestController{},"get,post:ApiFunc")
//	Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
J
JessonChan 已提交
137
func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
A
astaxie 已提交
138 139 140 141 142 143 144 145 146 147 148 149
	reflectVal := reflect.ValueOf(c)
	t := reflect.Indirect(reflectVal).Type()
	methods := make(map[string]string)
	if len(mappingMethods) > 0 {
		semi := strings.Split(mappingMethods[0], ";")
		for _, v := range semi {
			colon := strings.Split(v, ":")
			if len(colon) != 2 {
				panic("method mapping format is invalid")
			}
			comma := strings.Split(colon[0], ",")
			for _, m := range comma {
A
astaxie 已提交
150
				if _, ok := HTTPMETHOD[strings.ToUpper(m)]; m == "*" || ok {
A
astaxie 已提交
151
					if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
A
astaxie 已提交
152
						methods[strings.ToUpper(m)] = colon[1]
A
astaxie 已提交
153
					} else {
A
astaxie 已提交
154
						panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
A
astaxie 已提交
155 156 157 158 159 160 161 162
					}
				} else {
					panic(v + " is an invalid method mapping. Method doesn't exist " + m)
				}
			}
		}
	}

A
astaxie 已提交
163
	route := &controllerInfo{}
164
	route.pattern = pattern
A
astaxie 已提交
165 166 167
	route.methods = methods
	route.routerType = routerTypeBeego
	route.controllerType = t
A
astaxie 已提交
168 169 170 171 172
	if len(methods) == 0 {
		for _, m := range HTTPMETHOD {
			p.addToRouter(m, pattern, route)
		}
	} else {
A
astaxie 已提交
173
		for k := range methods {
A
astaxie 已提交
174 175 176 177 178 179 180 181 182 183 184
			if k == "*" {
				for _, m := range HTTPMETHOD {
					p.addToRouter(m, pattern, route)
				}
			} else {
				p.addToRouter(k, pattern, route)
			}
		}
	}
}

J
JessonChan 已提交
185
func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) {
A
astaxie 已提交
186 187 188
	if !RouterCaseSensitive {
		pattern = strings.ToLower(pattern)
	}
A
astaxie 已提交
189 190 191 192 193 194 195
	if t, ok := p.routers[method]; ok {
		t.AddRouter(pattern, r)
	} else {
		t := NewTree()
		t.AddRouter(pattern, r)
		p.routers[method] = t
	}
A
astaxie 已提交
196
}
A
astaxie 已提交
197

A
astaxie 已提交
198
// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller
A
astaxie 已提交
199
// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
J
JessonChan 已提交
200
func (p *ControllerRegister) Include(cList ...ControllerInterface) {
A
astaxie 已提交
201
	if RunMode == "dev" {
A
astaxie 已提交
202
		skip := make(map[string]bool, 10)
A
astaxie 已提交
203 204 205
		for _, c := range cList {
			reflectVal := reflect.ValueOf(c)
			t := reflect.Indirect(reflectVal).Type()
A
astaxie 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
			gopath := os.Getenv("GOPATH")
			if gopath == "" {
				panic("you are in dev mode. So please set gopath")
			}
			pkgpath := ""

			wgopath := filepath.SplitList(gopath)
			for _, wg := range wgopath {
				wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath()))
				if utils.FileExists(wg) {
					pkgpath = wg
					break
				}
			}
			if pkgpath != "" {
				if _, ok := skip[pkgpath]; !ok {
					skip[pkgpath] = true
A
astaxie 已提交
223
					parserPkg(pkgpath, t.PkgPath())
A
astaxie 已提交
224 225 226 227 228 229 230 231 232
				}
			}
		}
	}
	for _, c := range cList {
		reflectVal := reflect.ValueOf(c)
		t := reflect.Indirect(reflectVal).Type()
		key := t.PkgPath() + ":" + t.Name()
		if comm, ok := GlobalControllerRouter[key]; ok {
A
astaxie 已提交
233
			for _, a := range comm {
234
				p.Add(a.Router, c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
A
astaxie 已提交
235
			}
A
astaxie 已提交
236 237 238 239
		}
	}
}

A
astaxie 已提交
240
// Get add get method
A
astaxie 已提交
241 242 243 244
// usage:
//    Get("/", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
245
func (p *ControllerRegister) Get(pattern string, f FilterFunc) {
A
astaxie 已提交
246 247 248
	p.AddMethod("get", pattern, f)
}

A
astaxie 已提交
249
// Post add post method
A
astaxie 已提交
250 251 252 253
// usage:
//    Post("/api", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
254
func (p *ControllerRegister) Post(pattern string, f FilterFunc) {
A
astaxie 已提交
255 256 257
	p.AddMethod("post", pattern, f)
}

A
astaxie 已提交
258
// Put add put method
A
astaxie 已提交
259 260 261 262
// usage:
//    Put("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
263
func (p *ControllerRegister) Put(pattern string, f FilterFunc) {
A
astaxie 已提交
264 265 266
	p.AddMethod("put", pattern, f)
}

A
astaxie 已提交
267
// Delete add delete method
A
astaxie 已提交
268 269 270 271
// usage:
//    Delete("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
272
func (p *ControllerRegister) Delete(pattern string, f FilterFunc) {
A
astaxie 已提交
273 274 275
	p.AddMethod("delete", pattern, f)
}

A
astaxie 已提交
276
// Head add head method
A
astaxie 已提交
277 278 279 280
// usage:
//    Head("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
281
func (p *ControllerRegister) Head(pattern string, f FilterFunc) {
A
astaxie 已提交
282 283 284
	p.AddMethod("head", pattern, f)
}

A
astaxie 已提交
285
// Patch add patch method
A
astaxie 已提交
286 287 288 289
// usage:
//    Patch("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
290
func (p *ControllerRegister) Patch(pattern string, f FilterFunc) {
A
astaxie 已提交
291 292 293
	p.AddMethod("patch", pattern, f)
}

A
astaxie 已提交
294
// Options add options method
A
astaxie 已提交
295 296 297 298
// usage:
//    Options("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
299
func (p *ControllerRegister) Options(pattern string, f FilterFunc) {
A
astaxie 已提交
300 301 302
	p.AddMethod("options", pattern, f)
}

A
astaxie 已提交
303
// Any add all method
A
astaxie 已提交
304 305 306 307
// usage:
//    Any("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
308
func (p *ControllerRegister) Any(pattern string, f FilterFunc) {
A
astaxie 已提交
309 310 311
	p.AddMethod("*", pattern, f)
}

A
astaxie 已提交
312
// AddMethod add http method router
A
astaxie 已提交
313 314 315 316
// usage:
//    AddMethod("get","/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
J
JessonChan 已提交
317
func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
A
astaxie 已提交
318
	if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok {
A
astaxie 已提交
319 320 321
		panic("not support http method: " + method)
	}
	route := &controllerInfo{}
322
	route.pattern = pattern
A
astaxie 已提交
323
	route.routerType = routerTypeRESTFul
J
JessonChan 已提交
324
	route.runFunction = f
A
astaxie 已提交
325 326 327 328 329 330
	methods := make(map[string]string)
	if method == "*" {
		for _, val := range HTTPMETHOD {
			methods[val] = val
		}
	} else {
A
astaxie 已提交
331
		methods[strings.ToUpper(method)] = strings.ToUpper(method)
A
astaxie 已提交
332 333
	}
	route.methods = methods
A
astaxie 已提交
334
	for k := range methods {
A
astaxie 已提交
335 336 337 338 339 340 341 342
		if k == "*" {
			for _, m := range HTTPMETHOD {
				p.addToRouter(m, pattern, route)
			}
		} else {
			p.addToRouter(k, pattern, route)
		}
	}
A
astaxie 已提交
343 344
}

A
astaxie 已提交
345
// Handler add user defined Handler
J
JessonChan 已提交
346
func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
A
astaxie 已提交
347
	route := &controllerInfo{}
348
	route.pattern = pattern
A
astaxie 已提交
349 350
	route.routerType = routerTypeHandler
	route.handler = h
A
astaxie 已提交
351
	if len(options) > 0 {
A
astaxie 已提交
352
		if _, ok := options[0].(bool); ok {
A
astaxie 已提交
353
			pattern = path.Join(pattern, "?:all(.*)")
A
astaxie 已提交
354
		}
A
fix bug  
astaxie 已提交
355
	}
A
astaxie 已提交
356 357 358
	for _, m := range HTTPMETHOD {
		p.addToRouter(m, pattern, route)
	}
A
fix bug  
astaxie 已提交
359 360
}

A
astaxie 已提交
361
// AddAuto router to ControllerRegister.
362 363
// example beego.AddAuto(&MainContorlller{}),
// MainController has method List and Page.
A
astaxie 已提交
364 365
// visit the url /main/list to execute List function
// /main/page to execute Page function.
J
JessonChan 已提交
366
func (p *ControllerRegister) AddAuto(c ControllerInterface) {
A
astaxie 已提交
367
	p.AddAutoPrefix("/", c)
A
astaxie 已提交
368 369
}

A
astaxie 已提交
370
// AddAutoPrefix Add auto router to ControllerRegister with prefix.
A
astaxie 已提交
371 372 373 374
// example beego.AddAutoPrefix("/admin",&MainContorlller{}),
// MainController has method List and Page.
// visit the url /admin/main/list to execute List function
// /admin/main/page to execute Page function.
J
JessonChan 已提交
375
func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) {
A
astaxie 已提交
376 377 378
	reflectVal := reflect.ValueOf(c)
	rt := reflectVal.Type()
	ct := reflect.Indirect(reflectVal).Type()
A
astaxie 已提交
379
	controllerName := strings.TrimSuffix(ct.Name(), "Controller")
A
astaxie 已提交
380 381
	for i := 0; i < rt.NumMethod(); i++ {
		if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
A
astaxie 已提交
382 383 384 385
			route := &controllerInfo{}
			route.routerType = routerTypeBeego
			route.methods = map[string]string{"*": rt.Method(i).Name}
			route.controllerType = ct
A
astaxie 已提交
386 387 388 389
			pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*")
			patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*")
			patternfix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name))
			patternfixInit := path.Join(prefix, controllerName, rt.Method(i).Name)
390
			route.pattern = pattern
A
astaxie 已提交
391 392
			for _, m := range HTTPMETHOD {
				p.addToRouter(m, pattern, route)
A
astaxie 已提交
393
				p.addToRouter(m, patternInit, route)
A
astaxie 已提交
394
				p.addToRouter(m, patternfix, route)
A
astaxie 已提交
395
				p.addToRouter(m, patternfixInit, route)
A
astaxie 已提交
396
			}
A
astaxie 已提交
397
		}
A
astaxie 已提交
398 399 400
	}
}

A
astaxie 已提交
401
// InsertFilter Add a FilterFunc with pattern rule and action constant.
402
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
J
JessonChan 已提交
403
func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
404

A
astaxie 已提交
405 406 407 408
	mr := new(FilterRouter)
	mr.tree = NewTree()
	mr.pattern = pattern
	mr.filterFunc = filter
A
astaxie 已提交
409 410 411
	if !RouterCaseSensitive {
		pattern = strings.ToLower(pattern)
	}
412
	if len(params) == 0 {
413 414 415 416
		mr.returnOnOutput = true
	} else {
		mr.returnOnOutput = params[0]
	}
A
astaxie 已提交
417 418 419 420 421
	mr.tree.AddRouter(pattern, true)
	return p.insertFilterRouter(pos, mr)
}

// add Filter into
J
JessonChan 已提交
422
func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error {
K
knightmare 已提交
423
	p.filters[pos] = append(p.filters[pos], mr)
424
	p.enableFilter = true
425
	return nil
A
astaxie 已提交
426 427
}

A
astaxie 已提交
428
// URLFor does another controller handler in this request function.
429
// it can access any controller method.
A
astaxie 已提交
430
func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string {
A
astaxie 已提交
431 432 433 434 435 436 437 438 439
	paths := strings.Split(endpoint, ".")
	if len(paths) <= 1 {
		Warn("urlfor endpoint must like path.controller.method")
		return ""
	}
	if len(values)%2 != 0 {
		Warn("urlfor params must key-value pair")
		return ""
	}
A
astaxie 已提交
440
	params := make(map[string]string)
A
astaxie 已提交
441 442 443 444
	if len(values) > 0 {
		key := ""
		for k, v := range values {
			if k%2 == 0 {
445
				key = fmt.Sprint(v)
A
astaxie 已提交
446
			} else {
447
				params[key] = fmt.Sprint(v)
A
astaxie 已提交
448 449 450
			}
		}
	}
451
	controllName := strings.Join(paths[:len(paths)-1], "/")
A
astaxie 已提交
452
	methodName := paths[len(paths)-1]
A
astaxie 已提交
453 454
	for m, t := range p.routers {
		ok, url := p.geturl(t, "/", controllName, methodName, params, m)
A
astaxie 已提交
455 456 457
		if ok {
			return url
		}
A
astaxie 已提交
458
	}
A
astaxie 已提交
459
	return ""
A
astaxie 已提交
460 461
}

J
JessonChan 已提交
462
func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
A
astaxie 已提交
463 464
	for k, subtree := range t.fixrouters {
		u := path.Join(url, k)
A
astaxie 已提交
465
		ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
A
astaxie 已提交
466 467 468 469 470
		if ok {
			return ok, u
		}
	}
	if t.wildcard != nil {
A
astaxie 已提交
471
		u := path.Join(url, urlPlaceholder)
A
astaxie 已提交
472
		ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
A
astaxie 已提交
473 474 475 476
		if ok {
			return ok, u
		}
	}
477 478
	for _, l := range t.leaves {
		if c, ok := l.runObject.(*controllerInfo); ok {
479 480
			if c.routerType == routerTypeBeego &&
				strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
A
astaxie 已提交
481
				find := false
A
astaxie 已提交
482
				if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
A
astaxie 已提交
483 484 485
					if len(c.methods) == 0 {
						find = true
					} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) {
486 487
						find = true
					} else if m, ok = c.methods["*"]; ok && m == methodName {
A
astaxie 已提交
488
						find = true
A
astaxie 已提交
489
					}
490 491
				}
				if !find {
A
astaxie 已提交
492 493
					for m, md := range c.methods {
						if (m == "*" || m == httpMethod) && md == methodName {
A
astaxie 已提交
494 495
							find = true
						}
A
astaxie 已提交
496 497
					}
				}
A
astaxie 已提交
498
				if find {
499 500
					if l.regexps == nil {
						if len(l.wildcards) == 0 {
A
astaxie 已提交
501
							return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + tourl(params)
A
astaxie 已提交
502
						}
503 504 505
						if len(l.wildcards) == 1 {
							if v, ok := params[l.wildcards[0]]; ok {
								delete(params, l.wildcards[0])
A
astaxie 已提交
506
								return true, strings.Replace(url, urlPlaceholder, v, 1) + tourl(params)
507
							}
A
astaxie 已提交
508
							return false, ""
509
						}
510
						if len(l.wildcards) == 3 && l.wildcards[0] == "." {
A
astaxie 已提交
511 512 513 514
							if p, ok := params[":path"]; ok {
								if e, isok := params[":ext"]; isok {
									delete(params, ":path")
									delete(params, ":ext")
A
astaxie 已提交
515
									return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + tourl(params)
A
astaxie 已提交
516
								}
517
							}
A
fix #16  
astaxie 已提交
518
						}
A
astaxie 已提交
519
						canskip := false
520
						for _, v := range l.wildcards {
A
astaxie 已提交
521 522 523 524 525
							if v == ":" {
								canskip = true
								continue
							}
							if u, ok := params[v]; ok {
A
astaxie 已提交
526
								delete(params, v)
A
astaxie 已提交
527
								url = strings.Replace(url, urlPlaceholder, u, 1)
A
astaxie 已提交
528 529 530 531 532
							} else {
								if canskip {
									canskip = false
									continue
								}
A
astaxie 已提交
533
								return false, ""
A
astaxie 已提交
534 535
							}
						}
A
astaxie 已提交
536
						return true, url + tourl(params)
A
astaxie 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
					}
					var i int
					var startreg bool
					regurl := ""
					for _, v := range strings.Trim(l.regexps.String(), "^$") {
						if v == '(' {
							startreg = true
							continue
						} else if v == ')' {
							startreg = false
							if v, ok := params[l.wildcards[i]]; ok {
								delete(params, l.wildcards[i])
								regurl = regurl + v
								i++
							} else {
								break
A
astaxie 已提交
553
							}
A
astaxie 已提交
554 555
						} else if !startreg {
							regurl = string(append([]rune(regurl), v))
A
fix #16  
astaxie 已提交
556
						}
A
astaxie 已提交
557 558 559 560 561
					}
					if l.regexps.MatchString(regurl) {
						ps := strings.Split(regurl, "/")
						for _, p := range ps {
							url = strings.Replace(url, urlPlaceholder, p, 1)
A
astaxie 已提交
562
						}
A
astaxie 已提交
563
						return true, url + tourl(params)
A
fix bug  
astaxie 已提交
564
					}
A
astaxie 已提交
565
				}
A
fix bug  
astaxie 已提交
566 567
			}
		}
A
astaxie 已提交
568
	}
569

A
astaxie 已提交
570 571 572 573
	return false, ""
}

// Implement http.Handler interface.
J
JessonChan 已提交
574
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
575
	starttime := time.Now()
576
	var runrouter reflect.Type
577
	var findrouter bool
578
	var runMethod string
A
astaxie 已提交
579
	var routerInfo *controllerInfo
580

A
astaxie 已提交
581
	w := &responseWriter{rw, false, 0}
582 583 584 585

	if RunMode == "dev" {
		w.Header().Set("Server", BeegoServerName)
	}
A
astaxie 已提交
586 587

	// init context
A
astaxie 已提交
588 589 590 591 592
	context := &beecontext.Context{
		ResponseWriter: w,
		Request:        r,
		Input:          beecontext.NewInput(r),
		Output:         beecontext.NewOutput(),
A
astaxie 已提交
593
	}
A
astaxie 已提交
594 595
	context.Output.Context = context
	context.Output.EnableGzip = EnableGzip
A
astaxie 已提交
596

A
astaxie 已提交
597 598
	defer p.recoverPanic(context)

A
astaxie 已提交
599 600 601 602 603 604
	var urlPath string
	if !RouterCaseSensitive {
		urlPath = strings.ToLower(r.URL.Path)
	} else {
		urlPath = r.URL.Path
	}
A
astaxie 已提交
605
	// defined filter function
A
astaxie 已提交
606
	doFilter := func(pos int) (started bool) {
607 608 609
		if p.enableFilter {
			if l, ok := p.filters[pos]; ok {
				for _, filterR := range l {
A
astaxie 已提交
610 611 612
					if filterR.returnOnOutput && w.started {
						return true
					}
A
astaxie 已提交
613 614
					if ok, params := filterR.ValidRouter(urlPath); ok {
						for k, v := range params {
M
MrLee.Kun 已提交
615
							if context.Input.Params == nil {
616
								context.Input.Params = make(map[string]string)
M
MrLee.Kun 已提交
617
							}
618
							context.Input.Params[k] = v
Z
gofmt  
ziyel 已提交
619
						}
620
						filterR.filterFunc(context)
A
astaxie 已提交
621 622 623
					}
					if filterR.returnOnOutput && w.started {
						return true
624 625 626 627
					}
				}
			}
		}
628
		return false
629 630
	}

A
astaxie 已提交
631 632 633 634 635 636 637
	// filter wrong httpmethod
	if _, ok := HTTPMETHOD[r.Method]; !ok {
		http.Error(w, "Method Not Allowed", 405)
		goto Admin
	}

	// filter for static file
A
astaxie 已提交
638
	if doFilter(BeforeStatic) {
A
astaxie 已提交
639 640 641 642 643
		goto Admin
	}

	serverStaticRouter(context)
	if w.started {
A
astaxie 已提交
644
		findrouter = true
A
astaxie 已提交
645 646 647
		goto Admin
	}

A
astaxie 已提交
648 649
	// session init
	if SessionOn {
A
astaxie 已提交
650 651 652 653
		var err error
		context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
		if err != nil {
			Error(err)
A
astaxie 已提交
654
			exception("503", context)
A
astaxie 已提交
655 656
			return
		}
657 658 659
		defer func() {
			context.Input.CruSession.SessionRelease(w)
		}()
A
astaxie 已提交
660 661
	}

A
astaxie 已提交
662
	if r.Method != "GET" && r.Method != "HEAD" {
A
astaxie 已提交
663 664
		if CopyRequestBody && !context.Input.IsUpload() {
			context.Input.CopyBody()
A
fix bug  
astaxie 已提交
665
		}
A
astaxie 已提交
666
		context.Input.ParseFormOrMulitForm(MaxMemory)
A
fix bug  
astaxie 已提交
667 668
	}

A
astaxie 已提交
669
	if doFilter(BeforeRouter) {
670
		goto Admin
A
astaxie 已提交
671
	}
A
fix bug  
astaxie 已提交
672

A
asta.xie 已提交
673
	if context.Input.RunController != nil && context.Input.RunMethod != "" {
674 675 676 677 678 679
		findrouter = true
		runMethod = context.Input.RunMethod
		runrouter = context.Input.RunController
	}

	if !findrouter {
A
astaxie 已提交
680
		httpMethod := r.Method
681

A
astaxie 已提交
682 683
		if httpMethod == "POST" && context.Input.Query("_method") == "PUT" {
			httpMethod = "PUT"
684 685
		}

A
astaxie 已提交
686 687
		if httpMethod == "POST" && context.Input.Query("_method") == "DELETE" {
			httpMethod = "DELETE"
688 689
		}

A
astaxie 已提交
690
		if t, ok := p.routers[httpMethod]; ok {
A
astaxie 已提交
691
			runObject, p := t.Match(urlPath)
A
astaxie 已提交
692 693 694 695 696 697 698 699
			if r, ok := runObject.(*controllerInfo); ok {
				routerInfo = r
				findrouter = true
				if splat, ok := p[":splat"]; ok {
					splatlist := strings.Split(splat, "/")
					for k, v := range splatlist {
						p[strconv.Itoa(k)] = v
					}
700
				}
M
MrLee.Kun 已提交
701 702 703
				if p != nil {
					context.Input.Params = p
				}
704 705
			}
		}
A
astaxie 已提交
706

707 708 709 710
	}

	//if no matches to url, throw a not found exception
	if !findrouter {
A
astaxie 已提交
711
		exception("404", context)
712 713
		goto Admin
	}
A
fix bug  
astaxie 已提交
714

715
	if findrouter {
A
fix bug  
astaxie 已提交
716
		//execute middleware filters
A
astaxie 已提交
717
		if doFilter(BeforeExec) {
718
			goto Admin
A
fix bug  
astaxie 已提交
719
		}
A
astaxie 已提交
720 721 722
		isRunable := false
		if routerInfo != nil {
			if routerInfo.routerType == routerTypeRESTFul {
A
astaxie 已提交
723
				if _, ok := routerInfo.methods[r.Method]; ok {
A
astaxie 已提交
724
					isRunable = true
J
JessonChan 已提交
725
					routerInfo.runFunction(context)
A
astaxie 已提交
726
				} else {
A
astaxie 已提交
727
					exception("405", context)
A
astaxie 已提交
728 729 730 731 732
					goto Admin
				}
			} else if routerInfo.routerType == routerTypeHandler {
				isRunable = true
				routerInfo.handler.ServeHTTP(rw, r)
A
astaxie 已提交
733 734
			} else {
				runrouter = routerInfo.controllerType
A
astaxie 已提交
735 736 737
				method := r.Method
				if r.Method == "POST" && context.Input.Query("_method") == "PUT" {
					method = "PUT"
A
astaxie 已提交
738
				}
A
astaxie 已提交
739 740
				if r.Method == "POST" && context.Input.Query("_method") == "DELETE" {
					method = "DELETE"
A
astaxie 已提交
741 742 743 744 745 746
				}
				if m, ok := routerInfo.methods[method]; ok {
					runMethod = m
				} else if m, ok = routerInfo.methods["*"]; ok {
					runMethod = m
				} else {
A
astaxie 已提交
747
					runMethod = method
A
astaxie 已提交
748
				}
A
astaxie 已提交
749
			}
750
		}
A
fix bug  
astaxie 已提交
751

A
astaxie 已提交
752
		// also defined runrouter & runMethod from filter
A
astaxie 已提交
753 754 755 756 757 758 759 760 761 762
		if !isRunable {
			//Invoke the request handler
			vc := reflect.New(runrouter)
			execController, ok := vc.Interface().(ControllerInterface)
			if !ok {
				panic("controller is not ControllerInterface")
			}

			//call the controller init function
			execController.Init(context, runrouter.Name(), runMethod, vc.Interface())
A
fix bug  
astaxie 已提交
763

764 765 766
			//call prepare function
			execController.Prepare()

A
astaxie 已提交
767 768
			//if XSRF is Enable then check cookie where there has any cookie in the  request's cookie _csrf
			if EnableXSRF {
A
astaxie 已提交
769
				execController.XSRFToken()
A
astaxie 已提交
770
				if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
771
					(r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) {
A
astaxie 已提交
772
					execController.CheckXSRFCookie()
A
astaxie 已提交
773
				}
A
astaxie 已提交
774 775
			}

A
astaxie 已提交
776 777
			execController.URLMapping()

A
astaxie 已提交
778 779 780
			if !w.started {
				//exec main logic
				switch runMethod {
A
astaxie 已提交
781
				case "GET":
A
astaxie 已提交
782
					execController.Get()
A
astaxie 已提交
783
				case "POST":
A
astaxie 已提交
784
					execController.Post()
A
astaxie 已提交
785
				case "DELETE":
A
astaxie 已提交
786
					execController.Delete()
A
astaxie 已提交
787
				case "PUT":
A
astaxie 已提交
788
					execController.Put()
A
astaxie 已提交
789
				case "HEAD":
A
astaxie 已提交
790
					execController.Head()
A
astaxie 已提交
791
				case "PATCH":
A
astaxie 已提交
792
					execController.Patch()
A
astaxie 已提交
793
				case "OPTIONS":
A
astaxie 已提交
794 795
					execController.Options()
				default:
A
astaxie 已提交
796
					if !execController.HandlerFunc(runMethod) {
A
astaxie 已提交
797
						var in []reflect.Value
A
astaxie 已提交
798 799 800
						method := vc.MethodByName(runMethod)
						method.Call(in)
					}
A
astaxie 已提交
801 802 803
				}

				//render template
A
astaxie 已提交
804
				if !w.started && context.Output.Status == 0 {
A
astaxie 已提交
805 806 807 808 809
					if AutoRender {
						if err := execController.Render(); err != nil {
							panic(err)
						}
					}
A
fix bug  
astaxie 已提交
810 811
				}
			}
A
astaxie 已提交
812

A
astaxie 已提交
813 814 815
			// finish all runrouter. release resource
			execController.Finish()
		}
816

A
astaxie 已提交
817
		//execute middleware filters
A
astaxie 已提交
818
		if doFilter(AfterExec) {
819
			goto Admin
A
astaxie 已提交
820
		}
A
fix bug  
astaxie 已提交
821 822
	}

A
astaxie 已提交
823
	doFilter(FinishRouter)
A
astaxie 已提交
824

A
astaxie 已提交
825
Admin:
826
	timeend := time.Since(starttime)
827 828
	//admin module record QPS
	if EnableAdmin {
A
astaxie 已提交
829
		if FilterMonitorFunc(r.Method, r.URL.Path, timeend) {
A
astaxie 已提交
830
			if runrouter != nil {
A
astaxie 已提交
831
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runrouter.Name(), timeend)
A
astaxie 已提交
832
			} else {
A
astaxie 已提交
833
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeend)
A
astaxie 已提交
834
			}
A
astaxie 已提交
835
		}
836
	}
837

A
astaxie 已提交
838
	if RunMode == "dev" || AccessLogs {
839
		var devinfo string
840
		if findrouter {
A
astaxie 已提交
841 842 843 844 845
			if routerInfo != nil {
				devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s | % -40s |", r.Method, r.URL.Path, timeend.String(), "match", routerInfo.pattern)
			} else {
				devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "match")
			}
846
		} else {
A
astaxie 已提交
847
			devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch")
848
		}
A
astaxie 已提交
849
		if DefaultAccessLogFilter == nil || !DefaultAccessLogFilter.Filter(context) {
A
astaxie 已提交
850 851
			Debug(devinfo)
		}
852
	}
853 854 855

	// Call WriteHeader if status code has been set changed
	if context.Output.Status != 0 {
A
astaxie 已提交
856
		w.WriteHeader(context.Output.Status)
857
	}
A
fix bug  
astaxie 已提交
858 859
}

J
JessonChan 已提交
860
func (p *ControllerRegister) recoverPanic(context *beecontext.Context) {
A
astaxie 已提交
861
	if err := recover(); err != nil {
A
astaxie 已提交
862
		if err == ErrAbort {
A
astaxie 已提交
863 864
			return
		}
A
astaxie 已提交
865 866
		if !RecoverPanic {
			panic(err)
A
astaxie 已提交
867
		} else {
868
			if EnableErrorsShow {
A
astaxie 已提交
869
				if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
A
astaxie 已提交
870
					exception(fmt.Sprint(err), context)
A
astaxie 已提交
871 872 873 874
					return
				}
			}
			var stack string
A
astaxie 已提交
875
			Critical("the request url is ", context.Input.URL())
A
astaxie 已提交
876 877 878 879 880
			Critical("Handler crashed with error", err)
			for i := 1; ; i++ {
				_, file, line, ok := runtime.Caller(i)
				if !ok {
					break
A
astaxie 已提交
881
				}
A
astaxie 已提交
882 883 884 885 886
				Critical(fmt.Sprintf("%s:%d", file, line))
				stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
			}
			if RunMode == "dev" {
				showErr(err, context, stack)
A
astaxie 已提交
887 888 889 890 891
			}
		}
	}
}

A
fix bug  
astaxie 已提交
892 893 894
//responseWriter is a wrapper for the http.ResponseWriter
//started set to true if response was written to then don't execute other handler
type responseWriter struct {
A
astaxie 已提交
895
	http.ResponseWriter
A
astaxie 已提交
896 897
	started bool
	status  int
A
fix bug  
astaxie 已提交
898 899 900 901
}

// Header returns the header map that will be sent by WriteHeader.
func (w *responseWriter) Header() http.Header {
A
astaxie 已提交
902
	return w.ResponseWriter.Header()
A
fix bug  
astaxie 已提交
903 904 905
}

// Write writes the data to the connection as part of an HTTP reply,
906 907
// and sets `started` to true.
// started means the response has sent out.
A
fix bug  
astaxie 已提交
908 909
func (w *responseWriter) Write(p []byte) (int, error) {
	w.started = true
A
astaxie 已提交
910
	return w.ResponseWriter.Write(p)
A
fix bug  
astaxie 已提交
911 912 913
}

// WriteHeader sends an HTTP response header with status code,
914
// and sets `started` to true.
A
fix bug  
astaxie 已提交
915 916 917
func (w *responseWriter) WriteHeader(code int) {
	w.status = code
	w.started = true
A
astaxie 已提交
918
	w.ResponseWriter.WriteHeader(code)
919 920
}

A
astaxie 已提交
921 922 923 924 925 926 927 928 929 930
func tourl(params map[string]string) string {
	if len(params) == 0 {
		return ""
	}
	u := "?"
	for k, v := range params {
		u += k + "=" + v + "&"
	}
	return strings.TrimRight(u, "&")
}