router.go 22.7 KB
Newer Older
A
astaxie 已提交
1 2 3 4 5 6
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link        http://github.com/astaxie/beego for the canonical source repository
// @license     http://github.com/astaxie/beego/blob/master/LICENSE
// @authors     astaxie

A
fix bug  
astaxie 已提交
7 8 9
package beego

import (
A
astaxie 已提交
10 11
	"bufio"
	"errors"
A
astaxie 已提交
12
	"fmt"
A
astaxie 已提交
13
	"net"
A
fix bug  
astaxie 已提交
14
	"net/http"
A
astaxie 已提交
15
	"os"
A
astaxie 已提交
16
	"path"
A
astaxie 已提交
17
	"path/filepath"
A
fix bug  
astaxie 已提交
18 19
	"reflect"
	"runtime"
A
astaxie 已提交
20
	"strconv"
A
fix bug  
astaxie 已提交
21
	"strings"
22
	"time"
A
astaxie 已提交
23 24 25 26

	beecontext "github.com/astaxie/beego/context"
	"github.com/astaxie/beego/middleware"
	"github.com/astaxie/beego/toolbox"
27
	"github.com/astaxie/beego/utils"
A
fix bug  
astaxie 已提交
28 29
)

30
const (
31
	// default filter execution points
32 33 34 35 36 37
	BeforeRouter = iota
	BeforeExec
	AfterExec
	FinishRouter
)

A
astaxie 已提交
38 39 40 41 42 43
const (
	routerTypeBeego = iota
	routerTypeRESTFul
	routerTypeHandler
)

44
var (
45
	// supported http methods.
A
astaxie 已提交
46 47 48 49 50 51 52 53 54 55 56
	HTTPMETHOD = map[string]string{
		"GET":     "GET",
		"POST":    "POST",
		"PUT":     "PUT",
		"DELETE":  "DELETE",
		"PATCH":   "PATCH",
		"OPTIONS": "OPTIONS",
		"HEAD":    "HEAD",
		"TRACE":   "TRACE",
		"CONNECT": "CONNECT",
	}
A
astaxie 已提交
57 58 59 60 61 62 63 64
	// these beego.Controller's methods shouldn't reflect to AutoRouter
	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"}
65
)
A
astaxie 已提交
66

U
unphp 已提交
67 68 69 70 71
// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
func ExceptMethodAppend(action string) {
	exceptMethod = append(exceptMethod, action)
}

A
fix bug  
astaxie 已提交
72
type controllerInfo struct {
73
	pattern        string
A
fix bug  
astaxie 已提交
74
	controllerType reflect.Type
A
astaxie 已提交
75
	methods        map[string]string
A
astaxie 已提交
76 77 78
	handler        http.Handler
	runfunction    FilterFunc
	routerType     int
A
fix bug  
astaxie 已提交
79 80
}

81
// ControllerRegistor containers registered router rules, controller handlers and filters.
A
fix bug  
astaxie 已提交
82
type ControllerRegistor struct {
A
astaxie 已提交
83
	routers      map[string]*Tree
A
astaxie 已提交
84 85
	enableFilter bool
	filters      map[int][]*FilterRouter
A
fix bug  
astaxie 已提交
86 87
}

A
astaxie 已提交
88 89
// NewControllerRegister returns a new ControllerRegistor.
func NewControllerRegister() *ControllerRegistor {
A
astaxie 已提交
90
	return &ControllerRegistor{
A
astaxie 已提交
91
		routers: make(map[string]*Tree),
A
astaxie 已提交
92
		filters: make(map[int][]*FilterRouter),
A
astaxie 已提交
93
	}
A
fix bug  
astaxie 已提交
94 95
}

96 97 98 99 100 101 102 103 104 105
// Add controller handler and pattern rules to ControllerRegistor.
// 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")
A
astaxie 已提交
106
func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
A
astaxie 已提交
107 108 109 110 111 112 113 114 115 116 117 118
	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 已提交
