1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 14:21:00 +00:00

134 Commits

Author SHA1 Message Date
07c628c7e9 fix the commentsRouter init sequence 2014-11-06 17:30:50 +08:00
1e92d17605 fix the repeat commentsRouters 2014-11-06 16:25:47 +08:00
bb795847da fix the not exist config file application 2014-11-06 11:12:00 +08:00
54ba307f7f change this to short name 2014-11-05 22:40:31 +08:00
950ff91d87 hotfix for parsefiel 2014-11-05 22:23:54 +08:00
b43401b9f6 Merge pull request #907 from astaxie/develop
1.4.1 released
2014-11-04 22:39:03 +08:00
fe50269b3f change 1.4.1 to 1.4.2 2014-11-04 22:38:40 +08:00
000033e2a7 update the test case 2014-11-04 22:07:38 +08:00
15242d89ce simple the session init 2014-11-04 19:08:06 +08:00
76522d43af simple the session 2014-11-04 19:07:49 +08:00
52df1234bd Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-11-04 19:04:43 +08:00
fc6b9ce009 fix #620 simple the sessionID generate 2014-11-04 19:04:26 +08:00
b9fdd67519 add test case fot date & stringbool 2014-11-04 16:39:17 +08:00
7743eecfd4 support #761
type Test struct {
    Date    time.Time    `form:"Date, 2006-01-02"`
    Save    bool            `form:"Save"`
}
2014-11-04 16:19:46 +08:00
c4d8e4a244 fix #759 2014-11-04 15:29:33 +08:00
9d4ec508bb parse for github.com replace the . to _ 2014-11-04 10:19:30 +08:00
8b747f54bc fix #770 2014-11-03 23:33:11 +08:00
a2428af8a7 compatibility for warn & info function add one more depth 2014-11-03 16:48:45 +08:00
90a7ce5c6a split the file for logs 2014-11-03 16:45:42 +08:00
ef3c7c127b fix the variable 2014-11-03 16:44:05 +08:00
88caf1ed70 if read the log.go then calldepth add 1 2014-11-03 16:43:07 +08:00
304beaf89f update the log call deep 2014-11-03 16:40:08 +08:00
2288ac868c remove the deep Caller 2014-11-03 16:34:36 +08:00
8d797a4a5e file the static filter 2014-11-03 16:14:40 +08:00
10db97b193 add some tips for the admin server start 2014-11-03 15:08:51 +08:00
716962672f fix #751
add config ListenTCP4

when user want to listen on the TCP4, because now almost use the ipv4.
but default lister on the ipv6
2014-11-03 15:06:25 +08:00
90cff5f042 fix #824 2014-11-02 21:01:51 +08:00
da127bbc22 fix #855 #859 2014-10-31 16:31:23 +08:00
945b1da3a8 fix the gofmt 2014-10-31 15:48:57 +08:00
94c84b846f fix the init logger 2014-10-31 09:02:16 +08:00
db43892fe6 improve the Put #896 2014-10-31 00:28:51 +08:00
71149218d1 fix the log level 2014-10-30 17:43:32 +08:00
1636a7271c Revert "fix the log test"
This reverts commit ddbfc25e56.
2014-10-30 16:57:55 +08:00
68c3bdfdd4 Revert "logs:default support fileline"
This reverts commit 1f26852610.
2014-10-30 16:57:48 +08:00
ecd0a5487e fix the import cycle not allowed 2014-10-30 16:12:54 +08:00
fda841208d fix #893 2014-10-30 16:05:48 +08:00
57e62e5e57 update the file upload to io.Pipe 2014-10-30 11:16:09 +08:00
824e3f8f5b fix the only file upload param 2014-10-29 16:00:08 +08:00
1822dd95ac Merge pull request #892 from dockercn/master
Add a beego session backend using LedisDB
2014-10-29 14:18:20 +08:00
ddbfc25e56 fix the log test 2014-10-28 19:34:11 +08:00
1f26852610 logs:default support fileline 2014-10-28 19:33:14 +08:00
0188fb3711 Merge pull request #1 from chliang2030598/master
add session store in ledis
2014-10-27 06:02:19 +00:00
0bcd828d73 add session store in ledis 2014-10-26 22:56:00 -07:00
e11a27f1d1 Merge pull request #888 from astaxie/revert-887-add-column-default-attribute
Revert "Add column default attribute"
2014-10-26 10:41:34 +08:00
90caeb4cf7 Revert "Add column default attribute" 2014-10-26 10:41:22 +08:00
6c9249034d Merge pull request #887 from supar/add-column-default-attribute
Add column default attribute
2014-10-26 10:28:56 +08:00
14114018ea config ini support include 2014-10-24 19:03:27 +08:00
6f5162461e Add column DEFAULT attribute. Do not add if field is key or in
relations.
2014-10-24 14:51:35 +04:00
c34c514bba Skip add DEFAULT if the field is in relations (rel or reverse) 2014-10-24 14:37:46 +04:00
8ac2b9bf66 Merge branch 'master' into develop 2014-10-24 15:10:11 +08:00
2a85c79ce5 Merge pull request #881 from astaxie/revert-870-add-column-default-attribute
Revert "Add column default attribute"
2014-10-24 14:58:27 +08:00
767083bd56 Revert "Add column default attribute" 2014-10-24 14:58:17 +08:00
1a79513293 Merge branch 'master' into develop 2014-10-24 14:50:53 +08:00
c6cb1f92e8 Merge pull request #870 from supar/add-column-default-attribute
Add column default attribute
2014-10-24 14:49:53 +08:00
9c0aad06c5 Merge pull request #880 from chenghuama/patch-3
Update ini.go
2014-10-24 14:15:49 +08:00
180c6aafac Update ini.go
支持BOM格式的ini文件
2014-10-24 13:45:00 +08:00
710f5b6234 fix the test fun for pull request 873 2014-10-20 22:23:29 +08:00
dbf944adce Merge pull request #873 from WithGJR/develop
add new feature to 'renderform' function, user could add HTML id and class now
2014-10-20 22:03:52 +08:00
6c9ff81fc1 fix: if user didn't set id or class, then it won't be displayed in HTML code 2014-10-20 18:59:46 +08:00
ec6383c07d Merge pull request #858 from bsingr/develop
Allow to use fastcgi via standard io.
2014-10-20 18:30:10 +08:00
76db5cded4 fix the init mime 2014-10-20 18:21:17 +08:00
1b3e7de463 add new feature to 'renderform' function, user could add HTML id and class now 2014-10-20 17:49:16 +08:00
ab28edaf25 Fix comma in the switch, fix wronf function name 2014-10-17 13:02:18 +04:00
04431a7a15 Fix function name fmt.Stprintf -> fmt.Sprintf 2014-10-17 12:59:24 +04:00
b00c42b3df Fix undefind variable fieldType 2014-10-17 12:56:44 +04:00
4cae7af3f9 Add attribute DEFAULT '' to the CREAT, ALTER constructors 2014-10-17 12:53:59 +04:00
e4988b714e Add property colDefault to fieldInfo object, set its true if there is
orm configuration default `orm:"default(1)"`
2014-10-17 12:27:53 +04:00
24489df63d Merge pull request #867 from WithGJR/develop
fix router bug: when the request is PUT or DELETE, router can't find the...
2014-10-16 23:07:20 +08:00
fb8e9ae1a3 Merge pull request #868 from reterVision/patch-2
Use SETEX command to set session
2014-10-16 21:42:23 +08:00
1eb9aef687 Use SETEX command to set session
In order to be compatible with older version Redis, use `SETEX` command instead of `SET x y EX 360`.
2014-10-16 20:16:17 +08:00
efc14a1e8d fix router bug with more better way 2014-10-16 18:58:12 +08:00
fa1281002e fix router bug: when the request is PUT or DELETE, router can't find the actual route and will throw 404 page to user 2014-10-16 18:26:01 +08:00
812950b60d Allow to use fastcgi via standard io. 2014-10-13 13:47:44 +02:00
f9e991b538 Merge pull request #853 from tossp/email
支持发送邮件内嵌附件
2014-10-12 11:02:53 +08:00
fc07419938 Update mail.go 2014-10-11 00:42:01 +08:00
d69eee23f0 添加错误返回
不知道英文区的人能否看懂Cnglish。。。
2014-10-11 00:38:31 +08:00
41de7c7db6 fix
修改一个错误。
看到text/template包的写法,和你的想法是一致的。
2014-10-11 00:02:36 +08:00
6a33647f30 修改参数类型
为了保持向后兼容,
2014-10-10 23:40:02 +08:00
e5134873be 支持发送邮件内嵌附件
为*Email.AttachFile和Email.Attach增加了一个参数"id".
当id不为空时,设置头部信息Content-Disposition为inline,并添加Content-ID头的值为id
2014-10-10 14:40:07 +08:00
8d20ea04b0 Merge pull request #852 from pabdavis/statistics-json
[Proposal] Ability to get statistics data unformatted
2014-10-10 10:59:30 +08:00
5c1e8e42b9 Reworked implementation to not return encoded json 2014-10-09 17:07:28 -04:00
ca3e7568a1 Add ability to get statistics in json format 2014-10-09 16:32:56 -04:00
2823167848 Merge pull request #849 from pabdavis/runmode-env
Support run mode set by environment variable
2014-10-10 00:50:16 +08:00
a27f5c0dc0 Remove dependency of third party lib 2014-10-09 09:17:10 -04:00
8af0475251 fix #833 2014-10-09 18:47:22 +08:00
9c07332cfc Update README.md 2014-10-09 09:34:22 +08:00
a06e0f27ad Support run mode set by env var BEEGO_RUNMODE 2014-10-08 15:00:07 -04:00
a760e46f98 Merge pull request #837 from bsingr/develop
Insert pagination utilities from beego/wetalk. Refs #835.
2014-10-08 23:00:46 +08:00
262665f4e5 Remove PaginationController interface and pass context instead. Refs #835. 2014-10-08 16:01:42 +02:00
0b3763cc67 Cleanup pagination documentation. Refs #835. 2014-10-08 16:00:00 +02:00
c147f26cd1 Merge pull request #847 from pabdavis/multifilter-fix2
Changes to handle multi filters on execution pt
2014-10-08 21:24:28 +08:00
1ba7847913 Changing check from nil to len based on slice 2014-10-08 09:21:34 -04:00
52df979aca update the godocs 2014-10-08 14:02:57 +08:00
b6f789c497 Changes to handle multi filters on execution pt 2014-10-07 16:35:30 -04:00
fa6cbc08d9 Document usage of utils/pagination. Refs #835. 2014-10-07 11:23:08 +02:00
c4f8f45da4 Move pagination to utils/pagination. Refs #837, #835. 2014-10-06 11:37:08 +02:00
6fca4a8218 Insert pagination utilities from beego/wetalk. Refs #835. 2014-10-02 11:40:46 +02:00
aae89576c6 fix #814 2014-10-01 22:31:44 +08:00
a907a86476 fix #814 2014-10-01 22:28:49 +08:00
8716185de8 fix #794 2014-10-01 22:10:33 +08:00
31e6133413 beego: improve static file index.html simple code 2014-10-01 08:57:10 +08:00
3a5de83ec2 beego: support router case sensitive 2014-09-28 22:10:43 +08:00
f5f3395560 update the isdir 2014-09-24 14:48:08 +08:00
8164367762 beego: flash add success & Set 2014-09-23 23:54:38 +08:00
e1475b72b9 Merge pull request #826 from SnailKnows/patch-2
Update beego.go
2014-09-23 00:31:21 +08:00
f267ee8a12 fix the same name controller for UrlFor 2014-09-23 00:26:07 +08:00
7e060e6e5c fix the static file dir 2014-09-23 00:03:47 +08:00
727d2f9ea1 fix not found when has mulit static dir
robot &robots
2014-09-22 23:44:50 +08:00
67c0c232a1 Update beego.go 2014-09-20 18:56:46 +08:00
6c62198b59 remove the go style 2014-09-15 23:01:12 +08:00
e48e1ddaa9 Merge pull request #812 from ZhengYang/develop
More SQL keywords added and code cleanup
2014-09-11 20:48:18 +08:00
1f9281c830 minor code refactor 2014-09-11 15:17:48 +08:00
ccab9a7044 add more sql keywords 2014-09-11 13:48:39 +08:00
9013f5c6c7 Merge pull request #808 from ZhengYang/develop
more complete support for sql language
2014-09-09 14:21:49 +08:00
29b7ff84e1 more complete support for sql language 2014-09-09 14:17:12 +08:00
fb0cc55822 update the orm read me 2014-09-09 11:55:28 +08:00
0820e21738 Merge pull request #804 from ZhengYang/develop
QueryBuilder for building SQL queries quickly
2014-09-08 19:24:39 +08:00
38eb29fa7b err msg spell correction 2014-09-08 18:41:42 +08:00
cca0a3f76d name correction: QueryBuilder instead of QueryWriter 2014-09-08 18:31:32 +08:00
f9a9b5a905 new query builder based on driver 2014-09-08 17:56:55 +08:00
c667895ce5 added new querybuilder 2014-09-08 17:47:15 +08:00
b2cdabb8a0 added query builder for orm 2014-09-08 17:37:01 +08:00
647a47517d httplib: fix the header function for User-Agent 2014-09-05 23:21:41 +08:00
f7cd1479ba beego: improve the log debug for running server 2014-09-05 17:04:02 +08:00
fcc359af11 beego: fix the Upper for the _method value 2014-09-04 22:13:03 +08:00
08b3e4191e Merge branch 'master' into develop 2014-09-04 21:58:45 +08:00
4f4f7ce257 beego: fix the router for *.* with other regexp 2014-09-04 21:58:17 +08:00
d06e02474f Merge pull request #795 from mvpmvh/context_params
Context params
2014-09-04 09:11:35 +08:00
4d65330ca1 changing my package namespace to astaxie 2014-09-03 19:47:09 -05:00
2dfe1fc61c Merge pull request #792 from haowang1013/develop
fixed uninitialized return error if StartAndGC fails
2014-09-03 23:21:17 +08:00
29b60d6058 fixed uninitialized return error if StartAndGC fails 2014-09-03 22:43:06 +08:00
6eee223352 beego: fix the Upper for the _method value 2014-09-03 09:25:34 +08:00
db51ddab96 GetInt(), GetInt8(), GetInt16(), GetInt32(), GetInt64() and Example tests 2014-08-23 20:24:29 -05:00
baf2c63d5c Merge branch 'astaxie-master' 2014-08-23 10:09:37 -05:00
46 changed files with 1876 additions and 534 deletions

