934dd2e8 by Chen Liang

Merge branch 'master' of https://github.com/astaxie/beego

2 parents 0188fb37 07c628c7
1 {
2 "file_line": 500,
3 "func_line": 80,
4 "params_num":4,
5 "results_num":3,
6 "formated": true,
7 "pkg_name": true,
8 "camel_name":true,
9 "ignore":[
10 "a/*",
11 "b/*/c/*.go"
12 ],
13 "fatal":[
14 "formated"
15 ]
16 }
...@@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) { ...@@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
113 m["SessionName"] = SessionName 113 m["SessionName"] = SessionName
114 m["SessionGCMaxLifetime"] = SessionGCMaxLifetime 114 m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
115 m["SessionSavePath"] = SessionSavePath 115 m["SessionSavePath"] = SessionSavePath
116 m["SessionHashFunc"] = SessionHashFunc
117 m["SessionHashKey"] = SessionHashKey
118 m["SessionCookieLifeTime"] = SessionCookieLifeTime 116 m["SessionCookieLifeTime"] = SessionCookieLifeTime
119 m["UseFcgi"] = UseFcgi 117 m["UseFcgi"] = UseFcgi
120 m["MaxMemory"] = MaxMemory 118 m["MaxMemory"] = MaxMemory
...@@ -458,6 +456,7 @@ func (admin *adminApp) Run() { ...@@ -458,6 +456,7 @@ func (admin *adminApp) Run() {
458 for p, f := range admin.routers { 456 for p, f := range admin.routers {
459 http.Handle(p, f) 457 http.Handle(p, f)
460 } 458 }
459 BeeLogger.Info("Admin server Running on %s", addr)
461 err := http.ListenAndServe(addr, nil) 460 err := http.ListenAndServe(addr, nil)
462 if err != nil { 461 if err != nil {
463 BeeLogger.Critical("Admin ListenAndServe: ", err) 462 BeeLogger.Critical("Admin ListenAndServe: ", err)
......
...@@ -20,13 +20,8 @@ import ( ...@@ -20,13 +20,8 @@ import (
20 "net/http" 20 "net/http"
21 "net/http/fcgi" 21 "net/http/fcgi"
22 "time" 22 "time"
23
24 "github.com/astaxie/beego/context"
25 ) 23 )
26 24
27 // FilterFunc defines filter function type.
28 type FilterFunc func(*context.Context)
29
30 // App defines beego application with a new PatternServeMux. 25 // App defines beego application with a new PatternServeMux.
31 type App struct { 26 type App struct {
32 Handlers *ControllerRegistor 27 Handlers *ControllerRegistor
...@@ -48,8 +43,6 @@ func (app *App) Run() { ...@@ -48,8 +43,6 @@ func (app *App) Run() {
48 addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) 43 addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
49 } 44 }
50 45
51 BeeLogger.Info("Running on %s", addr)
52
53 var ( 46 var (
54 err error 47 err error
55 l net.Listener 48 l net.Listener
...@@ -57,6 +50,14 @@ func (app *App) Run() { ...@@ -57,6 +50,14 @@ func (app *App) Run() {
57 endRunning := make(chan bool, 1) 50 endRunning := make(chan bool, 1)
58 51
59 if UseFcgi { 52 if UseFcgi {
53 if UseStdIo {
54 err = fcgi.Serve(nil, app.Handlers) // standard I/O
55 if err == nil {
56 BeeLogger.Info("Use FCGI via standard I/O")
57 } else {
58 BeeLogger.Info("Cannot use FCGI via standard I/O", err)
59 }
60 } else {
60 if HttpPort == 0 { 61 if HttpPort == 0 {
61 l, err = net.Listen("unix", addr) 62 l, err = net.Listen("unix", addr)
62 } else { 63 } else {
...@@ -66,6 +67,7 @@ func (app *App) Run() { ...@@ -66,6 +67,7 @@ func (app *App) Run() {
66 BeeLogger.Critical("Listen: ", err) 67 BeeLogger.Critical("Listen: ", err)
67 } 68 }
68 err = fcgi.Serve(l, app.Handlers) 69 err = fcgi.Serve(l, app.Handlers)
70 }
69 } else { 71 } else {
70 app.Server.Addr = addr 72 app.Server.Addr = addr
71 app.Server.Handler = app.Handlers 73 app.Server.Handler = app.Handlers
...@@ -78,6 +80,7 @@ func (app *App) Run() { ...@@ -78,6 +80,7 @@ func (app *App) Run() {
78 if HttpsPort != 0 { 80 if HttpsPort != 0 {
79 app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) 81 app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
80 } 82 }
83 BeeLogger.Info("https server Running on %s", app.Server.Addr)
81 err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) 84 err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
82 if err != nil { 85 if err != nil {
83 BeeLogger.Critical("ListenAndServeTLS: ", err) 86 BeeLogger.Critical("ListenAndServeTLS: ", err)
...@@ -90,12 +93,30 @@ func (app *App) Run() { ...@@ -90,12 +93,30 @@ func (app *App) Run() {
90 if EnableHttpListen { 93 if EnableHttpListen {
91 go func() { 94 go func() {
92 app.Server.Addr = addr 95 app.Server.Addr = addr
96 BeeLogger.Info("http server Running on %s", app.Server.Addr)
97 if ListenTCP4 && HttpAddr == "" {
98 ln, err := net.Listen("tcp4", app.Server.Addr)
99 if err != nil {
100 BeeLogger.Critical("ListenAndServe: ", err)
101 time.Sleep(100 * time.Microsecond)
102 endRunning <- true
103 return
104 }
105 err = app.Server.Serve(ln)
106 if err != nil {
107 BeeLogger.Critical("ListenAndServe: ", err)
108 time.Sleep(100 * time.Microsecond)
109 endRunning <- true
110 return
111 }
112 } else {
93 err := app.Server.ListenAndServe() 113 err := app.Server.ListenAndServe()
94 if err != nil { 114 if err != nil {
95 BeeLogger.Critical("ListenAndServe: ", err) 115 BeeLogger.Critical("ListenAndServe: ", err)
96 time.Sleep(100 * time.Microsecond) 116 time.Sleep(100 * time.Microsecond)
97 endRunning <- true 117 endRunning <- true
98 } 118 }
119 }
99 }() 120 }()
100 } 121 }
101 } 122 }
......
...@@ -38,7 +38,7 @@ import ( ...@@ -38,7 +38,7 @@ import (
38 ) 38 )
39 39
40 // beego web framework version. 40 // beego web framework version.
41 const VERSION = "1.4.1" 41 const VERSION = "1.4.2"
42 42
43 type hookfunc func() error //hook function to run 43 type hookfunc func() error //hook function to run
44 var hooks []hookfunc //hook function slice to store the hookfunc 44 var hooks []hookfunc //hook function slice to store the hookfunc
...@@ -308,15 +308,20 @@ func SetStaticPath(url string, path string) *App { ...@@ -308,15 +308,20 @@ func SetStaticPath(url string, path string) *App {
308 308
309 // DelStaticPath removes the static folder setting in this url pattern in beego application. 309 // DelStaticPath removes the static folder setting in this url pattern in beego application.
310 func DelStaticPath(url string) *App { 310 func DelStaticPath(url string) *App {
311 if !strings.HasPrefix(url, "/") {
312 url = "/" + url
313 }
314 url = strings.TrimRight(url, "/")
311 delete(StaticDir, url) 315 delete(StaticDir, url)
312 return BeeApp 316 return BeeApp
313 } 317 }
314 318
315 // InsertFilter adds a FilterFunc with pattern condition and action constant. 319 // InsertFilter adds a FilterFunc with pattern condition and action constant.
316 // The pos means action constant including 320 // The pos means action constant including
317 // beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. 321 // beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
318 func InsertFilter(pattern string, pos int, filter FilterFunc) *App { 322 // The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
319 BeeApp.Handlers.InsertFilter(pattern, pos, filter) 323 func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
324 BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
320 return BeeApp 325 return BeeApp
321 } 326 }
322 327
...@@ -359,6 +364,9 @@ func initBeforeHttpRun() { ...@@ -359,6 +364,9 @@ func initBeforeHttpRun() {
359 } 364 }
360 } 365 }
361 366
367 //init mime
368 AddAPPStartHook(initMime)
369
362 // do hooks function 370 // do hooks function
363 for _, hk := range hooks { 371 for _, hk := range hooks {
364 err := hk() 372 err := hk()
...@@ -373,10 +381,8 @@ func initBeforeHttpRun() { ...@@ -373,10 +381,8 @@ func initBeforeHttpRun() {
373 if sessionConfig == "" { 381 if sessionConfig == "" {
374 sessionConfig = `{"cookieName":"` + SessionName + `",` + 382 sessionConfig = `{"cookieName":"` + SessionName + `",` +
375 `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + 383 `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
376 `"providerConfig":"` + SessionSavePath + `",` + 384 `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
377 `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + 385 `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
378 `"sessionIDHashFunc":"` + SessionHashFunc + `",` +
379 `"sessionIDHashKey":"` + SessionHashKey + `",` +
380 `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + 386 `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
381 `"domain":"` + SessionDomain + `",` + 387 `"domain":"` + SessionDomain + `",` +
382 `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` 388 `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
...@@ -404,9 +410,6 @@ func initBeforeHttpRun() { ...@@ -404,9 +410,6 @@ func initBeforeHttpRun() {
404 Get("/docs", serverDocs) 410 Get("/docs", serverDocs)
405 Get("/docs/*", serverDocs) 411 Get("/docs/*", serverDocs)
406 } 412 }
407
408 //init mime
409 AddAPPStartHook(initMime)
410 } 413 }
411 414
412 // this function is for test package init 415 // this function is for test package init
......
...@@ -81,13 +81,13 @@ func Register(name string, adapter Cache) { ...@@ -81,13 +81,13 @@ func Register(name string, adapter Cache) {
81 // Create a new cache driver by adapter name and config string. 81 // Create a new cache driver by adapter name and config string.
82 // config need to be correct JSON as string: {"interval":360}. 82 // config need to be correct JSON as string: {"interval":360}.
83 // it will start gc automatically. 83 // it will start gc automatically.
84 func NewCache(adapterName, config string) (adapter Cache, e error) { 84 func NewCache(adapterName, config string) (adapter Cache, err error) {
85 adapter, ok := adapters[adapterName] 85 adapter, ok := adapters[adapterName]
86 if !ok { 86 if !ok {
87 e = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) 87 err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
88 return 88 return
89 } 89 }
90 err := adapter.StartAndGC(config) 90 err = adapter.StartAndGC(config)
91 if err != nil { 91 if err != nil {
92 adapter = nil 92 adapter = nil
93 } 93 }
......
...@@ -75,14 +75,13 @@ func (rc *RedisCache) Get(key string) interface{} { ...@@ -75,14 +75,13 @@ func (rc *RedisCache) Get(key string) interface{} {
75 // put cache to redis. 75 // put cache to redis.
76 func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { 76 func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
77 var err error 77 var err error
78 if _, err = rc.do("SET", key, val); err != nil { 78 if _, err = rc.do("SETEX", key, timeout, val); err != nil {
79 return err 79 return err
80 } 80 }
81 81
82 if _, err = rc.do("HSET", rc.key, key, true); err != nil { 82 if _, err = rc.do("HSET", rc.key, key, true); err != nil {
83 return err 83 return err
84 } 84 }
85 _, err = rc.do("EXPIRE", key, timeout)
86 return err 85 return err
87 } 86 }
88 87
......
...@@ -48,6 +48,10 @@ type IniConfig struct { ...@@ -48,6 +48,10 @@ type IniConfig struct {
48 48
49 // ParseFile creates a new Config and parses the file configuration from the named file. 49 // ParseFile creates a new Config and parses the file configuration from the named file.
50 func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { 50 func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
51 return ini.parseFile(name)
52 }
53
54 func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
51 file, err := os.Open(name) 55 file, err := os.Open(name)
52 if err != nil { 56 if err != nil {
53 return nil, err 57 return nil, err
...@@ -66,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { ...@@ -66,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
66 70
67 var comment bytes.Buffer 71 var comment bytes.Buffer
68 buf := bufio.NewReader(file) 72 buf := bufio.NewReader(file)
73 // check the BOM
74 head, err := buf.Peek(3)
75 if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
76 for i := 1; i <= 3; i++ {
77 buf.ReadByte()
78 }
79 }
69 section := DEFAULT_SECTION 80 section := DEFAULT_SECTION
70 for { 81 for {
71 line, _, err := buf.ReadLine() 82 line, _, err := buf.ReadLine()
...@@ -108,13 +119,48 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { ...@@ -108,13 +119,48 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
108 cfg.data[section] = make(map[string]string) 119 cfg.data[section] = make(map[string]string)
109 } 120 }
110 keyValue := bytes.SplitN(line, bEqual, 2) 121 keyValue := bytes.SplitN(line, bEqual, 2)
122
123 key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
124 key = strings.ToLower(key)
125
126 // handle include "other.conf"
127 if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
128 includefiles := strings.Fields(key)
129 if includefiles[0] == "include" && len(includefiles) == 2 {
130 otherfile := strings.Trim(includefiles[1], "\"")
131 if !path.IsAbs(otherfile) {
132 otherfile = path.Join(path.Dir(name), otherfile)
133 }
134 i, err := ini.parseFile(otherfile)
135 if err != nil {
136 return nil, err
137 }
138 for sec, dt := range i.data {
139 if _, ok := cfg.data[sec]; !ok {
140 cfg.data[sec] = make(map[string]string)
141 }
142 for k, v := range dt {
143 cfg.data[sec][k] = v
144 }
145 }
146 for sec, comm := range i.sectionComment {
147 cfg.sectionComment[sec] = comm
148 }
149 for k, comm := range i.keyComment {
150 cfg.keyComment[k] = comm
151 }
152 continue
153 }
154 }
155
156 if len(keyValue) != 2 {
157 return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
158 }
111 val := bytes.TrimSpace(keyValue[1]) 159 val := bytes.TrimSpace(keyValue[1])
112 if bytes.HasPrefix(val, bDQuote) { 160 if bytes.HasPrefix(val, bDQuote) {
113 val = bytes.Trim(val, `"`) 161 val = bytes.Trim(val, `"`)
114 } 162 }
115 163
116 key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
117 key = strings.ToLower(key)
118 cfg.data[section][key] = string(val) 164 cfg.data[section][key] = string(val)
119 if comment.Len() > 0 { 165 if comment.Len() > 0 {
120 cfg.keyComment[section+"."+key] = comment.String() 166 cfg.keyComment[section+"."+key] = comment.String()
......
...@@ -69,7 +69,8 @@ func (ctx *Context) Abort(status int, body string) { ...@@ -69,7 +69,8 @@ func (ctx *Context) Abort(status int, body string) {
69 panic(e) 69 panic(e)
70 } 70 }
71 // last panic user string 71 // last panic user string
72 panic(body) 72 ctx.ResponseWriter.Write([]byte(body))
73 panic("User stop run")
73 } 74 }
74 75
75 // Write string to response body. 76 // Write string to response body.
......
...@@ -382,8 +382,37 @@ func (c *Controller) GetStrings(key string) []string { ...@@ -382,8 +382,37 @@ func (c *Controller) GetStrings(key string) []string {
382 return []string{} 382 return []string{}
383 } 383 }
384 384
385 // GetInt returns input value as int64. 385 // GetInt returns input as an int
386 func (c *Controller) GetInt(key string) (int64, error) { 386 func (c *Controller) GetInt(key string) (int, error) {
387 return strconv.Atoi(c.Ctx.Input.Query(key))
388 }
389
390 // GetInt8 return input as an int8
391 func (c *Controller) GetInt8(key string) (int8, error) {
392 i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 8)
393 i8 := int8(i64)
394
395 return i8, err
396 }
397
398 // GetInt16 returns input as an int16
399 func (c *Controller) GetInt16(key string) (int16, error) {
400 i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 16)
401 i16 := int16(i64)
402
403 return i16, err
404 }
405
406 // GetInt32 returns input as an int32
407 func (c *Controller) GetInt32(key string) (int32, error) {
408 i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
409 i32 := int32(i64)
410
411 return i32, err
412 }
413
414 // GetInt64 returns input value as int64.
415 func (c *Controller) GetInt64(key string) (int64, error) {
387 return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) 416 return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64)
388 } 417 }
389 418
......
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package beego
16
17 import (
18 "fmt"
19 "github.com/astaxie/beego/context"
20 )
21
22 func ExampleGetInt() {
23
24 i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
25 ctx := &context.Context{Input: i}
26 ctrlr := Controller{Ctx: ctx}
27
28 val, _ := ctrlr.GetInt("age")
29 fmt.Printf("%T", val)
30 //Output: int
31 }
32
33 func ExampleGetInt8() {
34
35 i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
36 ctx := &context.Context{Input: i}
37 ctrlr := Controller{Ctx: ctx}
38
39 val, _ := ctrlr.GetInt8("age")
40 fmt.Printf("%T", val)
41 //Output: int8
42 }
43
44 func ExampleGetInt16() {
45
46 i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
47 ctx := &context.Context{Input: i}
48 ctrlr := Controller{Ctx: ctx}
49
50 val, _ := ctrlr.GetInt16("age")
51 fmt.Printf("%T", val)
52 //Output: int16
53 }
54
55 func ExampleGetInt32() {
56
57 i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
58 ctx := &context.Context{Input: i}
59 ctrlr := Controller{Ctx: ctx}
60
61 val, _ := ctrlr.GetInt32("age")
62 fmt.Printf("%T", val)
63 //Output: int32
64 }
65
66 func ExampleGetInt64() {
67
68 i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
69 ctx := &context.Context{Input: i}
70 ctrlr := Controller{Ctx: ctx}
71
72 val, _ := ctrlr.GetInt64("age")
73 fmt.Printf("%T", val)
74 //Output: int64
75 }
...@@ -17,47 +17,47 @@ type ObjectController struct { ...@@ -17,47 +17,47 @@ type ObjectController struct {
17 beego.Controller 17 beego.Controller
18 } 18 }
19 19
20 func (this *ObjectController) Post() { 20 func (o *ObjectController) Post() {
21 var ob models.Object 21 var ob models.Object
22 json.Unmarshal(this.Ctx.Input.RequestBody, &ob) 22 json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
23 objectid := models.AddOne(ob) 23 objectid := models.AddOne(ob)
24 this.Data["json"] = map[string]string{"ObjectId": objectid} 24 o.Data["json"] = map[string]string{"ObjectId": objectid}
25 this.ServeJson() 25 o.ServeJson()
26 } 26 }
27 27
28 func (this *ObjectController) Get() { 28 func (o *ObjectController) Get() {
29 objectId := this.Ctx.Input.Params[":objectId"] 29 objectId := o.Ctx.Input.Params[":objectId"]
30 if objectId != "" { 30 if objectId != "" {
31 ob, err := models.GetOne(objectId) 31 ob, err := models.GetOne(objectId)
32 if err != nil { 32 if err != nil {
33 this.Data["json"] = err 33 o.Data["json"] = err
34 } else { 34 } else {
35 this.Data["json"] = ob 35 o.Data["json"] = ob
36 } 36 }
37 } else { 37 } else {
38 obs := models.GetAll() 38 obs := models.GetAll()
39 this.Data["json"] = obs 39 o.Data["json"] = obs
40 } 40 }
41 this.ServeJson() 41 o.ServeJson()
42 } 42 }
43 43
44 func (this *ObjectController) Put() { 44 func (o *ObjectController) Put() {
45 objectId := this.Ctx.Input.Params[":objectId"] 45 objectId := o.Ctx.Input.Params[":objectId"]
46 var ob models.Object 46 var ob models.Object
47 json.Unmarshal(this.Ctx.Input.RequestBody, &ob) 47 json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
48 48
49 err := models.Update(objectId, ob.Score) 49 err := models.Update(objectId, ob.Score)
50 if err != nil { 50 if err != nil {
51 this.Data["json"] = err 51 o.Data["json"] = err
52 } else { 52 } else {
53 this.Data["json"] = "update success!" 53 o.Data["json"] = "update success!"
54 } 54 }
55 this.ServeJson() 55 o.ServeJson()
56 } 56 }
57 57
58 func (this *ObjectController) Delete() { 58 func (o *ObjectController) Delete() {
59 objectId := this.Ctx.Input.Params[":objectId"] 59 objectId := o.Ctx.Input.Params[":objectId"]
60 models.Delete(objectId) 60 models.Delete(objectId)
61 this.Data["json"] = "delete success!" 61 o.Data["json"] = "delete success!"
62 this.ServeJson() 62 o.ServeJson()
63 } 63 }
......
...@@ -14,7 +14,7 @@ type MainController struct { ...@@ -14,7 +14,7 @@ type MainController struct {
14 beego.Controller 14 beego.Controller
15 } 15 }
16 16
17 func (this *MainController) Get() { 17 func (m *MainController) Get() {
18 this.Data["host"] = this.Ctx.Request.Host 18 m.Data["host"] = m.Ctx.Request.Host
19 this.TplNames = "index.tpl" 19 m.TplNames = "index.tpl"
20 } 20 }
......
...@@ -154,10 +154,10 @@ var upgrader = websocket.Upgrader{ ...@@ -154,10 +154,10 @@ var upgrader = websocket.Upgrader{
154 WriteBufferSize: 1024, 154 WriteBufferSize: 1024,
155 } 155 }
156 156
157 func (this *WSController) Get() { 157 func (w *WSController) Get() {
158 ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil) 158 ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
159 if _, ok := err.(websocket.HandshakeError); ok { 159 if _, ok := err.(websocket.HandshakeError); ok {
160 http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) 160 http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400)
161 return 161 return
162 } else if err != nil { 162 } else if err != nil {
163 return 163 return
......
...@@ -14,12 +14,18 @@ ...@@ -14,12 +14,18 @@
14 14
15 package beego 15 package beego
16 16
17 import "github.com/astaxie/beego/context"
18
19 // FilterFunc defines filter function type.
20 type FilterFunc func(*context.Context)
21
17 // FilterRouter defines filter operation before controller handler execution. 22 // FilterRouter defines filter operation before controller handler execution.
18 // it can match patterned url and do filter function when action arrives. 23 // it can match patterned url and do filter function when action arrives.
19 type FilterRouter struct { 24 type FilterRouter struct {
20 filterFunc FilterFunc 25 filterFunc FilterFunc
21 tree *Tree 26 tree *Tree
22 pattern string 27 pattern string
28 returnOnOutput bool
23 } 29 }
24 30
25 // ValidRouter check current request is valid for this filter. 31 // ValidRouter check current request is valid for this filter.
......
...@@ -32,6 +32,24 @@ func NewFlash() *FlashData { ...@@ -32,6 +32,24 @@ func NewFlash() *FlashData {
32 } 32 }
33 } 33 }
34 34
35 // Set message to flash
36 func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
37 if len(args) == 0 {
38 fd.Data[key] = msg
39 } else {
40 fd.Data[key] = fmt.Sprintf(msg, args...)
41 }
42 }
43
44 // Success writes success message to flash.
45 func (fd *FlashData) Success(msg string, args ...interface{}) {
46 if len(args) == 0 {
47 fd.Data["success"] = msg
48 } else {
49 fd.Data["success"] = fmt.Sprintf(msg, args...)
50 }
51 }
52
35 // Notice writes notice message to flash. 53 // Notice writes notice message to flash.
36 func (fd *FlashData) Notice(msg string, args ...interface{}) { 54 func (fd *FlashData) Notice(msg string, args ...interface{}) {
37 if len(args) == 0 { 55 if len(args) == 0 {
......
...@@ -25,12 +25,12 @@ type TestFlashController struct { ...@@ -25,12 +25,12 @@ type TestFlashController struct {
25 Controller 25 Controller
26 } 26 }
27 27
28 func (this *TestFlashController) TestWriteFlash() { 28 func (t *TestFlashController) TestWriteFlash() {
29 flash := NewFlash() 29 flash := NewFlash()
30 flash.Notice("TestFlashString") 30 flash.Notice("TestFlashString")
31 flash.Store(&this.Controller) 31 flash.Store(&t.Controller)
32 // we choose to serve json because we don't want to load a template html file 32 // we choose to serve json because we don't want to load a template html file
33 this.ServeJson(true) 33 t.ServeJson(true)
34 } 34 }
35 35
36 func TestFlashHeader(t *testing.T) { 36 func TestFlashHeader(t *testing.T) {
......
...@@ -37,6 +37,7 @@ import ( ...@@ -37,6 +37,7 @@ import (
37 "encoding/xml" 37 "encoding/xml"
38 "io" 38 "io"
39 "io/ioutil" 39 "io/ioutil"
40 "log"
40 "mime/multipart" 41 "mime/multipart"
41 "net" 42 "net"
42 "net/http" 43 "net/http"
...@@ -275,35 +276,36 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { ...@@ -275,35 +276,36 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
275 } else { 276 } else {
276 b.url = b.url + "?" + paramBody 277 b.url = b.url + "?" + paramBody
277 } 278 }
278 } else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 { 279 } else if b.req.Method == "POST" && b.req.Body == nil {
279 if len(b.files) > 0 { 280 if len(b.files) > 0 {
280 bodyBuf := &bytes.Buffer{} 281 pr, pw := io.Pipe()
281 bodyWriter := multipart.NewWriter(bodyBuf) 282 bodyWriter := multipart.NewWriter(pw)
283 go func() {
282 for formname, filename := range b.files { 284 for formname, filename := range b.files {
283 fileWriter, err := bodyWriter.CreateFormFile(formname, filename) 285 fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
284 if err != nil { 286 if err != nil {
285 return nil, err 287 log.Fatal(err)
286 } 288 }
287 fh, err := os.Open(filename) 289 fh, err := os.Open(filename)
288 if err != nil { 290 if err != nil {
289 return nil, err 291 log.Fatal(err)
290 } 292 }
291 //iocopy 293 //iocopy
292 _, err = io.Copy(fileWriter, fh) 294 _, err = io.Copy(fileWriter, fh)
293 fh.Close() 295 fh.Close()
294 if err != nil { 296 if err != nil {
295 return nil, err 297 log.Fatal(err)
296 } 298 }
297 } 299 }
298 for k, v := range b.params { 300 for k, v := range b.params {
299 bodyWriter.WriteField(k, v) 301 bodyWriter.WriteField(k, v)
300 } 302 }
301 contentType := bodyWriter.FormDataContentType()
302 bodyWriter.Close() 303 bodyWriter.Close()
303 b.Header("Content-Type", contentType) 304 pw.Close()
304 b.req.Body = ioutil.NopCloser(bodyBuf) 305 }()
305 b.req.ContentLength = int64(bodyBuf.Len()) 306 b.Header("Content-Type", bodyWriter.FormDataContentType())
306 } else { 307 b.req.Body = ioutil.NopCloser(pr)
308 } else if len(paramBody) > 0 {
307 b.Header("Content-Type", "application/x-www-form-urlencoded") 309 b.Header("Content-Type", "application/x-www-form-urlencoded")
308 b.Body(paramBody) 310 b.Body(paramBody)
309 } 311 }
...@@ -355,7 +357,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { ...@@ -355,7 +357,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
355 Jar: jar, 357 Jar: jar,
356 } 358 }
357 359
358 if b.setting.UserAgent != "" { 360 if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
359 b.req.Header.Set("User-Agent", b.setting.UserAgent) 361 b.req.Header.Set("User-Agent", b.setting.UserAgent)
360 } 362 }
361 363
......
...@@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) { ...@@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) {
66 } 66 }
67 } 67 }
68 68
69 func TestPostFile(t *testing.T) { 69 //func TestPostFile(t *testing.T) {
70 v := "smallfish" 70 // v := "smallfish"
71 req := Post("http://httpbin.org/post") 71 // req := Post("http://httpbin.org/post")
72 req.Param("username", v) 72 // req.Debug(true)
73 req.PostFile("uploadfile", "httplib_test.go") 73 // req.Param("username", v)
74 74 // req.PostFile("uploadfile", "httplib_test.go")
75 str, err := req.String() 75
76 if err != nil { 76 // str, err := req.String()
77 t.Fatal(err) 77 // if err != nil {
78 } 78 // t.Fatal(err)
79 t.Log(str) 79 // }
80 80 // t.Log(str)
81 n := strings.Index(str, v) 81
82 if n == -1 { 82 // n := strings.Index(str, v)
83 t.Fatal(v + " not found in post") 83 // if n == -1 {
84 } 84 // t.Fatal(v + " not found in post")
85 } 85 // }
86 //}
86 87
87 func TestSimplePut(t *testing.T) { 88 func TestSimplePut(t *testing.T) {
88 str, err := Put("http://httpbin.org/put").String() 89 str, err := Put("http://httpbin.org/put").String()
...@@ -203,3 +204,13 @@ func TestToFile(t *testing.T) { ...@@ -203,3 +204,13 @@ func TestToFile(t *testing.T) {
203 t.Fatal(err) 204 t.Fatal(err)
204 } 205 }
205 } 206 }
207
208 func TestHeader(t *testing.T) {
209 req := Get("http://httpbin.org/headers")
210 req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
211 str, err := req.String()
212 if err != nil {
213 t.Fatal(err)
214 }
215 t.Log(str)
216 }
......
...@@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { ...@@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
155 lm.level = loglevel 155 lm.level = loglevel
156 if bl.enableFuncCallDepth { 156 if bl.enableFuncCallDepth {
157 _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) 157 _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
158 if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) {
159 _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
160 }
158 if ok { 161 if ok {
159 _, filename := path.Split(file) 162 _, filename := path.Split(file)
160 lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) 163 lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
......
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package migration
16
17 type Table struct {
18 TableName string
19 Columns []*Column
20 }
21
22 func (t *Table) Create() string {
23 return ""
24 }
25
26 func (t *Table) Drop() string {
27 return ""
28 }
29
30 type Column struct {
31 Name string
32 Type string
33 Default interface{}
34 }
35
36 func Create(tbname string, columns ...Column) string {
37 return ""
38 }
39
40 func Drop(tbname string, columns ...Column) string {
41 return ""
42 }
43
44 func TableDDL(tbname string, columns ...Column) string {
45 return ""
46 }
...@@ -217,7 +217,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { ...@@ -217,7 +217,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
217 n.handlers.routers[k] = t 217 n.handlers.routers[k] = t
218 } 218 }
219 } 219 }
220 if n.handlers.enableFilter { 220 if ni.handlers.enableFilter {
221 for pos, filterList := range ni.handlers.filters { 221 for pos, filterList := range ni.handlers.filters {
222 for _, mr := range filterList { 222 for _, mr := range filterList {
223 t := NewTree() 223 t := NewTree()
......
...@@ -6,8 +6,6 @@ A powerful orm framework for go. ...@@ -6,8 +6,6 @@ A powerful orm framework for go.
6 6
7 It is heavily influenced by Django ORM, SQLAlchemy. 7 It is heavily influenced by Django ORM, SQLAlchemy.
8 8
9 now, beta, unstable, may be changing some api make your app build failed.
10
11 **Support Database:** 9 **Support Database:**
12 10
13 * MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) 11 * MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
......
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package orm
16
17 import "errors"
18
19 type QueryBuilder interface {
20 Select(fields ...string) QueryBuilder
21 From(tables ...string) QueryBuilder
22 InnerJoin(table string) QueryBuilder
23 LeftJoin(table string) QueryBuilder
24 RightJoin(table string) QueryBuilder
25 On(cond string) QueryBuilder
26 Where(cond string) QueryBuilder
27 And(cond string) QueryBuilder
28 Or(cond string) QueryBuilder
29 In(vals ...string) QueryBuilder
30 OrderBy(fields ...string) QueryBuilder
31 Asc() QueryBuilder
32 Desc() QueryBuilder
33 Limit(limit int) QueryBuilder
34 Offset(offset int) QueryBuilder
35 GroupBy(fields ...string) QueryBuilder
36 Having(cond string) QueryBuilder
37 Update(tables ...string) QueryBuilder
38 Set(kv ...string) QueryBuilder
39 Delete(tables ...string) QueryBuilder
40 InsertInto(table string, fields ...string) QueryBuilder
41 Values(vals ...string) QueryBuilder
42 Subquery(sub string, alias string) string
43 String() string
44 }
45
46 func NewQueryBuilder(driver string) (qb QueryBuilder, err error) {
47 if driver == "mysql" {
48 qb = new(MySQLQueryBuilder)
49 } else if driver == "postgres" {
50 err = errors.New("postgres query builder is not supported yet!")
51 } else if driver == "sqlite" {
52 err = errors.New("sqlite query builder is not supported yet!")
53 } else {
54 err = errors.New("unknown driver for query builder!")
55 }
56 return
57 }
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package orm
16
17 import (
18 "fmt"
19 "strconv"
20 "strings"
21 )
22
23 const COMMA_SPACE = ", "
24
25 type MySQLQueryBuilder struct {
26 Tokens []string
27 }
28
29 func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder {
30 qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, COMMA_SPACE))
31 return qb
32 }
33
34 func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder {
35 qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, COMMA_SPACE))
36 return qb
37 }
38
39 func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder {
40 qb.Tokens = append(qb.Tokens, "INNER JOIN", table)
41 return qb
42 }
43
44 func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder {
45 qb.Tokens = append(qb.Tokens, "LEFT JOIN", table)
46 return qb
47 }
48
49 func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder {
50 qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table)
51 return qb
52 }
53
54 func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder {
55 qb.Tokens = append(qb.Tokens, "ON", cond)
56 return qb
57 }
58
59 func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder {
60 qb.Tokens = append(qb.Tokens, "WHERE", cond)
61 return qb
62 }
63
64 func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder {
65 qb.Tokens = append(qb.Tokens, "AND", cond)
66 return qb
67 }
68
69 func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder {
70 qb.Tokens = append(qb.Tokens, "OR", cond)
71 return qb
72 }
73
74 func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder {
75 qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, COMMA_SPACE), ")")
76 return qb
77 }
78
79 func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder {
80 qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, COMMA_SPACE))
81 return qb
82 }
83
84 func (qb *MySQLQueryBuilder) Asc() QueryBuilder {
85 qb.Tokens = append(qb.Tokens, "ASC")
86 return qb
87 }
88
89 func (qb *MySQLQueryBuilder) Desc() QueryBuilder {
90 qb.Tokens = append(qb.Tokens, "DESC")
91 return qb
92 }
93
94 func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder {
95 qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit))
96 return qb
97 }
98
99 func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder {
100 qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset))
101 return qb
102 }
103
104 func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder {
105 qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, COMMA_SPACE))
106 return qb
107 }
108
109 func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder {
110 qb.Tokens = append(qb.Tokens, "HAVING", cond)
111 return qb
112 }
113
114 func (qb *MySQLQueryBuilder) Update(tables ...string) QueryBuilder {
115 qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, COMMA_SPACE))
116 return qb
117 }
118
119 func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder {
120 qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, COMMA_SPACE))
121 return qb
122 }
123
124 func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder {
125 qb.Tokens = append(qb.Tokens, "DELETE")
126 if len(tables) != 0 {
127 qb.Tokens = append(qb.Tokens, strings.Join(tables, COMMA_SPACE))
128 }
129 return qb
130 }
131
132 func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder {
133 qb.Tokens = append(qb.Tokens, "INSERT INTO", table)
134 if len(fields) != 0 {
135 fieldsStr := strings.Join(fields, COMMA_SPACE)
136 qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")")
137 }
138 return qb
139 }
140
141 func (qb *MySQLQueryBuilder) Values(vals ...string) QueryBuilder {
142 valsStr := strings.Join(vals, COMMA_SPACE)
143 qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")")
144 return qb
145 }
146
147 func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string {
148 return fmt.Sprintf("(%s) AS %s", sub, alias)
149 }
150
151 func (qb *MySQLQueryBuilder) String() string {
152 return strings.Join(qb.Tokens, " ")
153 }
...@@ -42,20 +42,25 @@ func init() { ...@@ -42,20 +42,25 @@ func init() {
42 42
43 var ( 43 var (
44 lastupdateFilename string = "lastupdate.tmp" 44 lastupdateFilename string = "lastupdate.tmp"
45 commentFilename string
45 pkgLastupdate map[string]int64 46 pkgLastupdate map[string]int64
46 genInfoList map[string][]ControllerComments 47 genInfoList map[string][]ControllerComments
47 ) 48 )
48 49
50 const COMMENTFL = "commentsRouter_"
51
49 func init() { 52 func init() {
50 pkgLastupdate = make(map[string]int64) 53 pkgLastupdate = make(map[string]int64)
51 genInfoList = make(map[string][]ControllerComments)
52 } 54 }
53 55
54 func parserPkg(pkgRealpath, pkgpath string) error { 56 func parserPkg(pkgRealpath, pkgpath string) error {
57 rep := strings.NewReplacer("/", "_", ".", "_")
58 commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go"
55 if !compareFile(pkgRealpath) { 59 if !compareFile(pkgRealpath) {
56 Info(pkgRealpath + " don't has updated") 60 Info(pkgRealpath + " don't has updated")
57 return nil 61 return nil
58 } 62 }
63 genInfoList = make(map[string][]ControllerComments)
59 fileSet := token.NewFileSet() 64 fileSet := token.NewFileSet()
60 astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { 65 astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
61 name := info.Name() 66 name := info.Name()
...@@ -155,7 +160,7 @@ func genRouterCode() { ...@@ -155,7 +160,7 @@ func genRouterCode() {
155 } 160 }
156 } 161 }
157 if globalinfo != "" { 162 if globalinfo != "" {
158 f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go")) 163 f, err := os.Create(path.Join(workPath, "routers", commentFilename))
159 if err != nil { 164 if err != nil {
160 panic(err) 165 panic(err)
161 } 166 }
...@@ -165,7 +170,7 @@ func genRouterCode() { ...@@ -165,7 +170,7 @@ func genRouterCode() {
165 } 170 }
166 171
167 func compareFile(pkgRealpath string) bool { 172 func compareFile(pkgRealpath string) bool {
168 if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) { 173 if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) {
169 return true 174 return true
170 } 175 }
171 if utils.FileExists(path.Join(workPath, lastupdateFilename)) { 176 if utils.FileExists(path.Join(workPath, lastupdateFilename)) {
......
...@@ -73,8 +73,30 @@ var ( ...@@ -73,8 +73,30 @@ var (
73 "GetControllerAndAction"} 73 "GetControllerAndAction"}
74 74
75 url_placeholder = "{{placeholder}}" 75 url_placeholder = "{{placeholder}}"
76 DefaultLogFilter FilterHandler = &logFilter{}
76 ) 77 )
77 78
79 type FilterHandler interface {
80 Filter(*beecontext.Context) bool
81 }
82
83 // default log filter static file will not show
84 type logFilter struct {
85 }
86
87 func (l *logFilter) Filter(ctx *beecontext.Context) bool {
88 requestPath := path.Clean(ctx.Input.Request.URL.Path)
89 if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
90 return true
91 }
92 for prefix, _ := range StaticDir {
93 if strings.HasPrefix(requestPath, prefix) {
94 return true
95 }
96 }
97 return false
98 }
99
78 // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter 100 // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
79 func ExceptMethodAppend(action string) { 101 func ExceptMethodAppend(action string) {
80 exceptMethod = append(exceptMethod, action) 102 exceptMethod = append(exceptMethod, action)
...@@ -163,6 +185,9 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM ...@@ -163,6 +185,9 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
163 } 185 }
164 186
165 func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) { 187 func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {
188 if !RouterCaseSensitive {
189 pattern = strings.ToLower(pattern)
190 }
166 if t, ok := p.routers[method]; ok { 191 if t, ok := p.routers[method]; ok {
167 t.AddRouter(pattern, r) 192 t.AddRouter(pattern, r)
168 } else { 193 } else {
...@@ -376,11 +401,21 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) ...@@ -376,11 +401,21 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface)
376 } 401 }
377 402
378 // Add a FilterFunc with pattern rule and action constant. 403 // Add a FilterFunc with pattern rule and action constant.
379 func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error { 404 // The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
405 func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
406
380 mr := new(FilterRouter) 407 mr := new(FilterRouter)
381 mr.tree = NewTree() 408 mr.tree = NewTree()
382 mr.pattern = pattern 409 mr.pattern = pattern
383 mr.filterFunc = filter 410 mr.filterFunc = filter
411 if !RouterCaseSensitive {
412 pattern = strings.ToLower(pattern)
413 }
414 if len(params) == 0 {
415 mr.returnOnOutput = true
416 } else {
417 mr.returnOnOutput = params[0]
418 }
384 mr.tree.AddRouter(pattern, true) 419 mr.tree.AddRouter(pattern, true)
385 return p.insertFilterRouter(pos, mr) 420 return p.insertFilterRouter(pos, mr)
386 } 421 }
...@@ -415,10 +450,10 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { ...@@ -415,10 +450,10 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
415 } 450 }
416 } 451 }
417 } 452 }
418 controllName := strings.Join(paths[:len(paths)-1], ".") 453 controllName := strings.Join(paths[:len(paths)-1], "/")
419 methodName := paths[len(paths)-1] 454 methodName := paths[len(paths)-1]
420 for _, t := range p.routers { 455 for m, t := range p.routers {
421 ok, url := p.geturl(t, "/", controllName, methodName, params) 456 ok, url := p.geturl(t, "/", controllName, methodName, params, m)
422 if ok { 457 if ok {
423 return url 458 return url
424 } 459 }
...@@ -426,24 +461,25 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { ...@@ -426,24 +461,25 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
426 return "" 461 return ""
427 } 462 }
428 463
429 func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) { 464 func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
430 for k, subtree := range t.fixrouters { 465 for k, subtree := range t.fixrouters {
431 u := path.Join(url, k) 466 u := path.Join(url, k)
432 ok, u := p.geturl(subtree, u, controllName, methodName, params) 467 ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
433 if ok { 468 if ok {
434 return ok, u 469 return ok, u
435 } 470 }
436 } 471 }
437 if t.wildcard != nil { 472 if t.wildcard != nil {
438 url = path.Join(url, url_placeholder) 473 u := path.Join(url, url_placeholder)
439 ok, u := p.geturl(t.wildcard, url, controllName, methodName, params) 474 ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
440 if ok { 475 if ok {
441 return ok, u 476 return ok, u
442 } 477 }
443 } 478 }
444 for _, l := range t.leaves { 479 for _, l := range t.leaves {
445 if c, ok := l.runObject.(*controllerInfo); ok { 480 if c, ok := l.runObject.(*controllerInfo); ok {
446 if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName { 481 if c.routerType == routerTypeBeego &&
482 strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
447 find := false 483 find := false
448 if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok { 484 if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
449 if len(c.methods) == 0 { 485 if len(c.methods) == 0 {
...@@ -455,8 +491,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin ...@@ -455,8 +491,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
455 } 491 }
456 } 492 }
457 if !find { 493 if !find {
458 for _, md := range c.methods { 494 for m, md := range c.methods {
459 if md == methodName { 495 if (m == "*" || m == httpMethod) && md == methodName {
460 find = true 496 find = true
461 } 497 }
462 } 498 }
...@@ -564,15 +600,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -564,15 +600,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
564 context.Output.Context = context 600 context.Output.Context = context
565 context.Output.EnableGzip = EnableGzip 601 context.Output.EnableGzip = EnableGzip
566 602
603 var urlPath string
604 if !RouterCaseSensitive {
605 urlPath = strings.ToLower(r.URL.Path)
606 } else {
607 urlPath = r.URL.Path
608 }
567 // defined filter function 609 // defined filter function
568 do_filter := func(pos int) (started bool) { 610 do_filter := func(pos int) (started bool) {
569 if p.enableFilter { 611 if p.enableFilter {
570 if l, ok := p.filters[pos]; ok { 612 if l, ok := p.filters[pos]; ok {
571 for _, filterR := range l { 613 for _, filterR := range l {
572 if ok, p := filterR.ValidRouter(r.URL.Path); ok { 614 if ok, p := filterR.ValidRouter(urlPath); ok {
573 context.Input.Params = p 615 context.Input.Params = p
574 filterR.filterFunc(context) 616 filterR.filterFunc(context)
575 if w.started { 617 if filterR.returnOnOutput && w.started {
576 return true 618 return true
577 } 619 }
578 } 620 }
...@@ -602,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -602,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
602 644
603 // session init 645 // session init
604 if SessionOn { 646 if SessionOn {
605 context.Input.CruSession = GlobalSessions.SessionStart(w, r) 647 var err error
648 context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
649 if err != nil {
650 Error(err)
651 middleware.Exception("503", rw, r, "")
652 return
653 }
606 defer func() { 654 defer func() {
607 context.Input.CruSession.SessionRelease(w) 655 context.Input.CruSession.SessionRelease(w)
608 }() 656 }()
...@@ -626,8 +674,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -626,8 +674,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
626 } 674 }
627 675
628 if !findrouter { 676 if !findrouter {
629 if t, ok := p.routers[r.Method]; ok { 677 http_method := r.Method
630 runObject, p := t.Match(r.URL.Path) 678
679 if http_method == "POST" && context.Input.Query("_method") == "PUT" {
680 http_method = "PUT"
681 }
682
683 if http_method == "POST" && context.Input.Query("_method") == "DELETE" {
684 http_method = "DELETE"
685 }
686
687 if t, ok := p.routers[http_method]; ok {
688 runObject, p := t.Match(urlPath)
631 if r, ok := runObject.(*controllerInfo); ok { 689 if r, ok := runObject.(*controllerInfo); ok {
632 routerInfo = r 690 routerInfo = r
633 findrouter = true 691 findrouter = true
...@@ -783,8 +841,10 @@ Admin: ...@@ -783,8 +841,10 @@ Admin:
783 } else { 841 } else {
784 devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") 842 devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch")
785 } 843 }
844 if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) {
786 Debug(devinfo) 845 Debug(devinfo)
787 } 846 }
847 }
788 848
789 // Call WriteHeader if status code has been set changed 849 // Call WriteHeader if status code has been set changed
790 if context.Output.Status != 0 { 850 if context.Output.Status != 0 {
...@@ -797,7 +857,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques ...@@ -797,7 +857,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
797 if err == USERSTOPRUN { 857 if err == USERSTOPRUN {
798 return 858 return
799 } 859 }
800 if _, ok := err.(middleware.HTTPException); ok { 860 if he, ok := err.(middleware.HTTPException); ok {
861 rw.WriteHeader(he.StatusCode)
862 rw.Write([]byte(he.Description))
801 // catch intented errors, only for HTTP 4XX and 5XX 863 // catch intented errors, only for HTTP 4XX and 5XX
802 } else { 864 } else {
803 if RunMode == "dev" { 865 if RunMode == "dev" {
...@@ -829,10 +891,16 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques ...@@ -829,10 +891,16 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
829 } else { 891 } else {
830 // in production model show all infomation 892 // in production model show all infomation
831 if ErrorsShow { 893 if ErrorsShow {
832 handler := p.getErrorHandler(fmt.Sprint(err)) 894 if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
895 handler(rw, r)
896 return
897 } else if handler, ok := middleware.ErrorMaps["503"]; ok {
833 handler(rw, r) 898 handler(rw, r)
834 return 899 return
835 } else { 900 } else {
901 rw.Write([]byte(fmt.Sprint(err)))
902 }
903 } else {
836 Critical("the request url is ", r.URL.Path) 904 Critical("the request url is ", r.URL.Path)
837 Critical("Handler crashed with error", err) 905 Critical("Handler crashed with error", err)
838 for i := 1; ; i++ { 906 for i := 1; ; i++ {
...@@ -850,24 +918,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques ...@@ -850,24 +918,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
850 } 918 }
851 } 919 }
852 920
853 // there always should be error handler that sets error code accordingly for all unhandled errors.
854 // in order to have custom UI for error page it's necessary to override "500" error.
855 func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
856 handler := middleware.SimpleServerError
857 ok := true
858 if errorCode != "" {
859 handler, ok = middleware.ErrorMaps[errorCode]
860 if !ok {
861 handler, ok = middleware.ErrorMaps["500"]
862 }
863 if !ok || handler == nil {
864 handler = middleware.SimpleServerError
865 }
866 }
867
868 return handler
869 }
870
871 //responseWriter is a wrapper for the http.ResponseWriter 921 //responseWriter is a wrapper for the http.ResponseWriter
872 //started set to true if response was written to then don't execute other handler 922 //started set to true if response was written to then don't execute other handler
873 type responseWriter struct { 923 type responseWriter struct {
......
...@@ -17,6 +17,7 @@ package beego ...@@ -17,6 +17,7 @@ package beego
17 import ( 17 import (
18 "net/http" 18 "net/http"
19 "net/http/httptest" 19 "net/http/httptest"
20 "strings"
20 "testing" 21 "testing"
21 22
22 "github.com/astaxie/beego/context" 23 "github.com/astaxie/beego/context"
...@@ -26,33 +27,33 @@ type TestController struct { ...@@ -26,33 +27,33 @@ type TestController struct {
26 Controller 27 Controller
27 } 28 }
28 29
29 func (this *TestController) Get() { 30 func (tc *TestController) Get() {
30 this.Data["Username"] = "astaxie" 31 tc.Data["Username"] = "astaxie"
31 this.Ctx.Output.Body([]byte("ok")) 32 tc.Ctx.Output.Body([]byte("ok"))
32 } 33 }
33 34
34 func (this *TestController) Post() { 35 func (tc *TestController) Post() {
35 this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) 36 tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
36 } 37 }
37 38
38 func (this *TestController) Param() { 39 func (tc *TestController) Param() {
39 this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) 40 tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
40 } 41 }
41 42
42 func (this *TestController) List() { 43 func (tc *TestController) List() {
43 this.Ctx.Output.Body([]byte("i am list")) 44 tc.Ctx.Output.Body([]byte("i am list"))
44 } 45 }
45 46
46 func (this *TestController) Params() { 47 func (tc *TestController) Params() {
47 this.Ctx.Output.Body([]byte(this.Ctx.Input.Params["0"] + this.Ctx.Input.Params["1"] + this.Ctx.Input.Params["2"])) 48 tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"]))
48 } 49 }
49 50
50 func (this *TestController) Myext() { 51 func (tc *TestController) Myext() {
51 this.Ctx.Output.Body([]byte(this.Ctx.Input.Param(":ext"))) 52 tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
52 } 53 }
53 54
54 func (this *TestController) GetUrl() { 55 func (tc *TestController) GetUrl() {
55 this.Ctx.Output.Body([]byte(this.UrlFor(".Myext"))) 56 tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext")))
56 } 57 }
57 58
58 func (t *TestController) GetParams() { 59 func (t *TestController) GetParams() {
...@@ -385,3 +386,196 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request ...@@ -385,3 +386,196 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request
385 386
386 return recorder, request 387 return recorder, request
387 } 388 }
389
390 // Execution point: BeforeRouter
391 // expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
392 func TestFilterBeforeRouter(t *testing.T) {
393 testName := "TestFilterBeforeRouter"
394 url := "/beforeRouter"
395
396 mux := NewControllerRegister()
397 mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)
398
399 mux.Get(url, beegoFilterFunc)
400
401 rw, r := testRequest("GET", url)
402 mux.ServeHTTP(rw, r)
403
404 if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
405 t.Errorf(testName + " BeforeRouter did not run")
406 }
407 if strings.Contains(rw.Body.String(), "hello") == true {
408 t.Errorf(testName + " BeforeRouter did not return properly")
409 }
410 }
411
412 // Execution point: BeforeExec
413 // expectation: only BeforeExec function is executed, match as router determines route only
414 func TestFilterBeforeExec(t *testing.T) {
415 testName := "TestFilterBeforeExec"
416 url := "/beforeExec"
417
418 mux := NewControllerRegister()
419 mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
420 mux.InsertFilter(url, BeforeExec, beegoBeforeExec1)
421
422 mux.Get(url, beegoFilterFunc)
423
424 rw, r := testRequest("GET", url)
425 mux.ServeHTTP(rw, r)
426
427 if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
428 t.Errorf(testName + " BeforeExec did not run")
429 }
430 if strings.Contains(rw.Body.String(), "hello") == true {
431 t.Errorf(testName + " BeforeExec did not return properly")
432 }
433 if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
434 t.Errorf(testName + " BeforeRouter ran in error")
435 }
436 }
437
438 // Execution point: AfterExec
439 // expectation: only AfterExec function is executed, match as router handles
440 func TestFilterAfterExec(t *testing.T) {
441 testName := "TestFilterAfterExec"
442 url := "/afterExec"
443
444 mux := NewControllerRegister()
445 mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
446 mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
447 mux.InsertFilter(url, AfterExec, beegoAfterExec1)
448
449 mux.Get(url, beegoFilterFunc)
450
451 rw, r := testRequest("GET", url)
452 mux.ServeHTTP(rw, r)
453
454 if strings.Contains(rw.Body.String(), "AfterExec1") == false {
455 t.Errorf(testName + " AfterExec did not run")
456 }
457 if strings.Contains(rw.Body.String(), "hello") == false {
458 t.Errorf(testName + " handler did not run properly")
459 }
460 if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
461 t.Errorf(testName + " BeforeRouter ran in error")
462 }
463 if strings.Contains(rw.Body.String(), "BeforeExec") == true {
464 t.Errorf(testName + " BeforeExec ran in error")
465 }
466 }
467
468 // Execution point: FinishRouter
469 // expectation: only FinishRouter function is executed, match as router handles
470 func TestFilterFinishRouter(t *testing.T) {
471 testName := "TestFilterFinishRouter"
472 url := "/finishRouter"
473
474 mux := NewControllerRegister()
475 mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
476 mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
477 mux.InsertFilter(url, AfterExec, beegoFilterNoOutput)
478 mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
479
480 mux.Get(url, beegoFilterFunc)
481
482 rw, r := testRequest("GET", url)
483 mux.ServeHTTP(rw, r)
484
485 if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
486 t.Errorf(testName + " FinishRouter did not run")
487 }
488 if strings.Contains(rw.Body.String(), "hello") == false {
489 t.Errorf(testName + " handler did not run properly")
490 }
491 if strings.Contains(rw.Body.String(), "AfterExec1") == true {
492 t.Errorf(testName + " AfterExec ran in error")
493 }
494 if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
495 t.Errorf(testName + " BeforeRouter ran in error")
496 }
497 if strings.Contains(rw.Body.String(), "BeforeExec") == true {
498 t.Errorf(testName + " BeforeExec ran in error")
499 }
500 }
501
502 // Execution point: FinishRouter
503 // expectation: only first FinishRouter function is executed, match as router handles
504 func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
505 testName := "TestFilterFinishRouterMultiFirstOnly"
506 url := "/finishRouterMultiFirstOnly"
507
508 mux := NewControllerRegister()
509 mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
510 mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
511
512 mux.Get(url, beegoFilterFunc)
513
514 rw, r := testRequest("GET", url)
515 mux.ServeHTTP(rw, r)
516
517 if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
518 t.Errorf(testName + " FinishRouter1 did not run")
519 }
520 if strings.Contains(rw.Body.String(), "hello") == false {
521 t.Errorf(testName + " handler did not run properly")
522 }
523 // not expected in body
524 if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
525 t.Errorf(testName + " FinishRouter2 did run")
526 }
527 }
528
529 // Execution point: FinishRouter
530 // expectation: both FinishRouter functions execute, match as router handles
531 func TestFilterFinishRouterMulti(t *testing.T) {
532 testName := "TestFilterFinishRouterMulti"
533 url := "/finishRouterMulti"
534
535 mux := NewControllerRegister()
536 mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
537 mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
538
539 mux.Get(url, beegoFilterFunc)
540
541 rw, r := testRequest("GET", url)
542 mux.ServeHTTP(rw, r)
543
544 if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
545 t.Errorf(testName + " FinishRouter1 did not run")
546 }
547 if strings.Contains(rw.Body.String(), "hello") == false {
548 t.Errorf(testName + " handler did not run properly")
549 }
550 if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
551 t.Errorf(testName + " FinishRouter2 did not run properly")
552 }
553 }
554
555 func beegoFilterNoOutput(ctx *context.Context) {
556 return
557 }
558 func beegoBeforeRouter1(ctx *context.Context) {
559 ctx.WriteString("|BeforeRouter1")
560 }
561 func beegoBeforeRouter2(ctx *context.Context) {
562 ctx.WriteString("|BeforeRouter2")
563 }
564 func beegoBeforeExec1(ctx *context.Context) {
565 ctx.WriteString("|BeforeExec1")
566 }
567 func beegoBeforeExec2(ctx *context.Context) {
568 ctx.WriteString("|BeforeExec2")
569 }
570 func beegoAfterExec1(ctx *context.Context) {
571 ctx.WriteString("|AfterExec1")
572 }
573 func beegoAfterExec2(ctx *context.Context) {
574 ctx.WriteString("|AfterExec2")
575 }
576 func beegoFinishRouter1(ctx *context.Context) {
577 ctx.WriteString("|FinishRouter1")
578 }
579 func beegoFinishRouter2(ctx *context.Context) {
580 ctx.WriteString("|FinishRouter2")
581 }
......
...@@ -109,7 +109,7 @@ func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) { ...@@ -109,7 +109,7 @@ func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
109 return 109 return
110 } 110 }
111 111
112 c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime) 112 c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
113 } 113 }
114 114
115 // redis session provider 115 // redis session provider
......
...@@ -29,7 +29,10 @@ func TestCookie(t *testing.T) { ...@@ -29,7 +29,10 @@ func TestCookie(t *testing.T) {
29 } 29 }
30 r, _ := http.NewRequest("GET", "/", nil) 30 r, _ := http.NewRequest("GET", "/", nil)
31 w := httptest.NewRecorder() 31 w := httptest.NewRecorder()
32 sess := globalSessions.SessionStart(w, r) 32 sess, err := globalSessions.SessionStart(w, r)
33 if err != nil {
34 t.Fatal("set error,", err)
35 }
33 err = sess.Set("username", "astaxie") 36 err = sess.Set("username", "astaxie")
34 if err != nil { 37 if err != nil {
35 t.Fatal("set error,", err) 38 t.Fatal("set error,", err)
......
...@@ -26,9 +26,12 @@ func TestMem(t *testing.T) { ...@@ -26,9 +26,12 @@ func TestMem(t *testing.T) {
26 go globalSessions.GC() 26 go globalSessions.GC()
27 r, _ := http.NewRequest("GET", "/", nil) 27 r, _ := http.NewRequest("GET", "/", nil)
28 w := httptest.NewRecorder() 28 w := httptest.NewRecorder()
29 sess := globalSessions.SessionStart(w, r) 29 sess, err := globalSessions.SessionStart(w, r)
30 if err != nil {
31 t.Fatal("set error,", err)
32 }
30 defer sess.SessionRelease(w) 33 defer sess.SessionRelease(w)
31 err := sess.Set("username", "astaxie") 34 err = sess.Set("username", "astaxie")
32 if err != nil { 35 if err != nil {
33 t.Fatal("set error,", err) 36 t.Fatal("set error,", err)
34 } 37 }
......
...@@ -28,19 +28,13 @@ ...@@ -28,19 +28,13 @@
28 package session 28 package session
29 29
30 import ( 30 import (
31 "crypto/hmac"
32 "crypto/md5"
33 "crypto/rand" 31 "crypto/rand"
34 "crypto/sha1"
35 "encoding/hex" 32 "encoding/hex"
36 "encoding/json" 33 "encoding/json"
37 "fmt" 34 "fmt"
38 "io"
39 "net/http" 35 "net/http"
40 "net/url" 36 "net/url"
41 "time" 37 "time"
42
43 "github.com/astaxie/beego/utils"
44 ) 38 )
45 39
46 // SessionStore contains all data for one session process with specific id. 40 // SessionStore contains all data for one session process with specific id.
...@@ -86,11 +80,10 @@ type managerConfig struct { ...@@ -86,11 +80,10 @@ type managerConfig struct {
86 Gclifetime int64 `json:"gclifetime"` 80 Gclifetime int64 `json:"gclifetime"`
87 Maxlifetime int64 `json:"maxLifetime"` 81 Maxlifetime int64 `json:"maxLifetime"`
88 Secure bool `json:"secure"` 82 Secure bool `json:"secure"`
89 SessionIDHashFunc string `json:"sessionIDHashFunc"`
90 SessionIDHashKey string `json:"sessionIDHashKey"`
91 CookieLifeTime int `json:"cookieLifeTime"` 83 CookieLifeTime int `json:"cookieLifeTime"`
92 ProviderConfig string `json:"providerConfig"` 84 ProviderConfig string `json:"providerConfig"`
93 Domain string `json:"domain"` 85 Domain string `json:"domain"`
86 SessionIdLength int64 `json:"sessionIdLength"`
94 } 87 }
95 88
96 // Manager contains Provider and its configuration. 89 // Manager contains Provider and its configuration.
...@@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) { ...@@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) {
129 if err != nil { 122 if err != nil {
130 return nil, err 123 return nil, err
131 } 124 }
132 if cf.SessionIDHashFunc == "" { 125
133 cf.SessionIDHashFunc = "sha1" 126 if cf.SessionIdLength == 0 {
134 } 127 cf.SessionIdLength = 16
135 if cf.SessionIDHashKey == "" {
136 cf.SessionIDHashKey = string(generateRandomKey(16))
137 } 128 }
138 129
139 return &Manager{ 130 return &Manager{
...@@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) { ...@@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) {
144 135
145 // Start session. generate or read the session id from http request. 136 // Start session. generate or read the session id from http request.
146 // if session id exists, return SessionStore with this id. 137 // if session id exists, return SessionStore with this id.
147 func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { 138 func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) {
148 cookie, err := r.Cookie(manager.config.CookieName) 139 cookie, errs := r.Cookie(manager.config.CookieName)
149 if err != nil || cookie.Value == "" { 140 if errs != nil || cookie.Value == "" {
150 sid := manager.sessionId(r) 141 sid, errs := manager.sessionId(r)
151 session, _ = manager.provider.SessionRead(sid) 142 if errs != nil {
143 return nil, errs
144 }
145 session, err = manager.provider.SessionRead(sid)
152 cookie = &http.Cookie{Name: manager.config.CookieName, 146 cookie = &http.Cookie{Name: manager.config.CookieName,
153 Value: url.QueryEscape(sid), 147 Value: url.QueryEscape(sid),
154 Path: "/", 148 Path: "/",
...@@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se ...@@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
163 } 157 }
164 r.AddCookie(cookie) 158 r.AddCookie(cookie)
165 } else { 159 } else {
166 sid, _ := url.QueryUnescape(cookie.Value) 160 sid, errs := url.QueryUnescape(cookie.Value)
161 if errs != nil {
162 return nil, errs
163 }
167 if manager.provider.SessionExist(sid) { 164 if manager.provider.SessionExist(sid) {
168 session, _ = manager.provider.SessionRead(sid) 165 session, err = manager.provider.SessionRead(sid)
169 } else { 166 } else {
170 sid = manager.sessionId(r) 167 sid, err = manager.sessionId(r)
171 session, _ = manager.provider.SessionRead(sid) 168 if err != nil {
169 return nil, err
170 }
171 session, err = manager.provider.SessionRead(sid)
172 cookie = &http.Cookie{Name: manager.config.CookieName, 172 cookie = &http.Cookie{Name: manager.config.CookieName,
173 Value: url.QueryEscape(sid), 173 Value: url.QueryEscape(sid),
174 Path: "/", 174 Path: "/",
...@@ -219,7 +219,10 @@ func (manager *Manager) GC() { ...@@ -219,7 +219,10 @@ func (manager *Manager) GC() {
219 219
220 // Regenerate a session id for this SessionStore who's id is saving in http request. 220 // Regenerate a session id for this SessionStore who's id is saving in http request.
221 func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { 221 func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
222 sid := manager.sessionId(r) 222 sid, err := manager.sessionId(r)
223 if err != nil {
224 return
225 }
223 cookie, err := r.Cookie(manager.config.CookieName) 226 cookie, err := r.Cookie(manager.config.CookieName)
224 if err != nil && cookie.Value == "" { 227 if err != nil && cookie.Value == "" {
225 //delete old cookie 228 //delete old cookie
...@@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int { ...@@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int {
251 return manager.provider.SessionAll() 254 return manager.provider.SessionAll()
252 } 255 }
253 256
254 // Set hash function for generating session id.
255 func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
256 manager.config.SessionIDHashFunc = hasfunc
257 manager.config.SessionIDHashKey = hashkey
258 }
259
260 // Set cookie with https. 257 // Set cookie with https.
261 func (manager *Manager) SetSecure(secure bool) { 258 func (manager *Manager) SetSecure(secure bool) {
262 manager.config.Secure = secure 259 manager.config.Secure = secure
263 } 260 }
264 261
265 // generate session id with rand string, unix nano time, remote addr by hash function. 262 func (manager *Manager) sessionId(r *http.Request) (string, error) {
266 func (manager *Manager) sessionId(r *http.Request) (sid string) { 263 b := make([]byte, manager.config.SessionIdLength)
267 bs := make([]byte, 32) 264 n, err := rand.Read(b)
268 if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil { 265 if n != len(b) || err != nil {
269 bs = utils.RandomCreateBytes(32) 266 return "", fmt.Errorf("Could not successfully read from the system CSPRNG.")
270 } 267 }
271 sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) 268 return hex.EncodeToString(b), nil
272 if manager.config.SessionIDHashFunc == "md5" {
273 h := md5.New()
274 h.Write([]byte(sig))
275 sid = hex.EncodeToString(h.Sum(nil))
276 } else if manager.config.SessionIDHashFunc == "sha1" {
277 h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
278 fmt.Fprintf(h, "%s", sig)
279 sid = hex.EncodeToString(h.Sum(nil))
280 } else {
281 h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
282 fmt.Fprintf(h, "%s", sig)
283 sid = hex.EncodeToString(h.Sum(nil))
284 }
285 return
286 } 269 }
......
...@@ -31,6 +31,7 @@ func serverStaticRouter(ctx *context.Context) { ...@@ -31,6 +31,7 @@ func serverStaticRouter(ctx *context.Context) {
31 return 31 return
32 } 32 }
33 requestPath := path.Clean(ctx.Input.Request.URL.Path) 33 requestPath := path.Clean(ctx.Input.Request.URL.Path)
34 i := 0
34 for prefix, staticDir := range StaticDir { 35 for prefix, staticDir := range StaticDir {
35 if len(prefix) == 0 { 36 if len(prefix) == 0 {
36 continue 37 continue
...@@ -41,8 +42,13 @@ func serverStaticRouter(ctx *context.Context) { ...@@ -41,8 +42,13 @@ func serverStaticRouter(ctx *context.Context) {
41 http.ServeFile(ctx.ResponseWriter, ctx.Request, file) 42 http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
42 return 43 return
43 } else { 44 } else {
45 i++
46 if i == len(StaticDir) {
44 http.NotFound(ctx.ResponseWriter, ctx.Request) 47 http.NotFound(ctx.ResponseWriter, ctx.Request)
45 return 48 return
49 } else {
50 continue
51 }
46 } 52 }
47 } 53 }
48 if strings.HasPrefix(requestPath, prefix) { 54 if strings.HasPrefix(requestPath, prefix) {
...@@ -59,9 +65,20 @@ func serverStaticRouter(ctx *context.Context) { ...@@ -59,9 +65,20 @@ func serverStaticRouter(ctx *context.Context) {
59 return 65 return
60 } 66 }
61 //if the request is dir and DirectoryIndex is false then 67 //if the request is dir and DirectoryIndex is false then
62 if finfo.IsDir() && !DirectoryIndex { 68 if finfo.IsDir() {
69 if !DirectoryIndex {
63 middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") 70 middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
64 return 71 return
72 } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' {
73 http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302)
74 return
75 }
76 } else if strings.HasSuffix(requestPath, "/index.html") {
77 file := path.Join(staticDir, requestPath)
78 if utils.FileExists(file) {
79 http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
80 return
81 }
65 } 82 }
66 83
67 //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request 84 //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request
......
...@@ -302,6 +302,14 @@ func ParseForm(form url.Values, obj interface{}) error { ...@@ -302,6 +302,14 @@ func ParseForm(form url.Values, obj interface{}) error {
302 302
303 switch fieldT.Type.Kind() { 303 switch fieldT.Type.Kind() {
304 case reflect.Bool: 304 case reflect.Bool:
305 if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
306 fieldV.SetBool(true)
307 continue
308 }
309 if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
310 fieldV.SetBool(false)
311 continue
312 }
305 b, err := strconv.ParseBool(value) 313 b, err := strconv.ParseBool(value)
306 if err != nil { 314 if err != nil {
307 return err 315 return err
...@@ -329,6 +337,19 @@ func ParseForm(form url.Values, obj interface{}) error { ...@@ -329,6 +337,19 @@ func ParseForm(form url.Values, obj interface{}) error {
329 fieldV.Set(reflect.ValueOf(value)) 337 fieldV.Set(reflect.ValueOf(value))
330 case reflect.String: 338 case reflect.String:
331 fieldV.SetString(value) 339 fieldV.SetString(value)
340 case reflect.Struct:
341 switch fieldT.Type.String() {
342 case "time.Time":
343 format := time.RFC3339
344 if len(tags) > 1 {
345 format = tags[1]
346 }
347 t, err := time.Parse(format, value)
348 if err != nil {
349 return err
350 }
351 fieldV.Set(reflect.ValueOf(t))
352 }
332 } 353 }
333 } 354 }
334 return nil 355 return nil
...@@ -368,23 +389,31 @@ func RenderForm(obj interface{}) template.HTML { ...@@ -368,23 +389,31 @@ func RenderForm(obj interface{}) template.HTML {
368 389
369 fieldT := objT.Field(i) 390 fieldT := objT.Field(i)
370 391
371 label, name, fType, ignored := parseFormTag(fieldT) 392 label, name, fType, id, class, ignored := parseFormTag(fieldT)
372 if ignored { 393 if ignored {
373 continue 394 continue
374 } 395 }
375 396
376 raw = append(raw, renderFormField(label, name, fType, fieldV.Interface())) 397 raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class))
377 } 398 }
378 return template.HTML(strings.Join(raw, "</br>")) 399 return template.HTML(strings.Join(raw, "</br>"))
379 } 400 }
380 401
381 // renderFormField returns a string containing HTML of a single form field. 402 // renderFormField returns a string containing HTML of a single form field.
382 func renderFormField(label, name, fType string, value interface{}) string { 403 func renderFormField(label, name, fType string, value interface{}, id string, class string) string {
404 if id != "" {
405 id = " id=\"" + id + "\""
406 }
407
408 if class != "" {
409 class = " class=\"" + class + "\""
410 }
411
383 if isValidForInput(fType) { 412 if isValidForInput(fType) {
384 return fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, label, name, fType, value) 413 return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v">`, label, id, class, name, fType, value)
385 } 414 }
386 415
387 return fmt.Sprintf(`%v<%v name="%v">%v</%v>`, label, fType, name, value, fType) 416 return fmt.Sprintf(`%v<%v%v%v name="%v">%v</%v>`, label, fType, id, class, name, value, fType)
388 } 417 }
389 418
390 // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. 419 // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
...@@ -400,12 +429,14 @@ func isValidForInput(fType string) bool { ...@@ -400,12 +429,14 @@ func isValidForInput(fType string) bool {
400 429
401 // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. 430 // parseFormTag takes the stuct-tag of a StructField and parses the `form` value.
402 // returned are the form label, name-property, type and wether the field should be ignored. 431 // returned are the form label, name-property, type and wether the field should be ignored.
403 func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) { 432 func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool) {
404 tags := strings.Split(fieldT.Tag.Get("form"), ",") 433 tags := strings.Split(fieldT.Tag.Get("form"), ",")
405 label = fieldT.Name + ": " 434 label = fieldT.Name + ": "
406 name = fieldT.Name 435 name = fieldT.Name
407 fType = "text" 436 fType = "text"
408 ignored = false 437 ignored = false
438 id = fieldT.Tag.Get("id")
439 class = fieldT.Tag.Get("class")
409 440
410 switch len(tags) { 441 switch len(tags) {
411 case 1: 442 case 1:
......
...@@ -108,6 +108,8 @@ func TestParseForm(t *testing.T) { ...@@ -108,6 +108,8 @@ func TestParseForm(t *testing.T) {
108 Age int `form:"age,text"` 108 Age int `form:"age,text"`
109 Email string 109 Email string
110 Intro string `form:",textarea"` 110 Intro string `form:",textarea"`
111 StrBool bool `form:"strbool"`
112 Date time.Time `form:"date,2006-01-02"`
111 } 113 }
112 114
113 u := user{} 115 u := user{}
...@@ -119,6 +121,8 @@ func TestParseForm(t *testing.T) { ...@@ -119,6 +121,8 @@ func TestParseForm(t *testing.T) {
119 "age": []string{"40"}, 121 "age": []string{"40"},
120 "Email": []string{"test@gmail.com"}, 122 "Email": []string{"test@gmail.com"},
121 "Intro": []string{"I am an engineer!"}, 123 "Intro": []string{"I am an engineer!"},
124 "strbool": []string{"yes"},
125 "date": []string{"2014-11-12"},
122 } 126 }
123 if err := ParseForm(form, u); err == nil { 127 if err := ParseForm(form, u); err == nil {
124 t.Fatal("nothing will be changed") 128 t.Fatal("nothing will be changed")
...@@ -144,6 +148,13 @@ func TestParseForm(t *testing.T) { ...@@ -144,6 +148,13 @@ func TestParseForm(t *testing.T) {
144 if u.Intro != "I am an engineer!" { 148 if u.Intro != "I am an engineer!" {
145 t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) 149 t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
146 } 150 }
151 if u.StrBool != true {
152 t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
153 }
154 y, m, d := u.Date.Date()
155 if y != 2014 || m.String() != "November" || d != 12 {
156 t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
157 }
147 } 158 }
148 159
149 func TestRenderForm(t *testing.T) { 160 func TestRenderForm(t *testing.T) {
...@@ -175,12 +186,12 @@ func TestRenderForm(t *testing.T) { ...@@ -175,12 +186,12 @@ func TestRenderForm(t *testing.T) {
175 } 186 }
176 187
177 func TestRenderFormField(t *testing.T) { 188 func TestRenderFormField(t *testing.T) {
178 html := renderFormField("Label: ", "Name", "text", "Value") 189 html := renderFormField("Label: ", "Name", "text", "Value", "", "")
179 if html != `Label: <input name="Name" type="text" value="Value">` { 190 if html != `Label: <input name="Name" type="text" value="Value">` {
180 t.Errorf("Wrong html output for input[type=text]: %v ", html) 191 t.Errorf("Wrong html output for input[type=text]: %v ", html)
181 } 192 }
182 193
183 html = renderFormField("Label: ", "Name", "textarea", "Value") 194 html = renderFormField("Label: ", "Name", "textarea", "Value", "", "")
184 if html != `Label: <textarea name="Name">Value</textarea>` { 195 if html != `Label: <textarea name="Name">Value</textarea>` {
185 t.Errorf("Wrong html output for textarea: %v ", html) 196 t.Errorf("Wrong html output for textarea: %v ", html)
186 } 197 }
...@@ -192,33 +203,34 @@ func TestParseFormTag(t *testing.T) { ...@@ -192,33 +203,34 @@ func TestParseFormTag(t *testing.T) {
192 All int `form:"name,text,年龄:"` 203 All int `form:"name,text,年龄:"`
193 NoName int `form:",hidden,年龄:"` 204 NoName int `form:",hidden,年龄:"`
194 OnlyLabel int `form:",,年龄:"` 205 OnlyLabel int `form:",,年龄:"`
195 OnlyName int `form:"name"` 206 OnlyName int `form:"name" id:"name" class:"form-name"`
196 Ignored int `form:"-"` 207 Ignored int `form:"-"`
197 } 208 }
198 209
199 objT := reflect.TypeOf(&user{}).Elem() 210 objT := reflect.TypeOf(&user{}).Elem()
200 211
201 label, name, fType, ignored := parseFormTag(objT.Field(0)) 212 label, name, fType, id, class, ignored := parseFormTag(objT.Field(0))
202 if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { 213 if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) {
203 t.Errorf("Form Tag with name, label and type was not correctly parsed.") 214 t.Errorf("Form Tag with name, label and type was not correctly parsed.")
204 } 215 }
205 216
206 label, name, fType, ignored = parseFormTag(objT.Field(1)) 217 label, name, fType, id, class, ignored = parseFormTag(objT.Field(1))
207 if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { 218 if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) {
208 t.Errorf("Form Tag with label and type but without name was not correctly parsed.") 219 t.Errorf("Form Tag with label and type but without name was not correctly parsed.")
209 } 220 }
210 221
211 label, name, fType, ignored = parseFormTag(objT.Field(2)) 222 label, name, fType, id, class, ignored = parseFormTag(objT.Field(2))
212 if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { 223 if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) {
213 t.Errorf("Form Tag containing only label was not correctly parsed.") 224 t.Errorf("Form Tag containing only label was not correctly parsed.")
214 } 225 }
215 226
216 label, name, fType, ignored = parseFormTag(objT.Field(3)) 227 label, name, fType, id, class, ignored = parseFormTag(objT.Field(3))
217 if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) { 228 if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false &&
229 id == "name" && class == "form-name") {
218 t.Errorf("Form Tag containing only name was not correctly parsed.") 230 t.Errorf("Form Tag containing only name was not correctly parsed.")
219 } 231 }
220 232
221 label, name, fType, ignored = parseFormTag(objT.Field(4)) 233 label, name, fType, id, class, ignored = parseFormTag(objT.Field(4))
222 if ignored == false { 234 if ignored == false {
223 t.Errorf("Form Tag that should be ignored was not correctly parsed.") 235 t.Errorf("Form Tag that should be ignored was not correctly parsed.")
224 } 236 }
......
...@@ -111,6 +111,27 @@ func (m *UrlMap) GetMap() map[string]interface{} { ...@@ -111,6 +111,27 @@ func (m *UrlMap) GetMap() map[string]interface{} {
111 return content 111 return content
112 } 112 }
113 113
114 func (m *UrlMap) GetMapData() []map[string]interface{} {
115
116 resultLists := make([]map[string]interface{}, 0)
117
118 for k, v := range m.urlmap {
119 for kk, vv := range v {
120 result := map[string]interface{}{
121 "request_url": k,
122 "method": kk,
123 "times": vv.RequestNum,
124 "total_time": toS(vv.TotalTime),
125 "max_time": toS(vv.MaxTime),
126 "min_time": toS(vv.MinTime),
127 "avg_time": toS(time.Duration(int64(vv.TotalTime) / vv.RequestNum)),
128 }
129 resultLists = append(resultLists, result)
130 }
131 }
132 return resultLists
133 }
134
114 // global statistics data map 135 // global statistics data map
115 var StatisticsMap *UrlMap 136 var StatisticsMap *UrlMap
116 137
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 package toolbox 15 package toolbox
16 16
17 import ( 17 import (
18 "encoding/json"
18 "testing" 19 "testing"
19 "time" 20 "time"
20 ) 21 )
...@@ -28,4 +29,12 @@ func TestStatics(t *testing.T) { ...@@ -28,4 +29,12 @@ func TestStatics(t *testing.T) {
28 StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000)) 29 StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
29 StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400)) 30 StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
30 t.Log(StatisticsMap.GetMap()) 31 t.Log(StatisticsMap.GetMap())
32
33 data := StatisticsMap.GetMapData()
34 b, err := json.Marshal(data)
35 if err != nil {
36 t.Errorf(err.Error())
37 }
38
39 t.Log(string(b))
31 } 40 }
......
...@@ -394,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string ...@@ -394,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
394 } 394 }
395 return true, params 395 return true, params
396 } 396 }
397 if len(wildcardValues) <= j {
398 return false, nil
399 }
397 params[v] = wildcardValues[j] 400 params[v] = wildcardValues[j]
398 j += 1 401 j += 1
399 } 402 }
......
...@@ -157,19 +157,37 @@ func (e *Email) Bytes() ([]byte, error) { ...@@ -157,19 +157,37 @@ func (e *Email) Bytes() ([]byte, error) {
157 } 157 }
158 158
159 // Add attach file to the send mail 159 // Add attach file to the send mail
160 func (e *Email) AttachFile(filename string) (a *Attachment, err error) { 160 func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
161 if len(args) < 1 && len(args) > 2 {
162 err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
163 return
164 }
165 filename := args[0]
166 id := ""
167 if len(args) > 1 {
168 id = args[1]
169 }
161 f, err := os.Open(filename) 170 f, err := os.Open(filename)
162 if err != nil { 171 if err != nil {
163 return 172 return
164 } 173 }
165 ct := mime.TypeByExtension(filepath.Ext(filename)) 174 ct := mime.TypeByExtension(filepath.Ext(filename))
166 basename := path.Base(filename) 175 basename := path.Base(filename)
167 return e.Attach(f, basename, ct) 176 return e.Attach(f, basename, ct, id)
168 } 177 }
169 178
170 // Attach is used to attach content from an io.Reader to the email. 179 // Attach is used to attach content from an io.Reader to the email.
171 // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. 180 // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
172 func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) { 181 func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
182 if len(args) < 1 && len(args) > 2 {
183 err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
184 return
185 }
186 c := args[0] //Content-Type
187 id := ""
188 if len(args) > 1 {
189 id = args[1] //Content-ID
190 }
173 var buffer bytes.Buffer 191 var buffer bytes.Buffer
174 if _, err = io.Copy(&buffer, r); err != nil { 192 if _, err = io.Copy(&buffer, r); err != nil {
175 return 193 return
...@@ -186,7 +204,12 @@ func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, e ...@@ -186,7 +204,12 @@ func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, e
186 // If the Content-Type is blank, set the Content-Type to "application/octet-stream" 204 // If the Content-Type is blank, set the Content-Type to "application/octet-stream"
187 at.Header.Set("Content-Type", "application/octet-stream") 205 at.Header.Set("Content-Type", "application/octet-stream")
188 } 206 }
207 if id != "" {
208 at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename))
209 at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id))
210 } else {
189 at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename)) 211 at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
212 }
190 at.Header.Set("Content-Transfer-Encoding", "base64") 213 at.Header.Set("Content-Transfer-Encoding", "base64")
191 e.Attachments = append(e.Attachments, at) 214 e.Attachments = append(e.Attachments, at)
192 return at, nil 215 return at, nil
...@@ -269,7 +292,7 @@ func qpEscape(dest []byte, c byte) { ...@@ -269,7 +292,7 @@ func qpEscape(dest []byte, c byte) {
269 const nums = "0123456789ABCDEF" 292 const nums = "0123456789ABCDEF"
270 dest[0] = '=' 293 dest[0] = '='
271 dest[1] = nums[(c&0xf0)>>4] 294 dest[1] = nums[(c&0xf0)>>4]
272 dest[2] = nums[(c & 0xf)] 295 dest[2] = nums[(c&0xf)]
273 } 296 }
274 297
275 // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer 298 // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
......
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package pagination
16
17 import (
18 "github.com/astaxie/beego/context"
19 )
20
21 // Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
22 func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
23 paginator = NewPaginator(context.Request, per, nums)
24 context.Input.Data["paginator"] = paginator
25 return
26 }
1 /*
2
3 The pagination package provides utilities to setup a paginator within the
4 context of a http request.
5
6 Usage
7
8 In your beego.Controller:
9
10 package controllers
11
12 import "github.com/astaxie/beego/utils/pagination"
13
14 type PostsController struct {
15 beego.Controller
16 }
17
18 func (this *PostsController) ListAllPosts() {
19 // sets this.Data["paginator"] with the current offset (from the url query param)
20 postsPerPage := 20
21 paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts())
22
23 // fetch the next 20 posts
24 this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage)
25 }
26
27
28 In your view templates:
29
30 {{if .paginator.HasPages}}
31 <ul class="pagination pagination">
32 {{if .paginator.HasPrev}}
33 <li><a href="{{.paginator.PageLinkFirst}}">{{ i18n .Lang "paginator.first_page"}}</a></li>
34 <li><a href="{{.paginator.PageLinkPrev}}">&laquo;</a></li>
35 {{else}}
36 <li class="disabled"><a>{{ i18n .Lang "paginator.first_page"}}</a></li>
37 <li class="disabled"><a>&laquo;</a></li>
38 {{end}}
39 {{range $index, $page := .paginator.Pages}}
40 <li{{if $.paginator.IsActive .}} class="active"{{end}}>
41 <a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
42 </li>
43 {{end}}
44 {{if .paginator.HasNext}}
45 <li><a href="{{.paginator.PageLinkNext}}">&raquo;</a></li>
46 <li><a href="{{.paginator.PageLinkLast}}">{{ i18n .Lang "paginator.last_page"}}</a></li>
47 {{else}}
48 <li class="disabled"><a>&raquo;</a></li>
49 <li class="disabled"><a>{{ i18n .Lang "paginator.last_page"}}</a></li>
50 {{end}}
51 </ul>
52 {{end}}
53
54 See also
55
56 http://beego.me/docs/mvc/view/page.md
57
58 */
59 package pagination
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package pagination
16
17 import (
18 "math"
19 "net/http"
20 "net/url"
21 "strconv"
22 )
23
24 // Paginator within the state of a http request.
25 type Paginator struct {
26 Request *http.Request
27 PerPageNums int
28 MaxPages int
29
30 nums int64
31 pageRange []int
32 pageNums int
33 page int
34 }
35
36 // Returns the total number of pages.
37 func (p *Paginator) PageNums() int {
38 if p.pageNums != 0 {
39 return p.pageNums
40 }
41 pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
42 if p.MaxPages > 0 {
43 pageNums = math.Min(pageNums, float64(p.MaxPages))
44 }
45 p.pageNums = int(pageNums)
46 return p.pageNums
47 }
48
49 // Returns the total number of items (e.g. from doing SQL count).
50 func (p *Paginator) Nums() int64 {
51 return p.nums
52 }
53
54 // Sets the total number of items.
55 func (p *Paginator) SetNums(nums interface{}) {
56 p.nums, _ = ToInt64(nums)
57 }
58
59 // Returns the current page.
60 func (p *Paginator) Page() int {
61 if p.page != 0 {
62 return p.page
63 }
64 if p.Request.Form == nil {
65 p.Request.ParseForm()
66 }
67 p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
68 if p.page > p.PageNums() {
69 p.page = p.PageNums()
70 }
71 if p.page <= 0 {
72 p.page = 1
73 }
74 return p.page
75 }
76
77 // Returns a list of all pages.
78 //
79 // Usage (in a view template):
80 //
81 // {{range $index, $page := .paginator.Pages}}
82 // <li{{if $.paginator.IsActive .}} class="active"{{end}}>
83 // <a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
84 // </li>
85 // {{end}}
86 func (p *Paginator) Pages() []int {
87 if p.pageRange == nil && p.nums > 0 {
88 var pages []int
89 pageNums := p.PageNums()
90 page := p.Page()
91 switch {
92 case page >= pageNums-4 && pageNums > 9:
93 start := pageNums - 9 + 1
94 pages = make([]int, 9)
95 for i, _ := range pages {
96 pages[i] = start + i
97 }
98 case page >= 5 && pageNums > 9:
99 start := page - 5 + 1
100 pages = make([]int, int(math.Min(9, float64(page+4+1))))
101 for i, _ := range pages {
102 pages[i] = start + i
103 }
104 default:
105 pages = make([]int, int(math.Min(9, float64(pageNums))))
106 for i, _ := range pages {
107 pages[i] = i + 1
108 }
109 }
110 p.pageRange = pages
111 }
112 return p.pageRange
113 }
114
115 // Returns URL for a given page index.
116 func (p *Paginator) PageLink(page int) string {
117 link, _ := url.ParseRequestURI(p.Request.RequestURI)
118 values := link.Query()
119 if page == 1 {
120 values.Del("p")
121 } else {
122 values.Set("p", strconv.Itoa(page))
123 }
124 link.RawQuery = values.Encode()
125 return link.String()
126 }
127
128 // Returns URL to the previous page.
129 func (p *Paginator) PageLinkPrev() (link string) {
130 if p.HasPrev() {
131 link = p.PageLink(p.Page() - 1)
132 }
133 return
134 }
135
136 // Returns URL to the next page.
137 func (p *Paginator) PageLinkNext() (link string) {
138 if p.HasNext() {
139 link = p.PageLink(p.Page() + 1)
140 }
141 return
142 }
143
144 // Returns URL to the first page.
145 func (p *Paginator) PageLinkFirst() (link string) {
146 return p.PageLink(1)
147 }
148
149 // Returns URL to the last page.
150 func (p *Paginator) PageLinkLast() (link string) {
151 return p.PageLink(p.PageNums())
152 }
153
154 // Returns true if the current page has a predecessor.
155 func (p *Paginator) HasPrev() bool {
156 return p.Page() > 1
157 }
158
159 // Returns true if the current page has a successor.
160 func (p *Paginator) HasNext() bool {
161 return p.Page() < p.PageNums()
162 }
163
164 // Returns true if the given page index points to the current page.
165 func (p *Paginator) IsActive(page int) bool {
166 return p.Page() == page
167 }
168
169 // Returns the current offset.
170 func (p *Paginator) Offset() int {
171 return (p.Page() - 1) * p.PerPageNums
172 }
173
174 // Returns true if there is more than one page.
175 func (p *Paginator) HasPages() bool {
176 return p.PageNums() > 1
177 }
178
179 // Instantiates a paginator struct for the current http request.
180 func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator {
181 p := Paginator{}
182 p.Request = req
183 if per <= 0 {
184 per = 10
185 }
186 p.PerPageNums = per
187 p.SetNums(nums)
188 return &p
189 }
1 // Copyright 2014 beego Author. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package pagination
16
17 import (
18 "fmt"
19 "reflect"
20 )
21
22 // convert any numeric value to int64
23 func ToInt64(value interface{}) (d int64, err error) {
24 val := reflect.ValueOf(value)
25 switch value.(type) {
26 case int, int8, int16, int32, int64:
27 d = val.Int()
28 case uint, uint8, uint16, uint32, uint64:
29 d = int64(val.Uint())
30 default:
31 err = fmt.Errorf("ToInt64 need numeric not `%T`", value)
32 }
33 return
34 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!