119
				if _, ok := HTTPMETHOD[strings.ToUpper(m)]; m == "*" || ok {
A
astaxie 已提交
120
					if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
A
astaxie 已提交
121
						methods[strings.ToUpper(m)] = colon[1]
A
astaxie 已提交
122 123 124 125 126 127 128 129 130 131
					} else {
						panic(colon[1] + " method doesn't exist in the controller " + t.Name())
					}
				} else {
					panic(v + " is an invalid method mapping. Method doesn't exist " + m)
				}
			}
		}
	}

A
astaxie 已提交
132
	route := &controllerInfo{}
133
	route.pattern = pattern
A
astaxie 已提交
134 135 136
	route.methods = methods
	route.routerType = routerTypeBeego
	route.controllerType = t
A
astaxie 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	if len(methods) == 0 {
		for _, m := range HTTPMETHOD {
			p.addToRouter(m, pattern, route)
		}
	} else {
		for k, _ := range methods {
			if k == "*" {
				for _, m := range HTTPMETHOD {
					p.addToRouter(m, pattern, route)
				}
			} else {
				p.addToRouter(k, pattern, route)
			}
		}
	}
}

func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {
	if t, ok := p.routers[method]; ok {
		t.AddRouter(pattern, r)
	} else {
		t := NewTree()
		t.AddRouter(pattern, r)
		p.routers[method] = t
	}
A
astaxie 已提交
162
}
A
astaxie 已提交
163

A
astaxie 已提交
164 165 166 167
// only when the Runmode is dev will generate router file in the router/auto.go from the controller
// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
func (p *ControllerRegistor) Include(cList ...ControllerInterface) {
	if RunMode == "dev" {
A
astaxie 已提交
168
		skip := make(map[string]bool, 10)
A
astaxie 已提交
169 170 171
		for _, c := range cList {
			reflectVal := reflect.ValueOf(c)
			t := reflect.Indirect(reflectVal).Type()
A
astaxie 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
			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 已提交
189
					parserPkg(pkgpath, t.PkgPath())
A
astaxie 已提交
190 191 192 193 194 195 196 197 198
				}
			}
		}
	}
	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 已提交
199
			for _, a := range comm {
200
				p.Add(a.Router, c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
A
astaxie 已提交
201
			}
A
astaxie 已提交
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
		}
	}
}

// add get method
// usage:
//    Get("/", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Get(pattern string, f FilterFunc) {
	p.AddMethod("get", pattern, f)
}

// add post method
// usage:
//    Post("/api", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Post(pattern string, f FilterFunc) {
	p.AddMethod("post", pattern, f)
}

// add put method
// usage:
//    Put("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Put(pattern string, f FilterFunc) {
	p.AddMethod("put", pattern, f)
}

// add delete method
// usage:
//    Delete("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Delete(pattern string, f FilterFunc) {
	p.AddMethod("delete", pattern, f)
}

// add head method
// usage:
//    Head("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Head(pattern string, f FilterFunc) {
	p.AddMethod("head", pattern, f)
}

// add patch method
// usage:
//    Patch("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Patch(pattern string, f FilterFunc) {
	p.AddMethod("patch", pattern, f)
}

// add options method
// usage:
//    Options("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Options(pattern string, f FilterFunc) {
	p.AddMethod("options", pattern, f)
}

// add all method
// usage:
//    Any("/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Any(pattern string, f FilterFunc) {
	p.AddMethod("*", pattern, f)
}

// add http method router
// usage:
//    AddMethod("get","/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {
A
astaxie 已提交
284
	if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok {
A
astaxie 已提交
285 286 287
		panic("not support http method: " + method)
	}
	route := &controllerInfo{}
288
	route.pattern = pattern
A
astaxie 已提交
289 290 291 292 293 294 295 296
	route.routerType = routerTypeRESTFul
	route.runfunction = f
	methods := make(map[string]string)
	if method == "*" {
		for _, val := range HTTPMETHOD {
			methods[val] = val
		}
	} else {
A
astaxie 已提交
297
		methods[strings.ToUpper(method)] = strings.ToUpper(method)
A
astaxie 已提交
298 299
	}
	route.methods = methods