View File

@ -1,16 +0,0 @@
{
"file_line": 500,
"func_line": 80,
"params_num":4,
"results_num":3,
"formated": true,
"pkg_name": true,
"camel_name":true,
"ignore":[
"a/*",
"b/*/c/*.go"
],
"fatal":[
"formated"
]
}

View File

@ -1,7 +1,7 @@
## Beego
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest)
[![GoDoc](https://godoc.org/github.com/astaxie/beego?status.svg)](https://godoc.org/github.com/astaxie/beego)
[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego)
beego is an open-source, high-performance, modularity, full-stack web framework.

View File

@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
m["SessionName"] = SessionName
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
m["SessionSavePath"] = SessionSavePath
m["SessionHashFunc"] = SessionHashFunc
m["SessionHashKey"] = SessionHashKey
m["SessionCookieLifeTime"] = SessionCookieLifeTime
m["UseFcgi"] = UseFcgi
m["MaxMemory"] = MaxMemory
@ -458,6 +456,7 @@ func (admin *adminApp) Run() {
for p, f := range admin.routers {
http.Handle(p, f)
}
BeeLogger.Info("Admin server Running on %s", addr)
err := http.ListenAndServe(addr, nil)
if err != nil {
BeeLogger.Critical("Admin ListenAndServe: ", err)

59
app.go
View File

@ -20,13 +20,8 @@ import (
"net/http"
"net/http/fcgi"
"time"
"github.com/astaxie/beego/context"
)
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// App defines beego application with a new PatternServeMux.
type App struct {
Handlers *ControllerRegistor
@ -48,8 +43,6 @@ func (app *App) Run() {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
}
BeeLogger.Info("Running on %s", addr)
var (
err error
l net.Listener
@ -57,15 +50,24 @@ func (app *App) Run() {
endRunning := make(chan bool, 1)
if UseFcgi {
if HttpPort == 0 {
l, err = net.Listen("unix", addr)
if UseStdIo {
err = fcgi.Serve(nil, app.Handlers) // standard I/O
if err == nil {
BeeLogger.Info("Use FCGI via standard I/O")
} else {
BeeLogger.Info("Cannot use FCGI via standard I/O", err)
}
} else {
l, err = net.Listen("tcp", addr)
if HttpPort == 0 {
l, err = net.Listen("unix", addr)
} else {
l, err = net.Listen("tcp", addr)
}
if err != nil {
BeeLogger.Critical("Listen: ", err)
}
err = fcgi.Serve(l, app.Handlers)
}
if err != nil {
BeeLogger.Critical("Listen: ", err)
}
err = fcgi.Serve(l, app.Handlers)
} else {
app.Server.Addr = addr
app.Server.Handler = app.Handlers
@ -78,6 +80,7 @@ func (app *App) Run() {
if HttpsPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
}
BeeLogger.Info("https server Running on %s", app.Server.Addr)
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err)
@ -90,11 +93,29 @@ func (app *App) Run() {
if EnableHttpListen {
go func() {
app.Server.Addr = addr
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
BeeLogger.Info("http server Running on %s", app.Server.Addr)
if ListenTCP4 && HttpAddr == "" {
ln, err := net.Listen("tcp4", app.Server.Addr)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
err = app.Server.Serve(ln)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}

View File

@ -38,7 +38,7 @@ import (
)
// beego web framework version.
const VERSION = "1.4.1"
const VERSION = "1.4.2"
type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc
@ -308,15 +308,20 @@ func SetStaticPath(url string, path string) *App {
// DelStaticPath removes the static folder setting in this url pattern in beego application.
func DelStaticPath(url string) *App {
if !strings.HasPrefix(url, "/") {
url = "/" + url
}
url = strings.TrimRight(url, "/")
delete(StaticDir, url)
return BeeApp
}
// InsertFilter adds a FilterFunc with pattern condition and action constant.
// The pos means action constant including
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
func InsertFilter(pattern string, pos int, filter FilterFunc) *App {
BeeApp.Handlers.InsertFilter(pattern, pos, filter)
// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
return BeeApp
}
@ -359,6 +364,9 @@ func initBeforeHttpRun() {
}
}
//init mime
AddAPPStartHook(initMime)
// do hooks function
for _, hk := range hooks {
err := hk()
@ -373,10 +381,8 @@ func initBeforeHttpRun() {
if sessionConfig == "" {
sessionConfig = `{"cookieName":"` + SessionName + `",` +
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + SessionSavePath + `",` +
`"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
`"domain":"` + SessionDomain + `",` +
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
@ -404,9 +410,6 @@ func initBeforeHttpRun() {
Get("/docs", serverDocs)
Get("/docs/*", serverDocs)
}
//init mime
AddAPPStartHook(initMime)
}
// this function is for test package init

6
cache/cache.go vendored
View File

@ -81,13 +81,13 @@ func Register(name string, adapter Cache) {
// Create a new cache driver by adapter name and config string.
// config need to be correct JSON as string: {"interval":360}.
// it will start gc automatically.
func NewCache(adapterName, config string) (adapter Cache, e error) {
func NewCache(adapterName, config string) (adapter Cache, err error) {
adapter, ok := adapters[adapterName]
if !ok {
e = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
return
}
err := adapter.StartAndGC(config)
err = adapter.StartAndGC(config)
if err != nil {
adapter = nil
}

View File

@ -75,14 +75,13 @@ func (rc *RedisCache) Get(key string) interface{} {
// put cache to redis.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
var err error
if _, err = rc.do("SET", key, val); err != nil {
if _, err = rc.do("SETEX", key, timeout, val); err != nil {
return err
}
if _, err = rc.do("HSET", rc.key, key, true); err != nil {
return err
}
_, err = rc.do("EXPIRE", key, timeout)
return err
}

533
config.go
View File

@ -15,7 +15,6 @@
package beego
import (
"errors"
"fmt"
"html/template"
"os"
@ -41,6 +40,7 @@ var (
EnableHttpListen bool
HttpAddr string
HttpPort int
ListenTCP4 bool
EnableHttpTLS bool
HttpsPort int
HttpCertFile string
@ -48,20 +48,19 @@ var (
RecoverPanic bool // flag of auto recover panic
AutoRender bool // flag of render template automatically
ViewsPath string
RunMode string // run mode, "dev" or "prod"
AppConfig config.ConfigContainer
AppConfig *beegoAppConfig
RunMode string // run mode, "dev" or "prod"
GlobalSessions *session.Manager // global session mananger
SessionOn bool // flag of starting session auto. default is false.
SessionProvider string // default session provider, memory, mysql , redis ,etc.
SessionName string // the cookie name when saving session id into cookie.
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info.
SessionHashFunc string // session hash generation func.
SessionHashKey string // session hash salt string.
SessionCookieLifeTime int // the life time of session id in cookie.
SessionAutoSetCookie bool // auto setcookie
SessionDomain string // the cookie domain default is empty
UseFcgi bool
UseStdIo bool
MaxMemory int64
EnableGzip bool // flag of enable gzip
DirectoryIndex bool // flag of display directory index. default is false.
@ -81,8 +80,110 @@ var (
FlashSeperator string // used to seperate flash key:value
AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger
RouterCaseSensitive bool // router case sensitive default is true
)
type beegoAppConfig struct {
innerConfig config.ConfigContainer
}
func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, error) {
ac, err := config.NewConfig(AppConfigProvider, AppConfigPath)
if err != nil {
return nil, err
}
rac := &beegoAppConfig{ac}
return rac, nil
}
func (b *beegoAppConfig) Set(key, val string) error {
return b.innerConfig.Set(key, val)
}
func (b *beegoAppConfig) String(key string) string {
v := b.innerConfig.String(RunMode + "::" + key)
if v == "" {
return b.innerConfig.String(key)
}
return v
}
func (b *beegoAppConfig) Strings(key string) []string {
v := b.innerConfig.Strings(RunMode + "::" + key)
if len(v) == 0 {
return b.innerConfig.Strings(key)
}
return v
}
func (b *beegoAppConfig) Int(key string) (int, error) {
v, err := b.innerConfig.Int(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Int(key)
}
return v, nil
}
func (b *beegoAppConfig) Int64(key string) (int64, error) {
v, err := b.innerConfig.Int64(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Int64(key)
}
return v, nil
}
func (b *beegoAppConfig) Bool(key string) (bool, error) {
v, err := b.innerConfig.Bool(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Bool(key)
}
return v, nil
}
func (b *beegoAppConfig) Float(key string) (float64, error) {
v, err := b.innerConfig.Float(RunMode + "::" + key)
if err != nil {
return b.innerConfig.Float(key)
}
return v, nil
}
func (b *beegoAppConfig) DefaultString(key string, defaultval string) string {
return b.innerConfig.DefaultString(key, defaultval)
}
func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string {
return b.innerConfig.DefaultStrings(key, defaultval)
}
func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int {
return b.innerConfig.DefaultInt(key, defaultval)
}
func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 {
return b.innerConfig.DefaultInt64(key, defaultval)
}
func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool {
return b.innerConfig.DefaultBool(key, defaultval)
}
func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 {
return b.innerConfig.DefaultFloat(key, defaultval)
}
func (b *beegoAppConfig) DIY(key string) (interface{}, error) {
return b.innerConfig.DIY(key)
}
func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) {
return b.innerConfig.GetSection(section)
}
func (b *beegoAppConfig) SaveConfigFile(filename string) error {
return b.innerConfig.SaveConfigFile(filename)
}
func init() {
// create beego application
BeeApp = NewApp()
@ -134,12 +235,11 @@ func init() {
SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600
SessionSavePath = ""
SessionHashFunc = "sha1"
SessionHashKey = "beegoserversessionkey"
SessionCookieLifeTime = 0 //set cookie default is the brower life
SessionAutoSetCookie = true
UseFcgi = false
UseStdIo = false
MaxMemory = 1 << 26 //64MB
@ -164,6 +264,8 @@ func init() {
FlashName = "BEEGO_FLASH"
FlashSeperator = "BEEGOFLASH"
RouterCaseSensitive = true
runtime.GOMAXPROCS(runtime.NumCPU())
// init BeeLogger
@ -172,262 +274,211 @@ func init() {
if err != nil {
fmt.Println("init console log error:", err)
}
SetLogFuncCall(true)
err = ParseConfig()
if err != nil && !os.IsNotExist(err) {
if err != nil && os.IsNotExist(err) {
// for init if doesn't have app.conf will not panic
Info(err)
ac := config.NewFakeConfig()
AppConfig = &beegoAppConfig{ac}
Warning(err)
}
}
// ParseConfig parsed default config file.
// now only support ini, next will support json.
func ParseConfig() (err error) {
AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath)
AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath)
if err != nil {
AppConfig = config.NewFakeConfig()
return err
} else {
}
envRunMode := os.Getenv("BEEGO_RUNMODE")
// set the runmode first
if envRunMode != "" {
RunMode = envRunMode
} else if runmode := AppConfig.String("RunMode"); runmode != "" {
RunMode = runmode
}
if v, err := GetConfig("string", "HttpAddr"); err == nil {
HttpAddr = v.(string)
HttpAddr = AppConfig.String("HttpAddr")
if v, err := AppConfig.Int("HttpPort"); err == nil {
HttpPort = v
}
if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
ListenTCP4 = v
}
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil {
EnableHttpListen = v
}
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil {
MaxMemory = maxmemory
}
if appname := AppConfig.String("AppName"); appname != "" {
AppName = appname
}
if autorender, err := AppConfig.Bool("AutoRender"); err == nil {
AutoRender = autorender
}
if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil {
RecoverPanic = autorecover
}
if views := AppConfig.String("ViewsPath"); views != "" {
ViewsPath = views
}
if sessionon, err := AppConfig.Bool("SessionOn"); err == nil {
SessionOn = sessionon
}
if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" {
SessionProvider = sessProvider
}
if sessName := AppConfig.String("SessionName"); sessName != "" {
SessionName = sessName
}
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" {
SessionSavePath = sesssavepath
}
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
SessionGCMaxLifetime = sessMaxLifeTime
}
if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
SessionCookieLifeTime = sesscookielifetime
}
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil {
UseFcgi = usefcgi
}
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil {
EnableGzip = enablegzip
}
if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil {
DirectoryIndex = directoryindex
}
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil {
HttpServerTimeOut = timeout
}
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil {
ErrorsShow = errorsshow
}
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil {
CopyRequestBody = copyrequestbody
}
if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" {
XSRFKEY = xsrfkey
}
if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil {
EnableXSRF = enablexsrf
}
if expire, err := AppConfig.Int("XSRFExpire"); err == nil {
XSRFExpire = expire
}
if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" {
TemplateLeft = tplleft
}
if tplright := AppConfig.String("TemplateRight"); tplright != "" {
TemplateRight = tplright
}
if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil {
EnableHttpTLS = httptls
}
if httpsport, err := AppConfig.Int("HttpsPort"); err == nil {
HttpsPort = httpsport
}
if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
HttpCertFile = certfile
}
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
HttpKeyFile = keyfile
}
if serverName := AppConfig.String("BeegoServerName"); serverName != "" {
BeegoServerName = serverName
}
if flashname := AppConfig.String("FlashName"); flashname != "" {
FlashName = flashname
}
if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" {
FlashSeperator = flashseperator
}
if sd := AppConfig.String("StaticDir"); sd != "" {
for k := range StaticDir {
delete(StaticDir, k)
}
if v, err := GetConfig("int", "HttpPort"); err == nil {
HttpPort = v.(int)
}
if v, err := GetConfig("bool", "EnableHttpListen"); err == nil {
EnableHttpListen = v.(bool)
}
if maxmemory, err := GetConfig("int64", "MaxMemory"); err == nil {
MaxMemory = maxmemory.(int64)
}
if appname, _ := GetConfig("string", "AppName"); appname != "" {
AppName = appname.(string)
}
if runmode, _ := GetConfig("string", "RunMode"); runmode != "" {
RunMode = runmode.(string)
}
if autorender, err := GetConfig("bool", "AutoRender"); err == nil {
AutoRender = autorender.(bool)
}
if autorecover, err := GetConfig("bool", "RecoverPanic"); err == nil {
RecoverPanic = autorecover.(bool)
}
if views, _ := GetConfig("string", "ViewsPath"); views != "" {
ViewsPath = views.(string)
}
if sessionon, err := GetConfig("bool", "SessionOn"); err == nil {
SessionOn = sessionon.(bool)
}
if sessProvider, _ := GetConfig("string", "SessionProvider"); sessProvider != "" {
SessionProvider = sessProvider.(string)
}
if sessName, _ := GetConfig("string", "SessionName"); sessName != "" {
SessionName = sessName.(string)
}
if sesssavepath, _ := GetConfig("string", "SessionSavePath"); sesssavepath != "" {
SessionSavePath = sesssavepath.(string)
}
if sesshashfunc, _ := GetConfig("string", "SessionHashFunc"); sesshashfunc != "" {
SessionHashFunc = sesshashfunc.(string)
}
if sesshashkey, _ := GetConfig("string", "SessionHashKey"); sesshashkey != "" {
SessionHashKey = sesshashkey.(string)
}
if sessMaxLifeTime, err := GetConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
SessionGCMaxLifetime = sessMaxLifeTime.(int64)
}
if sesscookielifetime, err := GetConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
SessionCookieLifeTime = sesscookielifetime.(int)
}
if usefcgi, err := GetConfig("bool", "UseFcgi"); err == nil {
UseFcgi = usefcgi.(bool)
}
if enablegzip, err := GetConfig("bool", "EnableGzip"); err == nil {
EnableGzip = enablegzip.(bool)
}
if directoryindex, err := GetConfig("bool", "DirectoryIndex"); err == nil {
DirectoryIndex = directoryindex.(bool)
}
if timeout, err := GetConfig("int64", "HttpServerTimeOut"); err == nil {
HttpServerTimeOut = timeout.(int64)
}
if errorsshow, err := GetConfig("bool", "ErrorsShow"); err == nil {
ErrorsShow = errorsshow.(bool)
}
if copyrequestbody, err := GetConfig("bool", "CopyRequestBody"); err == nil {
CopyRequestBody = copyrequestbody.(bool)
}
if xsrfkey, _ := GetConfig("string", "XSRFKEY"); xsrfkey != "" {
XSRFKEY = xsrfkey.(string)
}
if enablexsrf, err := GetConfig("bool", "EnableXSRF"); err == nil {
EnableXSRF = enablexsrf.(bool)
}
if expire, err := GetConfig("int", "XSRFExpire"); err == nil {
XSRFExpire = expire.(int)
}
if tplleft, _ := GetConfig("string", "TemplateLeft"); tplleft != "" {
TemplateLeft = tplleft.(string)
}
if tplright, _ := GetConfig("string", "TemplateRight"); tplright != "" {
TemplateRight = tplright.(string)
}
if httptls, err := GetConfig("bool", "EnableHttpTLS"); err == nil {
EnableHttpTLS = httptls.(bool)
}
if httpsport, err := GetConfig("int", "HttpsPort"); err == nil {
HttpsPort = httpsport.(int)
}
if certfile, _ := GetConfig("string", "HttpCertFile"); certfile != "" {
HttpCertFile = certfile.(string)
}
if keyfile, _ := GetConfig("string", "HttpKeyFile"); keyfile != "" {
HttpKeyFile = keyfile.(string)
}
if serverName, _ := GetConfig("string", "BeegoServerName"); serverName != "" {
BeegoServerName = serverName.(string)
}
if flashname, _ := GetConfig("string", "FlashName"); flashname != "" {
FlashName = flashname.(string)
}
if flashseperator, _ := GetConfig("string", "FlashSeperator"); flashseperator != "" {
FlashSeperator = flashseperator.(string)
}
if sd, _ := GetConfig("string", "StaticDir"); sd != "" {
for k := range StaticDir {
delete(StaticDir, k)
sds := strings.Fields(sd)
for _, v := range sds {
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
} else {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
}
sds := strings.Fields(sd.(string))
for _, v := range sds {
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
} else {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
}
}
if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" {
extensions := strings.Split(sgz, ",")
if len(extensions) > 0 {
StaticExtensionsToGzip = []string{}
for _, ext := range extensions {
if len(ext) == 0 {
continue
}
}
}
if sgz, _ := GetConfig("string", "StaticExtensionsToGzip"); sgz != "" {
extensions := strings.Split(sgz.(string), ",")
if len(extensions) > 0 {
StaticExtensionsToGzip = []string{}
for _, ext := range extensions {
if len(ext) == 0 {
continue
}
extWithDot := ext
if extWithDot[:1] != "." {
extWithDot = "." + extWithDot
}
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
extWithDot := ext
if extWithDot[:1] != "." {
extWithDot = "." + extWithDot
}
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
}
}
}
if enableadmin, err := GetConfig("bool", "EnableAdmin"); err == nil {
EnableAdmin = enableadmin.(bool)
}
if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil {
EnableAdmin = enableadmin
}
if adminhttpaddr, _ := GetConfig("string", "AdminHttpAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr.(string)
}
if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr
}
if adminhttpport, err := GetConfig("int", "AdminHttpPort"); err == nil {
AdminHttpPort = adminhttpport.(int)
}
if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil {
AdminHttpPort = adminhttpport
}
if enabledocs, err := GetConfig("bool", "EnableDocs"); err == nil {
EnableDocs = enabledocs.(bool)
}
if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil {
EnableDocs = enabledocs
}
if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil {
RouterCaseSensitive = casesensitive
}
return nil
}
// Getconfig throw the Runmode
// [dev]
// name = astaixe
// IsEnable = false
// [prod]
// name = slene
// IsEnable = true
//
// usage:
// GetConfig("string", "name")
// GetConfig("bool", "IsEnable")
func GetConfig(typ, key string) (interface{}, error) {
switch typ {
case "string":
v := AppConfig.String(RunMode + "::" + key)
if v == "" {
v = AppConfig.String(key)
}
return v, nil
case "strings":
v := AppConfig.Strings(RunMode + "::" + key)
if len(v) == 0 {
v = AppConfig.Strings(key)
}
return v, nil
case "int":
v, err := AppConfig.Int(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Int(key)
}
return v, nil
case "bool":
v, err := AppConfig.Bool(RunMode + "::" + key)
if err != nil {
return AppConfig.Bool(key)
}
return v, nil
case "int64":
v, err := AppConfig.Int64(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Int64(key)
}
return v, nil
case "float":
v, err := AppConfig.Float(RunMode + "::" + key)
if err != nil || v == 0 {
return AppConfig.Float(key)
}
return v, nil
}
return "", errors.New("not support type")
}

View File

@ -48,6 +48,10 @@ type IniConfig struct {
// ParseFile creates a new Config and parses the file configuration from the named file.
func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
return ini.parseFile(name)
}
func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
file, err := os.Open(name)
if err != nil {
return nil, err
@ -66,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
var comment bytes.Buffer
buf := bufio.NewReader(file)
// check the BOM
head, err := buf.Peek(3)
if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
for i := 1; i <= 3; i++ {
buf.ReadByte()
}
}
section := DEFAULT_SECTION
for {
line, _, err := buf.ReadLine()
@ -108,13 +119,48 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
cfg.data[section] = make(map[string]string)
}
keyValue := bytes.SplitN(line, bEqual, 2)
key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
key = strings.ToLower(key)
// handle include "other.conf"
if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
includefiles := strings.Fields(key)
if includefiles[0] == "include" && len(includefiles) == 2 {
otherfile := strings.Trim(includefiles[1], "\"")
if !path.IsAbs(otherfile) {
otherfile = path.Join(path.Dir(name), otherfile)
}
i, err := ini.parseFile(otherfile)
if err != nil {
return nil, err
}
for sec, dt := range i.data {
if _, ok := cfg.data[sec]; !ok {
cfg.data[sec] = make(map[string]string)
}
for k, v := range dt {
cfg.data[sec][k] = v
}
}
for sec, comm := range i.sectionComment {
cfg.sectionComment[sec] = comm
}
for k, comm := range i.keyComment {
cfg.keyComment[k] = comm
}
continue
}
}
if len(keyValue) != 2 {
return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
}
val := bytes.TrimSpace(keyValue[1])
if bytes.HasPrefix(val, bDQuote) {
val = bytes.Trim(val, `"`)
}
key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
key = strings.ToLower(key)
cfg.data[section][key] = string(val)
if comment.Len() > 0 {
cfg.keyComment[section+"."+key] = comment.String()

View File

@ -69,7 +69,8 @@ func (ctx *Context) Abort(status int, body string) {
panic(e)
}
// last panic user string
panic(body)
ctx.ResponseWriter.Write([]byte(body))
panic("User stop run")
}
// Write string to response body.

View File

@ -382,8 +382,37 @@ func (c *Controller) GetStrings(key string) []string {
return []string{}
}
// GetInt returns input value as int64.
func (c *Controller) GetInt(key string) (int64, error) {
// GetInt returns input as an int
func (c *Controller) GetInt(key string) (int, error) {
return strconv.Atoi(c.Ctx.Input.Query(key))
}
// GetInt8 return input as an int8
func (c *Controller) GetInt8(key string) (int8, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 8)
i8 := int8(i64)
return i8, err
}
// GetInt16 returns input as an int16
func (c *Controller) GetInt16(key string) (int16, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 16)
i16 := int16(i64)
return i16, err
}
// GetInt32 returns input as an int32
func (c *Controller) GetInt32(key string) (int32, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
i32 := int32(i64)
return i32, err
}
// GetInt64 returns input value as int64.
func (c *Controller) GetInt64(key string) (int64, error) {
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64)
}

75
controller_test.go Normal file
View File

@ -0,0 +1,75 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package beego
import (
"fmt"
"github.com/astaxie/beego/context"
)
func ExampleGetInt() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt("age")
fmt.Printf("%T", val)
//Output: int
}
func ExampleGetInt8() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt8("age")
fmt.Printf("%T", val)
//Output: int8
}
func ExampleGetInt16() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt16("age")
fmt.Printf("%T", val)
//Output: int16
}
func ExampleGetInt32() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt32("age")
fmt.Printf("%T", val)
//Output: int32
}
func ExampleGetInt64() {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt64("age")
fmt.Printf("%T", val)
//Output: int64
}

View File

@ -17,47 +17,47 @@ type ObjectController struct {
beego.Controller
}
func (this *ObjectController) Post() {
func (o *ObjectController) Post() {
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
objectid := models.AddOne(ob)
this.Data["json"] = map[string]string{"ObjectId": objectid}
this.ServeJson()
o.Data["json"] = map[string]string{"ObjectId": objectid}
o.ServeJson()
}
func (this *ObjectController) Get() {
objectId := this.Ctx.Input.Params[":objectId"]
func (o *ObjectController) Get() {
objectId := o.Ctx.Input.Params[":objectId"]
if objectId != "" {
ob, err := models.GetOne(objectId)
if err != nil {
this.Data["json"] = err
o.Data["json"] = err
} else {
this.Data["json"] = ob
o.Data["json"] = ob
}
} else {
obs := models.GetAll()
this.Data["json"] = obs
o.Data["json"] = obs
}
this.ServeJson()
o.ServeJson()
}
func (this *ObjectController) Put() {
objectId := this.Ctx.Input.Params[":objectId"]
func (o *ObjectController) Put() {
objectId := o.Ctx.Input.Params[":objectId"]
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
err := models.Update(objectId, ob.Score)
if err != nil {
this.Data["json"] = err
o.Data["json"] = err
} else {
this.Data["json"] = "update success!"
o.Data["json"] = "update success!"
}
this.ServeJson()
o.ServeJson()
}
func (this *ObjectController) Delete() {
objectId := this.Ctx.Input.Params[":objectId"]
func (o *ObjectController) Delete() {
objectId := o.Ctx.Input.Params[":objectId"]
models.Delete(objectId)
this.Data["json"] = "delete success!"
this.ServeJson()
o.Data["json"] = "delete success!"
o.ServeJson()
}

View File

@ -14,7 +14,7 @@ type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["host"] = this.Ctx.Request.Host
this.TplNames = "index.tpl"
func (m *MainController) Get() {
m.Data["host"] = m.Ctx.Request.Host
m.TplNames = "index.tpl"
}

View File

@ -150,14 +150,14 @@ type WSController struct {
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func (this *WSController) Get() {
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil)
func (w *WSController) Get() {
ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
if _, ok := err.(websocket.HandshakeError); ok {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return
} else if err != nil {
return

View File

@ -14,12 +14,18 @@
package beego
import "github.com/astaxie/beego/context"
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives.
type FilterRouter struct {
filterFunc FilterFunc
tree *Tree
pattern string
filterFunc FilterFunc
tree *Tree
pattern string
returnOnOutput bool
}
// ValidRouter check current request is valid for this filter.

View File

@ -32,6 +32,24 @@ func NewFlash() *FlashData {
}
}
// Set message to flash
func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data[key] = msg
} else {
fd.Data[key] = fmt.Sprintf(msg, args...)
}
}
// Success writes success message to flash.
func (fd *FlashData) Success(msg string, args ...interface{}) {
if len(args) == 0 {
fd.Data["success"] = msg
} else {
fd.Data["success"] = fmt.Sprintf(msg, args...)
}
}
// Notice writes notice message to flash.
func (fd *FlashData) Notice(msg string, args ...interface{}) {
if len(args) == 0 {

View File

@ -25,12 +25,12 @@ type TestFlashController struct {
Controller
}
func (this *TestFlashController) TestWriteFlash() {
func (t *TestFlashController) TestWriteFlash() {
flash := NewFlash()
flash.Notice("TestFlashString")
flash.Store(&this.Controller)
flash.Store(&t.Controller)
// we choose to serve json because we don't want to load a template html file
this.ServeJson(true)
t.ServeJson(true)
}
func TestFlashHeader(t *testing.T) {

View File

@ -37,6 +37,7 @@ import (
"encoding/xml"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net"
"net/http"
@ -275,35 +276,36 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} else {
b.url = b.url + "?" + paramBody
}
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
} else if b.req.Method == "POST" && b.req.Body == nil {
if len(b.files) > 0 {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
return nil, err
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
log.Fatal(err)
}
fh, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
log.Fatal(err)
}
}
fh, err := os.Open(filename)
if err != nil {
return nil, err
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
return nil, err
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
b.Header("Content-Type", contentType)
b.req.Body = ioutil.NopCloser(bodyBuf)
b.req.ContentLength = int64(bodyBuf.Len())
} else {
bodyWriter.Close()
pw.Close()
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
} else if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
@ -355,7 +357,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
Jar: jar,
}
if b.setting.UserAgent != "" {
if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}

View File

@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) {
}
}
func TestPostFile(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
req.PostFile("uploadfile", "httplib_test.go")
//func TestPostFile(t *testing.T) {
// v := "smallfish"
// req := Post("http://httpbin.org/post")
// req.Debug(true)
// req.Param("username", v)
// req.PostFile("uploadfile", "httplib_test.go")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
// str, err := req.String()
// if err != nil {
// t.Fatal(err)
// }
// t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
// n := strings.Index(str, v)
// if n == -1 {
// t.Fatal(v + " not found in post")
// }
//}
func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String()
@ -203,3 +204,13 @@ func TestToFile(t *testing.T) {
t.Fatal(err)
}
}
func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers")
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")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}

View File

@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
lm.level = loglevel
if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) {
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
}
if ok {
_, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)

46
migration/ddl.go Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package migration
type Table struct {
TableName string
Columns []*Column
}
func (t *Table) Create() string {
return ""
}
func (t *Table) Drop() string {
return ""
}
type Column struct {
Name string
Type string
Default interface{}
}
func Create(tbname string, columns ...Column) string {
return ""
}
func Drop(tbname string, columns ...Column) string {
return ""
}
func TableDDL(tbname string, columns ...Column) string {
return ""
}

View File

@ -217,7 +217,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
n.handlers.routers[k] = t
}
}
if n.handlers.enableFilter {
if ni.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList {
t := NewTree()

View File

@ -6,8 +6,6 @@ A powerful orm framework for go.
It is heavily influenced by Django ORM, SQLAlchemy.
now, beta, unstable, may be changing some api make your app build failed.
**Support Database:**
* MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)

57
orm/qb.go Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package orm
import "errors"
type QueryBuilder interface {
Select(fields ...string) QueryBuilder
From(tables ...string) QueryBuilder
InnerJoin(table string) QueryBuilder
LeftJoin(table string) QueryBuilder
RightJoin(table string) QueryBuilder
On(cond string) QueryBuilder
Where(cond string) QueryBuilder
And(cond string) QueryBuilder
Or(cond string) QueryBuilder
In(vals ...string) QueryBuilder
OrderBy(fields ...string) QueryBuilder
Asc() QueryBuilder
Desc() QueryBuilder
Limit(limit int) QueryBuilder
Offset(offset int) QueryBuilder
GroupBy(fields ...string) QueryBuilder
Having(cond string) QueryBuilder
Update(tables ...string) QueryBuilder
Set(kv ...string) QueryBuilder
Delete(tables ...string) QueryBuilder
InsertInto(table string, fields ...string) QueryBuilder
Values(vals ...string) QueryBuilder
Subquery(sub string, alias string) string
String() string
}
func NewQueryBuilder(driver string) (qb QueryBuilder, err error) {
if driver == "mysql" {
qb = new(MySQLQueryBuilder)
} else if driver == "postgres" {
err = errors.New("postgres query builder is not supported yet!")
} else if driver == "sqlite" {
err = errors.New("sqlite query builder is not supported yet!")
} else {
err = errors.New("unknown driver for query builder!")
}
return
}

153
orm/qb_mysql.go Normal file
View File

@ -0,0 +1,153 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package orm
import (
"fmt"
"strconv"
"strings"
)
const COMMA_SPACE = ", "
type MySQLQueryBuilder struct {
Tokens []string
}
func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "INNER JOIN", table)
return qb
}
func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "LEFT JOIN", table)
return qb
}
func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table)
return qb
}
func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "ON", cond)
return qb
}
func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "WHERE", cond)
return qb
}
func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "AND", cond)
return qb
}
func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "OR", cond)
return qb
}
func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, COMMA_SPACE), ")")
return qb
}
func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) Asc() QueryBuilder {
qb.Tokens = append(qb.Tokens, "ASC")
return qb
}
func (qb *MySQLQueryBuilder) Desc() QueryBuilder {
qb.Tokens = append(qb.Tokens, "DESC")
return qb
}
func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder {
qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit))
return qb
}
func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder {
qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset))
return qb
}
func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "HAVING", cond)
return qb
}
func (qb *MySQLQueryBuilder) Update(tables ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, COMMA_SPACE))
return qb
}
func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "DELETE")
if len(tables) != 0 {
qb.Tokens = append(qb.Tokens, strings.Join(tables, COMMA_SPACE))
}
return qb
}
func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder {
qb.Tokens = append(qb.Tokens, "INSERT INTO", table)
if len(fields) != 0 {
fieldsStr := strings.Join(fields, COMMA_SPACE)
qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")")
}
return qb
}
func (qb *MySQLQueryBuilder) Values(vals ...string) QueryBuilder {
valsStr := strings.Join(vals, COMMA_SPACE)
qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")")
return qb
}
func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string {
return fmt.Sprintf("(%s) AS %s", sub, alias)
}
func (qb *MySQLQueryBuilder) String() string {
return strings.Join(qb.Tokens, " ")
}

