Merge branch 'master' of https://github.com/astaxie/beego
Showing
43 changed files
with
1401 additions
and
288 deletions
.go_style
deleted
100644 → 0
| ... | @@ -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,15 +50,24 @@ func (app *App) Run() { | ... | @@ -57,15 +50,24 @@ func (app *App) Run() { |
| 57 | endRunning := make(chan bool, 1) | 50 | endRunning := make(chan bool, 1) |
| 58 | 51 | ||
| 59 | if UseFcgi { | 52 | if UseFcgi { |
| 60 | if HttpPort == 0 { | 53 | if UseStdIo { |
| 61 | l, err = net.Listen("unix", addr) | 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 | } | ||
| 62 | } else { | 60 | } else { |
| 63 | l, err = net.Listen("tcp", addr) | 61 | if HttpPort == 0 { |
| 64 | } | 62 | l, err = net.Listen("unix", addr) |
| 65 | if err != nil { | 63 | } else { |
| 66 | BeeLogger.Critical("Listen: ", err) | 64 | l, err = net.Listen("tcp", addr) |
| 65 | } | ||
| 66 | if err != nil { | ||
| 67 | BeeLogger.Critical("Listen: ", err) | ||
| 68 | } | ||
| 69 | err = fcgi.Serve(l, app.Handlers) | ||
| 67 | } | 70 | } |
| 68 | err = fcgi.Serve(l, app.Handlers) | ||
| 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,11 +93,29 @@ func (app *App) Run() { | ... | @@ -90,11 +93,29 @@ 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 |
| 93 | err := app.Server.ListenAndServe() | 96 | BeeLogger.Info("http server Running on %s", app.Server.Addr) |
| 94 | if err != nil { | 97 | if ListenTCP4 && HttpAddr == "" { |
| 95 | BeeLogger.Critical("ListenAndServe: ", err) | 98 | ln, err := net.Listen("tcp4", app.Server.Addr) |
| 96 | time.Sleep(100 * time.Microsecond) | 99 | if err != nil { |
| 97 | endRunning <- true | 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 { | ||
| 113 | err := app.Server.ListenAndServe() | ||
| 114 | if err != nil { | ||
| 115 | BeeLogger.Critical("ListenAndServe: ", err) | ||
| 116 | time.Sleep(100 * time.Microsecond) | ||
| 117 | endRunning <- true | ||
| 118 | } | ||
| 98 | } | 119 | } |
| 99 | }() | 120 | }() |
| 100 | } | 121 | } | ... | ... |
| ... | @@ -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 | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -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 | ... | ... |
controller_test.go
0 → 100644
| 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 | } | ... | ... |
| ... | @@ -150,14 +150,14 @@ type WSController struct { | ... | @@ -150,14 +150,14 @@ type WSController struct { |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | var upgrader = websocket.Upgrader{ | 152 | var upgrader = websocket.Upgrader{ |
| 153 | ReadBufferSize: 1024, | 153 | ReadBufferSize: 1024, |
| 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) |
| 282 | for formname, filename := range b.files { | 283 | go func() { |
| 283 | fileWriter, err := bodyWriter.CreateFormFile(formname, filename) | 284 | for formname, filename := range b.files { |
| 284 | if err != nil { | 285 | fileWriter, err := bodyWriter.CreateFormFile(formname, filename) |
| 285 | return nil, err | 286 | if err != nil { |
| 287 | log.Fatal(err) | ||
| 288 | } | ||
| 289 | fh, err := os.Open(filename) | ||
| 290 | if err != nil { | ||
| 291 | log.Fatal(err) | ||
| 292 | } | ||
| 293 | //iocopy | ||
| 294 | _, err = io.Copy(fileWriter, fh) | ||
| 295 | fh.Close() | ||
| 296 | if err != nil { | ||
| 297 | log.Fatal(err) | ||
| 298 | } | ||
| 286 | } | 299 | } |
| 287 | fh, err := os.Open(filename) | 300 | for k, v := range b.params { |
| 288 | if err != nil { | 301 | bodyWriter.WriteField(k, v) |
| 289 | return nil, err | ||
| 290 | } | 302 | } |
| 291 | //iocopy | 303 | bodyWriter.Close() |
| 292 | _, err = io.Copy(fileWriter, fh) | 304 | pw.Close() |
| 293 | fh.Close() | 305 | }() |
| 294 | if err != nil { | 306 | b.Header("Content-Type", bodyWriter.FormDataContentType()) |
| 295 | return nil, err | 307 | b.req.Body = ioutil.NopCloser(pr) |
| 296 | } | 308 | } else if len(paramBody) > 0 { |
| 297 | } | ||
| 298 | for k, v := range b.params { | ||
| 299 | bodyWriter.WriteField(k, v) | ||
| 300 | } | ||
| 301 | contentType := bodyWriter.FormDataContentType() | ||
| 302 | bodyWriter.Close() | ||
| 303 | b.Header("Content-Type", contentType) | ||
| 304 | b.req.Body = ioutil.NopCloser(bodyBuf) | ||
| 305 | b.req.ContentLength = int64(bodyBuf.Len()) | ||
| 306 | } else { | ||
| 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) | ... | ... |
migration/ddl.go
0 → 100644
| 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) | ... | ... |
orm/qb.go
0 → 100644
| 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 | } |
orm/qb_mysql.go
0 → 100644
| 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)) { | ... | ... |
| ... | @@ -72,9 +72,31 @@ var ( | ... | @@ -72,9 +72,31 @@ var ( |
| 72 | "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", | 72 | "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", |
| 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,7 +841,9 @@ Admin: | ... | @@ -783,7 +841,9 @@ 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 | } |
| 786 | Debug(devinfo) | 844 | if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { |
| 845 | Debug(devinfo) | ||
| 846 | } | ||
| 787 | } | 847 | } |
| 788 | 848 | ||
| 789 | // Call WriteHeader if status code has been set changed | 849 | // Call WriteHeader if status code has been set changed |
| ... | @@ -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,9 +891,15 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques | ... | @@ -829,9 +891,15 @@ 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 { |
| 833 | handler(rw, r) | 895 | handler(rw, r) |
| 834 | return | 896 | return |
| 897 | } else if handler, ok := middleware.ErrorMaps["503"]; ok { | ||
| 898 | handler(rw, r) | ||
| 899 | return | ||
| 900 | } else { | ||
| 901 | rw.Write([]byte(fmt.Sprint(err))) | ||
| 902 | } | ||
| 835 | } else { | 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) |
| ... | @@ -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. |
| ... | @@ -81,16 +75,15 @@ func Register(name string, provide Provider) { | ... | @@ -81,16 +75,15 @@ func Register(name string, provide Provider) { |
| 81 | } | 75 | } |
| 82 | 76 | ||
| 83 | type managerConfig struct { | 77 | type managerConfig struct { |
| 84 | CookieName string `json:"cookieName"` | 78 | CookieName string `json:"cookieName"` |
| 85 | EnableSetCookie bool `json:"enableSetCookie,omitempty"` | 79 | EnableSetCookie bool `json:"enableSetCookie,omitempty"` |
| 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"` | 83 | CookieLifeTime int `json:"cookieLifeTime"` |
| 90 | SessionIDHashKey string `json:"sessionIDHashKey"` | 84 | ProviderConfig string `json:"providerConfig"` |
| 91 | CookieLifeTime int `json:"cookieLifeTime"` | 85 | Domain string `json:"domain"` |
| 92 | ProviderConfig string `json:"providerConfig"` | 86 | SessionIdLength int64 `json:"sessionIdLength"` |
| 93 | Domain string `json:"domain"` | ||
| 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 | } | ||
| 271 | sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) | ||
| 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 | } | 267 | } |
| 285 | return | 268 | return hex.EncodeToString(b), nil |
| 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 { |
| 44 | http.NotFound(ctx.ResponseWriter, ctx.Request) | 45 | i++ |
| 45 | return | 46 | if i == len(StaticDir) { |
| 47 | http.NotFound(ctx.ResponseWriter, ctx.Request) | ||
| 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() { |
| 63 | middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") | 69 | if !DirectoryIndex { |
| 64 | return | 70 | middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") |
| 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: | ... | ... |
| ... | @@ -102,12 +102,14 @@ func TestHtmlunquote(t *testing.T) { | ... | @@ -102,12 +102,14 @@ func TestHtmlunquote(t *testing.T) { |
| 102 | 102 | ||
| 103 | func TestParseForm(t *testing.T) { | 103 | func TestParseForm(t *testing.T) { |
| 104 | type user struct { | 104 | type user struct { |
| 105 | Id int `form:"-"` | 105 | Id int `form:"-"` |
| 106 | tag string `form:"tag"` | 106 | tag string `form:"tag"` |
| 107 | Name interface{} `form:"username"` | 107 | Name interface{} `form:"username"` |
| 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 | } |
| 189 | at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename)) | 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 { | ||
| 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 | ... | ... |
utils/pagination/controller.go
0 → 100644
| 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 | } |
utils/pagination/doc.go
0 → 100644
| 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}}">«</a></li> | ||
| 35 | {{else}} | ||
| 36 | <li class="disabled"><a>{{ i18n .Lang "paginator.first_page"}}</a></li> | ||
| 37 | <li class="disabled"><a>«</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}}">»</a></li> | ||
| 46 | <li><a href="{{.paginator.PageLinkLast}}">{{ i18n .Lang "paginator.last_page"}}</a></li> | ||
| 47 | {{else}} | ||
| 48 | <li class="disabled"><a>»</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 |
utils/pagination/paginator.go
0 → 100644
| 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 | } |
utils/pagination/utils.go
0 → 100644
| 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 | } |
-
Please register or sign in to post a comment