A
astaxie 已提交
300 301 302 303 304 305 306 307 308
	for k, _ := range methods {
		if k == "*" {
			for _, m := range HTTPMETHOD {
				p.addToRouter(m, pattern, route)
			}
		} else {
			p.addToRouter(k, pattern, route)
		}
	}
A
astaxie 已提交
309 310
}

A
astaxie 已提交
311
// add user defined Handler
A
astaxie 已提交
312
func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) {
A
astaxie 已提交
313
	route := &controllerInfo{}
314
	route.pattern = pattern
A
astaxie 已提交
315 316
	route.routerType = routerTypeHandler
	route.handler = h
A
astaxie 已提交
317
	if len(options) > 0 {
A
astaxie 已提交
318 319
		if _, ok := options[0].(bool); ok {
			pattern = path.Join(pattern, "?:all")
A
astaxie 已提交
320
		}
A
fix bug  
astaxie 已提交
321
	}
A
astaxie 已提交
322 323 324
	for _, m := range HTTPMETHOD {
		p.addToRouter(m, pattern, route)
	}
A
fix bug  
astaxie 已提交
325 326
}

327 328 329
// Add auto router to ControllerRegistor.
// example beego.AddAuto(&MainContorlller{}),
// MainController has method List and Page.
A
astaxie 已提交
330 331
// visit the url /main/list to execute List function
// /main/page to execute Page function.
A
astaxie 已提交
332
func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
A
astaxie 已提交
333
	p.AddAutoPrefix("/", c)
A
astaxie 已提交
334 335 336 337 338 339 340 341 342 343 344
}

// Add auto router to ControllerRegistor with prefix.
// 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.
func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) {
	reflectVal := reflect.ValueOf(c)
	rt := reflectVal.Type()
	ct := reflect.Indirect(reflectVal).Type()
A
astaxie 已提交
345
	controllerName := strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller"))
A
astaxie 已提交
346 347
	for i := 0; i < rt.NumMethod(); i++ {
		if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
A
astaxie 已提交
348 349 350 351 352
			route := &controllerInfo{}
			route.routerType = routerTypeBeego
			route.methods = map[string]string{"*": rt.Method(i).Name}
			route.controllerType = ct
			pattern := path.Join(prefix, controllerName, strings.ToLower(rt.Method(i).Name), "*")
A
astaxie 已提交
353
			patternfix := path.Join(prefix, controllerName, strings.ToLower(rt.Method(i).Name))
354
			route.pattern = pattern
A
astaxie 已提交
355 356
			for _, m := range HTTPMETHOD {
				p.addToRouter(m, pattern, route)
A
astaxie 已提交
357
				p.addToRouter(m, patternfix, route)
A
astaxie 已提交
358
			}
A
astaxie 已提交
359
		}
A
astaxie 已提交
360 361 362
	}
}

363
// Add a FilterFunc with pattern rule and action constant.
364
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error {
A
astaxie 已提交
365 366 367 368 369 370 371 372 373 374
	mr := new(FilterRouter)
	mr.tree = NewTree()
	mr.pattern = pattern
	mr.filterFunc = filter
	mr.tree.AddRouter(pattern, true)
	return p.insertFilterRouter(pos, mr)
}

// add Filter into
func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error {
K
knightmare 已提交
375
	p.filters[pos] = append(p.filters[pos], mr)
376
	p.enableFilter = true
377
	return nil
A
astaxie 已提交
378 379
}

380 381
// UrlFor does another controller handler in this request function.
// it can access any controller method.
A
astaxie 已提交
382 383 384 385 386 387 388 389 390 391
func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
	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 已提交
392
	params := make(map[string]string)
A
astaxie 已提交
393 394 395 396 397 398
	if len(values) > 0 {
		key := ""
		for k, v := range values {
			if k%2 == 0 {
				key = v
			} else {
A
astaxie 已提交
399
				params[key] = v
A
astaxie 已提交
400 401 402 403 404
			}
		}
	}
	controllName := strings.Join(paths[:len(paths)-1], ".")
	methodName := paths[len(paths)-1]