View File

@ -42,20 +42,25 @@ func init() {
var (
lastupdateFilename string = "lastupdate.tmp"
commentFilename string
pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments
)
const COMMENTFL = "commentsRouter_"
func init() {
pkgLastupdate = make(map[string]int64)
genInfoList = make(map[string][]ControllerComments)
}
func parserPkg(pkgRealpath, pkgpath string) error {
rep := strings.NewReplacer("/", "_", ".", "_")
commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go"
if !compareFile(pkgRealpath) {
Info(pkgRealpath + " don't has updated")
return nil
}
genInfoList = make(map[string][]ControllerComments)
fileSet := token.NewFileSet()
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name()
@ -155,7 +160,7 @@ func genRouterCode() {
}
}
if globalinfo != "" {
f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go"))
f, err := os.Create(path.Join(workPath, "routers", commentFilename))
if err != nil {
panic(err)
}
@ -165,7 +170,7 @@ func genRouterCode() {
}
func compareFile(pkgRealpath string) bool {
if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) {
if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) {
return true
}
if utils.FileExists(path.Join(workPath, lastupdateFilename)) {

132
router.go
View File

@ -72,9 +72,31 @@ var (
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
"GetControllerAndAction"}
url_placeholder = "{{placeholder}}"
url_placeholder = "{{placeholder}}"
DefaultLogFilter FilterHandler = &logFilter{}
)
type FilterHandler interface {
Filter(*beecontext.Context) bool
}
// default log filter static file will not show
type logFilter struct {
}
func (l *logFilter) Filter(ctx *beecontext.Context) bool {
requestPath := path.Clean(ctx.Input.Request.URL.Path)
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
return true
}
for prefix, _ := range StaticDir {
if strings.HasPrefix(requestPath, prefix) {
return true
}
}
return false
}
// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
func ExceptMethodAppend(action string) {
exceptMethod = append(exceptMethod, action)
@ -163,6 +185,9 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
}
func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {
if !RouterCaseSensitive {
pattern = strings.ToLower(pattern)
}
if t, ok := p.routers[method]; ok {
t.AddRouter(pattern, r)
} else {
@ -376,11 +401,21 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface)
}
// Add a FilterFunc with pattern rule and action constant.
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error {
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
mr := new(FilterRouter)
mr.tree = NewTree()
mr.pattern = pattern
mr.filterFunc = filter
if !RouterCaseSensitive {
pattern = strings.ToLower(pattern)
}
if len(params) == 0 {
mr.returnOnOutput = true
} else {
mr.returnOnOutput = params[0]
}
mr.tree.AddRouter(pattern, true)
return p.insertFilterRouter(pos, mr)
}
@ -415,10 +450,10 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
}
}
}
controllName := strings.Join(paths[:len(paths)-1], ".")
controllName := strings.Join(paths[:len(paths)-1], "/")
methodName := paths[len(paths)-1]
for _, t := range p.routers {
ok, url := p.geturl(t, "/", controllName, methodName, params)
for m, t := range p.routers {
ok, url := p.geturl(t, "/", controllName, methodName, params, m)
if ok {
return url
}
@ -426,24 +461,25 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
return ""
}
func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) {
func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
for k, subtree := range t.fixrouters {
u := path.Join(url, k)
ok, u := p.geturl(subtree, u, controllName, methodName, params)
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
if ok {
return ok, u
}
}
if t.wildcard != nil {
url = path.Join(url, url_placeholder)
ok, u := p.geturl(t.wildcard, url, controllName, methodName, params)
u := path.Join(url, url_placeholder)
ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
if ok {
return ok, u
}
}
for _, l := range t.leaves {
if c, ok := l.runObject.(*controllerInfo); ok {
if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName {
if c.routerType == routerTypeBeego &&
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
find := false
if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
if len(c.methods) == 0 {
@ -455,8 +491,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
}
}
if !find {
for _, md := range c.methods {
if md == methodName {
for m, md := range c.methods {
if (m == "*" || m == httpMethod) && md == methodName {
find = true
}
}
@ -564,15 +600,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Output.Context = context
context.Output.EnableGzip = EnableGzip
var urlPath string
if !RouterCaseSensitive {
urlPath = strings.ToLower(r.URL.Path)
} else {
urlPath = r.URL.Path
}
// defined filter function
do_filter := func(pos int) (started bool) {
if p.enableFilter {
if l, ok := p.filters[pos]; ok {
for _, filterR := range l {
if ok, p := filterR.ValidRouter(r.URL.Path); ok {
if ok, p := filterR.ValidRouter(urlPath); ok {
context.Input.Params = p
filterR.filterFunc(context)
if w.started {
if filterR.returnOnOutput && w.started {
return true
}
}
@ -602,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
// session init
if SessionOn {
context.Input.CruSession = GlobalSessions.SessionStart(w, r)
var err error
context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
if err != nil {
Error(err)
middleware.Exception("503", rw, r, "")
return
}
defer func() {
context.Input.CruSession.SessionRelease(w)
}()
@ -626,8 +674,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
if !findrouter {
if t, ok := p.routers[r.Method]; ok {
runObject, p := t.Match(r.URL.Path)
http_method := r.Method
if http_method == "POST" && context.Input.Query("_method") == "PUT" {
http_method = "PUT"
}
if http_method == "POST" && context.Input.Query("_method") == "DELETE" {
http_method = "DELETE"
}
if t, ok := p.routers[http_method]; ok {
runObject, p := t.Match(urlPath)
if r, ok := runObject.(*controllerInfo); ok {
routerInfo = r
findrouter = true
@ -705,7 +763,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if EnableXSRF {
execController.XsrfToken()
if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
(r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) {
(r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) {
execController.CheckXsrfCookie()
}
}
@ -783,7 +841,9 @@ Admin:
} else {
devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch")
}
Debug(devinfo)
if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) {
Debug(devinfo)
}
}
// Call WriteHeader if status code has been set changed
@ -797,7 +857,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
if err == USERSTOPRUN {
return
}
if _, ok := err.(middleware.HTTPException); ok {
if he, ok := err.(middleware.HTTPException); ok {
rw.WriteHeader(he.StatusCode)
rw.Write([]byte(he.Description))
// catch intented errors, only for HTTP 4XX and 5XX
} else {
if RunMode == "dev" {
@ -829,9 +891,15 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
} else {
// in production model show all infomation
if ErrorsShow {
handler := p.getErrorHandler(fmt.Sprint(err))
handler(rw, r)
return
if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
handler(rw, r)
return
} else if handler, ok := middleware.ErrorMaps["503"]; ok {
handler(rw, r)
return
} else {
rw.Write([]byte(fmt.Sprint(err)))
}
} else {
Critical("the request url is ", r.URL.Path)
Critical("Handler crashed with error", err)
@ -850,24 +918,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
}
}
// there always should be error handler that sets error code accordingly for all unhandled errors.
// in order to have custom UI for error page it's necessary to override "500" error.
func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
handler := middleware.SimpleServerError
ok := true
if errorCode != "" {
handler, ok = middleware.ErrorMaps[errorCode]
if !ok {
handler, ok = middleware.ErrorMaps["500"]
}
if !ok || handler == nil {
handler = middleware.SimpleServerError
}
}
return handler
}
//responseWriter is a wrapper for the http.ResponseWriter
//started set to true if response was written to then don't execute other handler
type responseWriter struct {

View File

@ -17,6 +17,7 @@ package beego
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/astaxie/beego/context"
@ -26,33 +27,33 @@ type TestController struct {
Controller
}
func (this *TestController) Get() {
this.Data["Username"] = "astaxie"
this.Ctx.Output.Body([]byte("ok"))
func (tc *TestController) Get() {
tc.Data["Username"] = "astaxie"
tc.Ctx.Output.Body([]byte("ok"))
}
func (this *TestController) Post() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
func (tc *TestController) Post() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
}
func (this *TestController) Param() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
func (tc *TestController) Param() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
}
func (this *TestController) List() {
this.Ctx.Output.Body([]byte("i am list"))
func (tc *TestController) List() {
tc.Ctx.Output.Body([]byte("i am list"))
}
func (this *TestController) Params() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Params["0"] + this.Ctx.Input.Params["1"] + this.Ctx.Input.Params["2"]))
func (tc *TestController) Params() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"]))
}
func (this *TestController) Myext() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Param(":ext")))
func (tc *TestController) Myext() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
}
func (this *TestController) GetUrl() {
this.Ctx.Output.Body([]byte(this.UrlFor(".Myext")))
func (tc *TestController) GetUrl() {
tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext")))
}
func (t *TestController) GetParams() {
@ -385,3 +386,196 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request
return recorder, request
}
// Execution point: BeforeRouter
// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
func TestFilterBeforeRouter(t *testing.T) {
testName := "TestFilterBeforeRouter"
url := "/beforeRouter"
mux := NewControllerRegister()
mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
t.Errorf(testName + " BeforeRouter did not run")
}
if strings.Contains(rw.Body.String(), "hello") == true {
t.Errorf(testName + " BeforeRouter did not return properly")
}
}
// Execution point: BeforeExec
// expectation: only BeforeExec function is executed, match as router determines route only
func TestFilterBeforeExec(t *testing.T) {
testName := "TestFilterBeforeExec"
url := "/beforeExec"
mux := NewControllerRegister()
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
mux.InsertFilter(url, BeforeExec, beegoBeforeExec1)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
t.Errorf(testName + " BeforeExec did not run")
}
if strings.Contains(rw.Body.String(), "hello") == true {
t.Errorf(testName + " BeforeExec did not return properly")
}
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
t.Errorf(testName + " BeforeRouter ran in error")
}
}
// Execution point: AfterExec
// expectation: only AfterExec function is executed, match as router handles
func TestFilterAfterExec(t *testing.T) {
testName := "TestFilterAfterExec"
url := "/afterExec"
mux := NewControllerRegister()
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
mux.InsertFilter(url, AfterExec, beegoAfterExec1)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "AfterExec1") == false {
t.Errorf(testName + " AfterExec did not run")
}
if strings.Contains(rw.Body.String(), "hello") == false {
t.Errorf(testName + " handler did not run properly")
}
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
t.Errorf(testName + " BeforeRouter ran in error")
}
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
t.Errorf(testName + " BeforeExec ran in error")
}
}
// Execution point: FinishRouter
// expectation: only FinishRouter function is executed, match as router handles
func TestFilterFinishRouter(t *testing.T) {
testName := "TestFilterFinishRouter"
url := "/finishRouter"
mux := NewControllerRegister()
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
mux.InsertFilter(url, AfterExec, beegoFilterNoOutput)
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
t.Errorf(testName + " FinishRouter did not run")
}
if strings.Contains(rw.Body.String(), "hello") == false {
t.Errorf(testName + " handler did not run properly")
}
if strings.Contains(rw.Body.String(), "AfterExec1") == true {
t.Errorf(testName + " AfterExec ran in error")
}
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
t.Errorf(testName + " BeforeRouter ran in error")
}
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
t.Errorf(testName + " BeforeExec ran in error")
}
}
// Execution point: FinishRouter
// expectation: only first FinishRouter function is executed, match as router handles
func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
testName := "TestFilterFinishRouterMultiFirstOnly"
url := "/finishRouterMultiFirstOnly"
mux := NewControllerRegister()
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
t.Errorf(testName + " FinishRouter1 did not run")
}
if strings.Contains(rw.Body.String(), "hello") == false {
t.Errorf(testName + " handler did not run properly")
}
// not expected in body
if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
t.Errorf(testName + " FinishRouter2 did run")
}
}
// Execution point: FinishRouter
// expectation: both FinishRouter functions execute, match as router handles
func TestFilterFinishRouterMulti(t *testing.T) {
testName := "TestFilterFinishRouterMulti"
url := "/finishRouterMulti"
mux := NewControllerRegister()
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
mux.Get(url, beegoFilterFunc)
rw, r := testRequest("GET", url)
mux.ServeHTTP(rw, r)
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
t.Errorf(testName + " FinishRouter1 did not run")
}
if strings.Contains(rw.Body.String(), "hello") == false {
t.Errorf(testName + " handler did not run properly")
}
if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
t.Errorf(testName + " FinishRouter2 did not run properly")
}
}
func beegoFilterNoOutput(ctx *context.Context) {
return
}
func beegoBeforeRouter1(ctx *context.Context) {
ctx.WriteString("|BeforeRouter1")
}
func beegoBeforeRouter2(ctx *context.Context) {
ctx.WriteString("|BeforeRouter2")
}
func beegoBeforeExec1(ctx *context.Context) {
ctx.WriteString("|BeforeExec1")
}
func beegoBeforeExec2(ctx *context.Context) {
ctx.WriteString("|BeforeExec2")
}
func beegoAfterExec1(ctx *context.Context) {
ctx.WriteString("|AfterExec1")
}
func beegoAfterExec2(ctx *context.Context) {
ctx.WriteString("|AfterExec2")
}
func beegoFinishRouter1(ctx *context.Context) {
ctx.WriteString("|FinishRouter1")
}
func beegoFinishRouter2(ctx *context.Context) {
ctx.WriteString("|FinishRouter2")
}