A
astaxie 已提交
405 406 407 408 409
	for _, t := range p.routers {
		ok, url := p.geturl(t, "/", controllName, methodName, params)
		if ok {
			return url
		}
A
astaxie 已提交
410
	}
A
astaxie 已提交
411
	return ""
A
astaxie 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
}

func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) {
	for k, subtree := range t.fixrouters {
		u := path.Join(url, k)
		ok, u := p.geturl(subtree, u, controllName, methodName, params)
		if ok {
			return ok, u
		}
	}
	if t.wildcard != nil {
		ok, u := p.geturl(t.wildcard, url, controllName, methodName, params)
		if ok {
			return ok, u
		}
	}
428 429
	for _, l := range t.leaves {
		if c, ok := l.runObject.(*controllerInfo); ok {
A
astaxie 已提交
430 431
			if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName {
				find := false
A
astaxie 已提交
432
				if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
A
astaxie 已提交
433 434 435
					if len(c.methods) == 0 {
						find = true
					} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) {
436 437
						find = true
					} else if m, ok = c.methods["*"]; ok && m == methodName {
A
astaxie 已提交
438
						find = true
A
astaxie 已提交
439
					}
440 441
				}
				if !find {
A
astaxie 已提交
442 443 444 445
					for _, md := range c.methods {
						if md == methodName {
							find = true
						}
A
astaxie 已提交
446 447
					}
				}
A
astaxie 已提交
448
				if find {
449 450
					if l.regexps == nil {
						if len(l.wildcards) == 0 {
A
astaxie 已提交
451 452
							return true, url
						}
453 454 455
						if len(l.wildcards) == 1 {
							if v, ok := params[l.wildcards[0]]; ok {
								delete(params, l.wildcards[0])
A
astaxie 已提交
456 457
								return true, url + "/" + v + tourl(params)
							}
458
							if l.wildcards[0] == ":splat" {
A
astaxie 已提交
459
								return true, url + tourl(params)
460 461
							}
						}
462
						if len(l.wildcards) == 3 && l.wildcards[0] == "." {
A
astaxie 已提交
463 464 465 466 467 468
							if p, ok := params[":path"]; ok {
								if e, isok := params[":ext"]; isok {
									delete(params, ":path")
									delete(params, ":ext")
									return true, url + "/" + p + "." + e + tourl(params)
								}
469
							}
A
fix #16  
astaxie 已提交
470
						}
A
astaxie 已提交
471
						canskip := false
472
						for _, v := range l.wildcards {
A
astaxie 已提交
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
							if v == ":" {
								canskip = true
								continue
							}
							if u, ok := params[v]; ok {
								url += "/" + u
							} else {
								if canskip {
									canskip = false
									continue
								} else {
									return false, ""
								}
							}
						}
						return true, url
A
astaxie 已提交
489
					} else {
A
astaxie 已提交
490 491
						var i int
						var startreg bool
492 493
						regurl := ""
						for _, v := range strings.Trim(l.regexps.String(), "^$") {
A
astaxie 已提交
494 495 496 497 498
							if v == '(' {
								startreg = true
								continue
							} else if v == ')' {
								startreg = false
499 500 501
								if v, ok := params[l.wildcards[i]]; ok {
									delete(params, l.wildcards[i])
									regurl = regurl + v
A
astaxie 已提交
502 503
									i++
								} else {
A
astaxie 已提交
504 505
									break
								}
A
astaxie 已提交
506
							} else if !startreg {
507
								regurl = string(append([]rune(regurl), v))
A
astaxie 已提交
508
							}
A
fix #16  
astaxie 已提交
509
						}
510
						if l.regexps.MatchString(regurl) {
A
astaxie 已提交
511 512 513 514 515
							if url == "/" {
								return true, url + regurl + tourl(params)
							} else {
								return true, url + "/" + regurl + tourl(params)
							}
A
astaxie 已提交
516
						}
A
fix bug  
astaxie 已提交
517
					}
A
astaxie 已提交
518
				}
A
fix bug  
astaxie 已提交
519 520
			}
		}
A
astaxie 已提交
521
	}
522

A
astaxie 已提交
523 524 525 526 527 528
	return false, ""
}

// Implement http.Handler interface.
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	defer p.recoverPanic(rw, r)
529
	starttime := time.Now()
530
	var runrouter reflect.Type
531
	var findrouter bool
532
	var runMethod string
A
astaxie 已提交
533
	var routerInfo *controllerInfo
534

A
fix bug  
astaxie 已提交
535
	w := &responseWriter{writer: rw}
536 537 538 539

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

	// init context
A
astaxie 已提交
542 543 544 545 546
	context := &beecontext.Context{
		ResponseWriter: w,
		Request:        r,
		Input:          beecontext.NewInput(r),
		Output:         beecontext.NewOutput(),
A
astaxie 已提交
547
	}
A
astaxie 已提交
548 549
	context.Output.Context = context
	context.Output.EnableGzip = EnableGzip
A
astaxie 已提交
550 551

	// defined filter function
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
	do_filter := func(pos int) (started bool) {
		if p.enableFilter {
			if l, ok := p.filters[pos]; ok {
				for _, filterR := range l {
					if ok, p := filterR.ValidRouter(r.URL.Path); ok {
						context.Input.Params = p
						filterR.filterFunc(context)
						if w.started {
							return true
						}
					}
				}
			}
		}

567
		return false
568 569
	}

A
astaxie 已提交
570 571 572
	// session init
	if SessionOn {
		context.Input.CruSession = GlobalSessions.SessionStart(w, r)
573 574 575
		defer func() {
			context.Input.CruSession.SessionRelease(w)
		}()
A
astaxie 已提交
576 577
	}

A
astaxie 已提交
578
	if _, ok := HTTPMETHOD[r.Method]; !ok {
A
astaxie 已提交
579
		http.Error(w, "Method Not Allowed", 405)
580
		goto Admin
A
astaxie 已提交
581
	}
A
astaxie 已提交
582

A
astaxie 已提交
583
	if r.Method != "GET" && r.Method != "HEAD" {
A
astaxie 已提交
584 585
		if CopyRequestBody && !context.Input.IsUpload() {
			context.Input.CopyBody()
A
fix bug  
astaxie 已提交
586
		}
A
astaxie 已提交
587
		context.Input.ParseFormOrMulitForm(MaxMemory)
A
fix bug  
astaxie 已提交
588 589
	}

A
astaxie 已提交
590
	if do_filter(BeforeRouter) {
591
		goto Admin
A
astaxie 已提交
592
	}
A
fix bug  
astaxie 已提交
593

A
asta.xie 已提交
594
	if context.Input.RunController != nil && context.Input.RunMethod != "" {
595 596 597 598 599 600
		findrouter = true
		runMethod = context.Input.RunMethod
		runrouter = context.Input.RunController
	}

	if !findrouter {
A
astaxie 已提交
601
		if t, ok := p.routers[r.Method]; ok {
L
ljyf5593 已提交
602
			runObject, p := t.Match(strings.ToLower(r.URL.Path))
A
astaxie 已提交
603 604 605 606 607 608 609 610
			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
					}
611
				}
A
astaxie 已提交
612
				context.Input.Params = p
613 614
			}
		}
A
astaxie 已提交
615

616 617 618 619 620 621 622
	}

	//if no matches to url, throw a not found exception
	if !findrouter {
		middleware.Exception("404", rw, r, "")
		goto Admin
	}
A
fix bug  
astaxie 已提交
623