View File

@ -0,0 +1,168 @@
package session
import (
"net/http"
"sync"
"github.com/astaxie/beego/session"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
)
var ledispder = &LedisProvider{}
var c *ledis.DB
// ledis session store
type LedisSessionStore struct {
sid string
lock sync.RWMutex
values map[interface{}]interface{}
maxlifetime int64
}
// set value in ledis session
func (ls *LedisSessionStore) Set(key, value interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values[key] = value
return nil
}
// get value in ledis session
func (ls *LedisSessionStore) Get(key interface{}) interface{} {
ls.lock.RLock()
defer ls.lock.RUnlock()
if v, ok := ls.values[key]; ok {
return v
} else {
return nil
}
}
// delete value in ledis session
func (ls *LedisSessionStore) Delete(key interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
delete(ls.values, key)
return nil
}
// clear all values in ledis session
func (ls *LedisSessionStore) Flush() error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values = make(map[interface{}]interface{})
return nil
}
// get ledis session id
func (ls *LedisSessionStore) SessionID() string {
return ls.sid
}
// save session values to ledis
func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
b, err := session.EncodeGob(ls.values)
if err != nil {
return
}
c.Set([]byte(ls.sid), b)
c.Expire([]byte(ls.sid), ls.maxlifetime)
}
// ledis session provider
type LedisProvider struct {
maxlifetime int64
savePath string
}
// init ledis session
// savepath like ledis server saveDataPath,pool size
// e.g. 127.0.0.1:6379,100,astaxie
func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
lp.maxlifetime = maxlifetime
lp.savePath = savePath
cfg := new(config.Config)
cfg.DataDir = lp.savePath
var err error
nowLedis, err := ledis.Open(cfg)
c, err = nowLedis.Select(0)
if err != nil {
println(err)
return nil
}
return nil
}
// read ledis session by sid
func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
if len(kvs) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob(kvs)
if err != nil {
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
return ls, nil
}
// check ledis session exist by sid
func (lp *LedisProvider) SessionExist(sid string) bool {
count, _ := c.Exists([]byte(sid))
if count == 0 {
return false
} else {
return true
}
}
// generate new sid for ledis session
func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
count, _ := c.Exists([]byte(sid))
if count == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Set([]byte(sid), []byte(""))
c.Expire([]byte(sid), lp.maxlifetime)
} else {
data, _ := c.Get([]byte(oldsid))
c.Set([]byte(sid), data)
c.Expire([]byte(sid), lp.maxlifetime)
}
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
if len(kvs) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob([]byte(kvs))
if err != nil {
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
return ls, nil
}
// delete ledis session by id
func (lp *LedisProvider) SessionDestroy(sid string) error {
c.Del([]byte(sid))
return nil
}
// Impelment method, no used.
func (lp *LedisProvider) SessionGC() {
return
}
// @todo
func (lp *LedisProvider) SessionAll() int {
return 0
}
func init() {
session.Register("ledis", ledispder)
}