624
	if findrouter {
A
fix bug  
astaxie 已提交
625
		//execute middleware filters
626 627
		if do_filter(BeforeExec) {
			goto Admin
A
fix bug  
astaxie 已提交
628
		}
A
astaxie 已提交
629 630 631
		isRunable := false
		if routerInfo != nil {
			if routerInfo.routerType == routerTypeRESTFul {
A
astaxie 已提交
632
				if _, ok := routerInfo.methods[r.Method]; ok {
A
astaxie 已提交
633 634 635 636 637 638 639 640 641
					isRunable = true
					routerInfo.runfunction(context)
				} else {
					middleware.Exception("405", rw, r, "Method Not Allowed")
					goto Admin
				}
			} else if routerInfo.routerType == routerTypeHandler {
				isRunable = true
				routerInfo.handler.ServeHTTP(rw, r)
A
astaxie 已提交
642 643
			} else {
				runrouter = routerInfo.controllerType
A
astaxie 已提交
644 645 646
				method := r.Method
				if r.Method == "POST" && context.Input.Query("_method") == "PUT" {
					method = "PUT"
A
astaxie 已提交
647
				}
A
astaxie 已提交
648 649
				if r.Method == "POST" && context.Input.Query("_method") == "DELETE" {
					method = "DELETE"
A
astaxie 已提交
650 651 652 653 654 655
				}
				if m, ok := routerInfo.methods[method]; ok {
					runMethod = m
				} else if m, ok = routerInfo.methods["*"]; ok {
					runMethod = m
				} else {
A
astaxie 已提交
656
					runMethod = method
A
astaxie 已提交
657
				}
A
astaxie 已提交
658
			}
659
		}
A
fix bug  
astaxie 已提交
660

A
astaxie 已提交
661
		// also defined runrouter & runMethod from filter
A
astaxie 已提交
662 663 664 665 666 667 668 669 670 671
		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 已提交
672

673 674 675
			//call prepare function
			execController.Prepare()

A
astaxie 已提交
676 677 678 679 680 681 682
			//if XSRF is Enable then check cookie where there has any cookie in the  request's cookie _csrf
			if EnableXSRF {
				execController.XsrfToken()
				if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
					(r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) {
					execController.CheckXsrfCookie()
				}
A
astaxie 已提交
683 684
			}

A
astaxie 已提交
685 686
			execController.URLMapping()

A
astaxie 已提交
687 688 689
			if !w.started {
				//exec main logic
				switch runMethod {
A
astaxie 已提交
690
				case "GET":
A
astaxie 已提交
691
					execController.Get()
A
astaxie 已提交
692
				case "POST":
A
astaxie 已提交
693
					execController.Post()
A
astaxie 已提交
694
				case "DELETE":
A
astaxie 已提交
695
					execController.Delete()
A
astaxie 已提交
696
				case "PUT":
A
astaxie 已提交
697
					execController.Put()
A
astaxie 已提交
698
				case "HEAD":
A
astaxie 已提交
699
					execController.Head()
A
astaxie 已提交
700
				case "PATCH":
A
astaxie 已提交
701
					execController.Patch()
A
astaxie 已提交
702
				case "OPTIONS":
A
astaxie 已提交
703 704
					execController.Options()
				default:
A
astaxie 已提交
705 706 707 708 709
					if !execController.HandlerFunc(runMethod) {
						in := make([]reflect.Value, 0)
						method := vc.MethodByName(runMethod)
						method.Call(in)
					}
A
astaxie 已提交
710 711 712
				}

				//render template
713
				if !w.started {
A
astaxie 已提交
714 715 716 717
					if AutoRender {
						if err := execController.Render(); err != nil {
							panic(err)
						}
718

A
astaxie 已提交
719
					}
A
fix bug  
astaxie 已提交
720 721
				}
			}
A
astaxie 已提交
722

A
astaxie 已提交
723 724 725
			// finish all runrouter. release resource
			execController.Finish()
		}
726

A
astaxie 已提交
727
		//execute middleware filters
728 729
		if do_filter(AfterExec) {
			goto Admin
A
astaxie 已提交
730
		}
A
fix bug  
astaxie 已提交
731 732
	}

733
	do_filter(FinishRouter)
A
astaxie 已提交
734

A
astaxie 已提交
735
Admin:
736
	timeend := time.Since(starttime)