View File

@ -109,7 +109,7 @@ func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
return
}
c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime)
c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
}
// redis session provider

View File

@ -29,7 +29,10 @@ func TestCookie(t *testing.T) {
}
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sess := globalSessions.SessionStart(w, r)
sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("set error,", err)
}
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set error,", err)

View File

@ -26,9 +26,12 @@ func TestMem(t *testing.T) {
go globalSessions.GC()
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sess := globalSessions.SessionStart(w, r)
sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("set error,", err)
}
defer sess.SessionRelease(w)
err := sess.Set("username", "astaxie")
err = sess.Set("username", "astaxie")
if err != nil {
t.Fatal("set error,", err)
}

View File

@ -28,19 +28,13 @@
package session
import (
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"github.com/astaxie/beego/utils"
)
// SessionStore contains all data for one session process with specific id.
@ -81,16 +75,15 @@ func Register(name string, provide Provider) {
}
type managerConfig struct {
CookieName string `json:"cookieName"`
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
Gclifetime int64 `json:"gclifetime"`
Maxlifetime int64 `json:"maxLifetime"`
Secure bool `json:"secure"`
SessionIDHashFunc string `json:"sessionIDHashFunc"`
SessionIDHashKey string `json:"sessionIDHashKey"`
CookieLifeTime int `json:"cookieLifeTime"`
ProviderConfig string `json:"providerConfig"`
Domain string `json:"domain"`
CookieName string `json:"cookieName"`
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
Gclifetime int64 `json:"gclifetime"`
Maxlifetime int64 `json:"maxLifetime"`
Secure bool `json:"secure"`
CookieLifeTime int `json:"cookieLifeTime"`
ProviderConfig string `json:"providerConfig"`
Domain string `json:"domain"`
SessionIdLength int64 `json:"sessionIdLength"`
}
// Manager contains Provider and its configuration.
@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) {
if err != nil {
return nil, err
}
if cf.SessionIDHashFunc == "" {
cf.SessionIDHashFunc = "sha1"
}
if cf.SessionIDHashKey == "" {
cf.SessionIDHashKey = string(generateRandomKey(16))
if cf.SessionIdLength == 0 {
cf.SessionIdLength = 16
}
return &Manager{
@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) {
// Start session. generate or read the session id from http request.
// if session id exists, return SessionStore with this id.
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) {
cookie, err := r.Cookie(manager.config.CookieName)
if err != nil || cookie.Value == "" {
sid := manager.sessionId(r)
session, _ = manager.provider.SessionRead(sid)
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) {
cookie, errs := r.Cookie(manager.config.CookieName)
if errs != nil || cookie.Value == "" {
sid, errs := manager.sessionId(r)
if errs != nil {
return nil, errs
}
session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: "/",
@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
}
r.AddCookie(cookie)
} else {
sid, _ := url.QueryUnescape(cookie.Value)
sid, errs := url.QueryUnescape(cookie.Value)
if errs != nil {
return nil, errs
}
if manager.provider.SessionExist(sid) {
session, _ = manager.provider.SessionRead(sid)
session, err = manager.provider.SessionRead(sid)
} else {
sid = manager.sessionId(r)
session, _ = manager.provider.SessionRead(sid)
sid, err = manager.sessionId(r)
if err != nil {
return nil, err
}
session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: "/",
@ -219,7 +219,10 @@ func (manager *Manager) GC() {
// Regenerate a session id for this SessionStore who's id is saving in http request.
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
sid := manager.sessionId(r)
sid, err := manager.sessionId(r)
if err != nil {
return
}
cookie, err := r.Cookie(manager.config.CookieName)
if err != nil && cookie.Value == "" {
//delete old cookie
@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int {
return manager.provider.SessionAll()
}
// Set hash function for generating session id.
func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
manager.config.SessionIDHashFunc = hasfunc
manager.config.SessionIDHashKey = hashkey
}
// Set cookie with https.
func (manager *Manager) SetSecure(secure bool) {
manager.config.Secure = secure
}
// generate session id with rand string, unix nano time, remote addr by hash function.
func (manager *Manager) sessionId(r *http.Request) (sid string) {
bs := make([]byte, 32)
if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil {
bs = utils.RandomCreateBytes(32)
func (manager *Manager) sessionId(r *http.Request) (string, error) {
b := make([]byte, manager.config.SessionIdLength)
n, err := rand.Read(b)
if n != len(b) || err != nil {
return "", fmt.Errorf("Could not successfully read from the system CSPRNG.")
}
sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs)
if manager.config.SessionIDHashFunc == "md5" {
h := md5.New()
h.Write([]byte(sig))
sid = hex.EncodeToString(h.Sum(nil))
} else if manager.config.SessionIDHashFunc == "sha1" {
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
fmt.Fprintf(h, "%s", sig)
sid = hex.EncodeToString(h.Sum(nil))
} else {
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
fmt.Fprintf(h, "%s", sig)
sid = hex.EncodeToString(h.Sum(nil))
}
return
return hex.EncodeToString(b), nil
}

View File

@ -31,6 +31,7 @@ func serverStaticRouter(ctx *context.Context) {
return
}
requestPath := path.Clean(ctx.Input.Request.URL.Path)
i := 0
for prefix, staticDir := range StaticDir {
if len(prefix) == 0 {
continue
@ -41,8 +42,13 @@ func serverStaticRouter(ctx *context.Context) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return
} else {
http.NotFound(ctx.ResponseWriter, ctx.Request)
return
i++
if i == len(StaticDir) {
http.NotFound(ctx.ResponseWriter, ctx.Request)
return
} else {
continue
}
}
}
if strings.HasPrefix(requestPath, prefix) {
@ -59,9 +65,20 @@ func serverStaticRouter(ctx *context.Context) {
return
}
//if the request is dir and DirectoryIndex is false then
if finfo.IsDir() && !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
return
if finfo.IsDir() {
if !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
return
} else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' {
http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302)
return
}
} else if strings.HasSuffix(requestPath, "/index.html") {
file := path.Join(staticDir, requestPath)
if utils.FileExists(file) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return
}
}
//This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request

View File

@ -302,6 +302,14 @@ func ParseForm(form url.Values, obj interface{}) error {
switch fieldT.Type.Kind() {
case reflect.Bool:
if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
fieldV.SetBool(true)
continue
}
if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
fieldV.SetBool(false)
continue
}
b, err := strconv.ParseBool(value)
if err != nil {
return err
@ -329,6 +337,19 @@ func ParseForm(form url.Values, obj interface{}) error {
fieldV.Set(reflect.ValueOf(value))
case reflect.String:
fieldV.SetString(value)
case reflect.Struct:
switch fieldT.Type.String() {
case "time.Time":
format := time.RFC3339
if len(tags) > 1 {
format = tags[1]
}
t, err := time.Parse(format, value)
if err != nil {
return err
}
fieldV.Set(reflect.ValueOf(t))
}
}
}
return nil
@ -368,23 +389,31 @@ func RenderForm(obj interface{}) template.HTML {
fieldT := objT.Field(i)
label, name, fType, ignored := parseFormTag(fieldT)
label, name, fType, id, class, ignored := parseFormTag(fieldT)
if ignored {
continue
}
raw = append(raw, renderFormField(label, name, fType, fieldV.Interface()))
raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class))
}
return template.HTML(strings.Join(raw, "</br>"))
}
// renderFormField returns a string containing HTML of a single form field.
func renderFormField(label, name, fType string, value interface{}) string {
if isValidForInput(fType) {
return fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, label, name, fType, value)
func renderFormField(label, name, fType string, value interface{}, id string, class string) string {
if id != "" {
id = " id=\"" + id + "\""
}
return fmt.Sprintf(`%v<%v name="%v">%v</%v>`, label, fType, name, value, fType)
if class != "" {
class = " class=\"" + class + "\""
}
if isValidForInput(fType) {
return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v">`, label, id, class, name, fType, value)
}
return fmt.Sprintf(`%v<%v%v%v name="%v">%v</%v>`, label, fType, id, class, name, value, fType)
}
// 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 {
// parseFormTag takes the stuct-tag of a StructField and parses the `form` value.
// returned are the form label, name-property, type and wether the field should be ignored.
func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) {
func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool) {
tags := strings.Split(fieldT.Tag.Get("form"), ",")
label = fieldT.Name + ": "
name = fieldT.Name
fType = "text"
ignored = false
id = fieldT.Tag.Get("id")
class = fieldT.Tag.Get("class")
switch len(tags) {
case 1:

View File

@ -102,12 +102,14 @@ func TestHtmlunquote(t *testing.T) {
func TestParseForm(t *testing.T) {
type user struct {
Id int `form:"-"`
tag string `form:"tag"`
Name interface{} `form:"username"`
Age int `form:"age,text"`
Email string
Intro string `form:",textarea"`
Id int `form:"-"`
tag string `form:"tag"`
Name interface{} `form:"username"`
Age int `form:"age,text"`
Email string
Intro string `form:",textarea"`
StrBool bool `form:"strbool"`
Date time.Time `form:"date,2006-01-02"`
}
u := user{}
@ -119,6 +121,8 @@ func TestParseForm(t *testing.T) {
"age": []string{"40"},
"Email": []string{"test@gmail.com"},
"Intro": []string{"I am an engineer!"},
"strbool": []string{"yes"},
"date": []string{"2014-11-12"},
}
if err := ParseForm(form, u); err == nil {
t.Fatal("nothing will be changed")
@ -144,6 +148,13 @@ func TestParseForm(t *testing.T) {
if u.Intro != "I am an engineer!" {
t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
}
if u.StrBool != true {
t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
}
y, m, d := u.Date.Date()
if y != 2014 || m.String() != "November" || d != 12 {
t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
}
}
func TestRenderForm(t *testing.T) {
@ -175,12 +186,12 @@ func TestRenderForm(t *testing.T) {
}
func TestRenderFormField(t *testing.T) {
html := renderFormField("Label: ", "Name", "text", "Value")
html := renderFormField("Label: ", "Name", "text", "Value", "", "")
if html != `Label: <input name="Name" type="text" value="Value">` {
t.Errorf("Wrong html output for input[type=text]: %v ", html)
}
html = renderFormField("Label: ", "Name", "textarea", "Value")
html = renderFormField("Label: ", "Name", "textarea", "Value", "", "")
if html != `Label: <textarea name="Name">Value</textarea>` {
t.Errorf("Wrong html output for textarea: %v ", html)
}
@ -192,33 +203,34 @@ func TestParseFormTag(t *testing.T) {
All int `form:"name,text,年龄:"`
NoName int `form:",hidden,年龄:"`
OnlyLabel int `form:",,年龄:"`
OnlyName int `form:"name"`
OnlyName int `form:"name" id:"name" class:"form-name"`
Ignored int `form:"-"`
}
objT := reflect.TypeOf(&user{}).Elem()
label, name, fType, ignored := parseFormTag(objT.Field(0))
label, name, fType, id, class, ignored := parseFormTag(objT.Field(0))
if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) {
t.Errorf("Form Tag with name, label and type was not correctly parsed.")
}
label, name, fType, ignored = parseFormTag(objT.Field(1))
label, name, fType, id, class, ignored = parseFormTag(objT.Field(1))
if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) {
t.Errorf("Form Tag with label and type but without name was not correctly parsed.")
}
label, name, fType, ignored = parseFormTag(objT.Field(2))
label, name, fType, id, class, ignored = parseFormTag(objT.Field(2))
if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) {
t.Errorf("Form Tag containing only label was not correctly parsed.")
}
label, name, fType, ignored = parseFormTag(objT.Field(3))
if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) {
label, name, fType, id, class, ignored = parseFormTag(objT.Field(3))
if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false &&
id == "name" && class == "form-name") {
t.Errorf("Form Tag containing only name was not correctly parsed.")
}
label, name, fType, ignored = parseFormTag(objT.Field(4))
label, name, fType, id, class, ignored = parseFormTag(objT.Field(4))
if ignored == false {
t.Errorf("Form Tag that should be ignored was not correctly parsed.")
}

View File

@ -111,6 +111,27 @@ func (m *UrlMap) GetMap() map[string]interface{} {
return content
}
func (m *UrlMap) GetMapData() []map[string]interface{} {
resultLists := make([]map[string]interface{}, 0)
for k, v := range m.urlmap {
for kk, vv := range v {
result := map[string]interface{}{
"request_url": k,
"method": kk,
"times": vv.RequestNum,
"total_time": toS(vv.TotalTime),
"max_time": toS(vv.MaxTime),
"min_time": toS(vv.MinTime),
"avg_time": toS(time.Duration(int64(vv.TotalTime) / vv.RequestNum)),
}
resultLists = append(resultLists, result)
}
}
return resultLists
}
// global statistics data map
var StatisticsMap *UrlMap

View File

@ -15,6 +15,7 @@
package toolbox
import (
"encoding/json"
"testing"
"time"
)
@ -28,4 +29,12 @@ func TestStatics(t *testing.T) {
StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
t.Log(StatisticsMap.GetMap())
data := StatisticsMap.GetMapData()
b, err := json.Marshal(data)
if err != nil {
t.Errorf(err.Error())
}
t.Log(string(b))
}

18
tree.go
View File

@ -237,12 +237,18 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
regexpStr = "/" + regexpStr
}
} else if reg != "" {
for _, w := range params {
if w == "." || w == ":" {
continue
if seg == "*.*" {
regexpStr = "/([^.]+).(.+)"
} else {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "/([^/]+)" + regexpStr
}
regexpStr = "/([^/]+)" + regexpStr
}
}
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
} else {
@ -388,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
}
return true, params
}
if len(wildcardValues) <= j {
return false, nil
}
params[v] = wildcardValues[j]
j += 1
}
@ -396,6 +405,7 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
}
return true, params
}
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
return false, nil
}

View File

@ -42,6 +42,9 @@ func init() {
routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
routers = append(routers, testinfo{"/dl/:width:int/:height:int/*.*",
"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})

View File

@ -157,19 +157,37 @@ func (e *Email) Bytes() ([]byte, error) {
}
// Add attach file to the send mail
func (e *Email) AttachFile(filename string) (a *Attachment, err error) {
func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
return
}
filename := args[0]
id := ""
if len(args) > 1 {
id = args[1]
}
f, err := os.Open(filename)
if err != nil {
return
}
ct := mime.TypeByExtension(filepath.Ext(filename))
basename := path.Base(filename)
return e.Attach(f, basename, ct)
return e.Attach(f, basename, ct, id)
}
// Attach is used to attach content from an io.Reader to the email.
// Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) {
func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
return
}
c := args[0] //Content-Type
id := ""
if len(args) > 1 {
id = args[1] //Content-ID
}
var buffer bytes.Buffer
if _, err = io.Copy(&buffer, r); err != nil {
return
@ -186,7 +204,12 @@ func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, e
// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
at.Header.Set("Content-Type", "application/octet-stream")
}
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
if id != "" {
at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename))
at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id))
} else {
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
}
at.Header.Set("Content-Transfer-Encoding", "base64")
e.Attachments = append(e.Attachments, at)
return at, nil
@ -269,7 +292,7 @@ func qpEscape(dest []byte, c byte) {
const nums = "0123456789ABCDEF"
dest[0] = '='
dest[1] = nums[(c&0xf0)>>4]
dest[2] = nums[(c & 0xf)]
dest[2] = nums[(c&0xf)]
}
// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer

View File

@ -0,0 +1,26 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pagination
import (
"github.com/astaxie/beego/context"
)
// Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
paginator = NewPaginator(context.Request, per, nums)
context.Input.Data["paginator"] = paginator
return
}

59
utils/pagination/doc.go Normal file
View File

@ -0,0 +1,59 @@
/*
The pagination package provides utilities to setup a paginator within the
context of a http request.
Usage
In your beego.Controller:
package controllers
import "github.com/astaxie/beego/utils/pagination"
type PostsController struct {
beego.Controller
}
func (this *PostsController) ListAllPosts() {
// sets this.Data["paginator"] with the current offset (from the url query param)
postsPerPage := 20
paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts())
// fetch the next 20 posts
this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage)
}
In your view templates:
{{if .paginator.HasPages}}
<ul class="pagination pagination">
{{if .paginator.HasPrev}}
<li><a href="{{.paginator.PageLinkFirst}}">{{ i18n .Lang "paginator.first_page"}}</a></li>
<li><a href="{{.paginator.PageLinkPrev}}">&laquo;</a></li>
{{else}}
<li class="disabled"><a>{{ i18n .Lang "paginator.first_page"}}</a></li>
<li class="disabled"><a>&laquo;</a></li>
{{end}}
{{range $index, $page := .paginator.Pages}}
<li{{if $.paginator.IsActive .}} class="active"{{end}}>
<a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
</li>
{{end}}
{{if .paginator.HasNext}}
<li><a href="{{.paginator.PageLinkNext}}">&raquo;</a></li>
<li><a href="{{.paginator.PageLinkLast}}">{{ i18n .Lang "paginator.last_page"}}</a></li>
{{else}}
<li class="disabled"><a>&raquo;</a></li>
<li class="disabled"><a>{{ i18n .Lang "paginator.last_page"}}</a></li>
{{end}}
</ul>
{{end}}
See also
http://beego.me/docs/mvc/view/page.md
*/
package pagination