737 738
	//admin module record QPS
	if EnableAdmin {
A
astaxie 已提交
739
		if FilterMonitorFunc(r.Method, r.URL.Path, timeend) {
A
astaxie 已提交
740
			if runrouter != nil {
A
astaxie 已提交
741
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runrouter.Name(), timeend)
A
astaxie 已提交
742
			} else {
A
astaxie 已提交
743
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeend)
A
astaxie 已提交
744
			}
A
astaxie 已提交
745
		}
746
	}
747 748 749

	if RunMode == "dev" {
		if findrouter {
A
astaxie 已提交
750
			Info("beego: router defined " + routerInfo.pattern + " " + r.URL.Path + " +" + timeend.String())
751
		} else {
A
astaxie 已提交
752
			Info("beego:" + r.URL.Path + " 404" + " +" + timeend.String())
753 754
		}
	}
A
fix bug  
astaxie 已提交
755 756
}

A
astaxie 已提交
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) {
	if err := recover(); err != nil {
		if err == USERSTOPRUN {
			return
		}
		if _, ok := err.(middleware.HTTPException); ok {
			// catch intented errors, only for HTTP 4XX and 5XX
		} else {
			if RunMode == "dev" {
				if !RecoverPanic {
					panic(err)
				} else {
					if ErrorsShow {
						if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
							handler(rw, r)
							return
						}
					}
					var stack string
					Critical("the request url is ", r.URL.Path)
					Critical("Handler crashed with error", err)
					for i := 1; ; i++ {
						_, file, line, ok := runtime.Caller(i)
						if !ok {
							break
						}
						Critical(file, line)
						stack = stack + fmt.Sprintln(file, line)
					}
					middleware.ShowErr(err, rw, r, stack)
				}
			} else {
				if !RecoverPanic {
					panic(err)
				} else {
					// in production model show all infomation
					if ErrorsShow {
						handler := p.getErrorHandler(fmt.Sprint(err))
						handler(rw, r)
						return
					} else {
						Critical("the request url is ", r.URL.Path)
						Critical("Handler crashed with error", err)
						for i := 1; ; i++ {
							_, file, line, ok := runtime.Caller(i)
							if !ok {
								break
							}
							Critical(file, line)
						}
					}
				}
			}

		}
	}
}

815 816
// there always should be error handler that sets error code accordingly for all unhandled errors.
// in order to have custom UI for error page it's necessary to override "500" error.
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
	handler := middleware.SimpleServerError
	ok := true
	if errorCode != "" {
		handler, ok = middleware.ErrorMaps[errorCode]
		if !ok {
			handler, ok = middleware.ErrorMaps["500"]
		}
		if !ok || handler == nil {
			handler = middleware.SimpleServerError
		}
	}

	return handler
}

A
fix bug  
astaxie 已提交
833 834 835
//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 已提交
836 837 838
	writer  http.ResponseWriter
	started bool
	status  int
A
fix bug  
astaxie 已提交
839 840 841 842 843 844 845 846
}

// Header returns the header map that will be sent by WriteHeader.
func (w *responseWriter) Header() http.Header {
	return w.writer.Header()
}

// Write writes the data to the connection as part of an HTTP reply,
847 848
// and sets `started` to true.
// started means the response has sent out.
A
fix bug  
astaxie 已提交
849 850 851 852 853 854
func (w *responseWriter) Write(p []byte) (int, error) {
	w.started = true
	return w.writer.Write(p)
}

// WriteHeader sends an HTTP response header with status code,
855
// and sets `started` to true.
A
fix bug  
astaxie 已提交
856 857 858 859 860
func (w *responseWriter) WriteHeader(code int) {
	w.status = code
	w.started = true
	w.writer.WriteHeader(code)
}
A
astaxie 已提交
861 862 863 864 865 866 867 868 869

// hijacker for http
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hj, ok := w.writer.(http.Hijacker)
	if !ok {
		return nil, nil, errors.New("webserver doesn't support hijacking")
	}
	return hj.Hijack()
}
A
astaxie 已提交
870 871 872 873 874 875 876 877 878 879 880

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, "&")
}