View File

@ -0,0 +1,189 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pagination
import (
"math"
"net/http"
"net/url"
"strconv"
)
// Paginator within the state of a http request.
type Paginator struct {
Request *http.Request
PerPageNums int
MaxPages int
nums int64
pageRange []int
pageNums int
page int
}
// Returns the total number of pages.
func (p *Paginator) PageNums() int {
if p.pageNums != 0 {
return p.pageNums
}
pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
if p.MaxPages > 0 {
pageNums = math.Min(pageNums, float64(p.MaxPages))
}
p.pageNums = int(pageNums)
return p.pageNums
}
// Returns the total number of items (e.g. from doing SQL count).
func (p *Paginator) Nums() int64 {
return p.nums
}
// Sets the total number of items.
func (p *Paginator) SetNums(nums interface{}) {
p.nums, _ = ToInt64(nums)
}
// Returns the current page.
func (p *Paginator) Page() int {
if p.page != 0 {
return p.page
}
if p.Request.Form == nil {
p.Request.ParseForm()
}
p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
if p.page > p.PageNums() {
p.page = p.PageNums()
}
if p.page <= 0 {
p.page = 1
}
return p.page
}
// Returns a list of all pages.
//
// Usage (in a view template):
//
// {{range $index, $page := .paginator.Pages}}
// <li{{if $.paginator.IsActive .}} class="active"{{end}}>
// <a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
// </li>
// {{end}}
func (p *Paginator) Pages() []int {
if p.pageRange == nil && p.nums > 0 {
var pages []int
pageNums := p.PageNums()
page := p.Page()
switch {
case page >= pageNums-4 && pageNums > 9:
start := pageNums - 9 + 1
pages = make([]int, 9)
for i, _ := range pages {
pages[i] = start + i
}
case page >= 5 && pageNums > 9:
start := page - 5 + 1
pages = make([]int, int(math.Min(9, float64(page+4+1))))
for i, _ := range pages {
pages[i] = start + i
}
default:
pages = make([]int, int(math.Min(9, float64(pageNums))))
for i, _ := range pages {
pages[i] = i + 1
}
}
p.pageRange = pages
}
return p.pageRange
}
// Returns URL for a given page index.
func (p *Paginator) PageLink(page int) string {
link, _ := url.ParseRequestURI(p.Request.RequestURI)
values := link.Query()
if page == 1 {
values.Del("p")
} else {
values.Set("p", strconv.Itoa(page))
}
link.RawQuery = values.Encode()
return link.String()
}
// Returns URL to the previous page.
func (p *Paginator) PageLinkPrev() (link string) {
if p.HasPrev() {
link = p.PageLink(p.Page() - 1)
}
return
}
// Returns URL to the next page.
func (p *Paginator) PageLinkNext() (link string) {
if p.HasNext() {
link = p.PageLink(p.Page() + 1)
}
return
}
// Returns URL to the first page.
func (p *Paginator) PageLinkFirst() (link string) {
return p.PageLink(1)
}
// Returns URL to the last page.
func (p *Paginator) PageLinkLast() (link string) {
return p.PageLink(p.PageNums())
}
// Returns true if the current page has a predecessor.
func (p *Paginator) HasPrev() bool {
return p.Page() > 1
}
// Returns true if the current page has a successor.
func (p *Paginator) HasNext() bool {
return p.Page() < p.PageNums()
}
// Returns true if the given page index points to the current page.
func (p *Paginator) IsActive(page int) bool {
return p.Page() == page
}
// Returns the current offset.
func (p *Paginator) Offset() int {
return (p.Page() - 1) * p.PerPageNums
}
// Returns true if there is more than one page.
func (p *Paginator) HasPages() bool {
return p.PageNums() > 1
}
// Instantiates a paginator struct for the current http request.
func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator {
p := Paginator{}
p.Request = req
if per <= 0 {
per = 10
}
p.PerPageNums = per
p.SetNums(nums)
return &p
}

34
utils/pagination/utils.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pagination
import (
"fmt"
"reflect"
)
// convert any numeric value to int64
func ToInt64(value interface{}) (d int64, err error) {
val := reflect.ValueOf(value)
switch value.(type) {
case int, int8, int16, int32, int64:
d = val.Int()
case uint, uint8, uint16, uint32, uint64:
d = int64(val.Uint())
default:
err = fmt.Errorf("ToInt64 need numeric not `%T`", value)
}
return
}