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

106 Commits

Author SHA1 Message Date
469f283b68 beego:fix captcha filter router 2014-06-19 20:29:36 +08:00
085c362ffb beego:fix router expge 2014-06-18 23:32:47 +08:00
c3a07555c4 beego:change version to 1.3.0 2014-06-18 15:15:45 +08:00
2b8e411174 merger master httplib 2014-06-18 15:04:08 +08:00
720a77c1f9 beego:rever docs 2014-06-18 11:15:43 +08:00
b943b74fc5 beego:init GlobalDocApi 2014-06-18 11:09:47 +08:00
67be7b532d beego:doc move to swagger 2014-06-18 10:36:20 +08:00
8be83fe488 Merge pull request #642 from JessonChan/develop
ignore nil time
2014-06-17 15:25:03 +08:00
cff632f553 beego: swagger EnableDocs 2014-06-16 16:05:19 +08:00
4990d88861 Merge pull request #648 from redaready/develop
update chat example
2014-06-14 12:13:50 +08:00
7075ad8a28 update chat example 2014-06-14 00:21:26 +02:00
0e278ae358 beego:format the admin print route 2014-06-13 00:14:30 +08:00
e38a23b30e beego:admin add print method 2014-06-13 00:08:43 +08:00
117904be73 beego:fix the some regexp routes to different func 2014-06-12 23:08:05 +08:00
3b807845f2 beego:addtree support regexp 2014-06-12 20:50:29 +08:00
00b710e168 beego:namespace sub router add url to pattern 2014-06-11 23:51:19 +08:00
e25fcffbc0 config:json add support array 2014-06-11 22:47:11 +08:00
c13141b8bf beego:fix when user defined function equal to HTTP 2014-06-11 22:45:54 +08:00
7a7ff735e3 Merge pull request #644 from chrisport/develop
config: fix error when json config starts with an array
2014-06-11 22:02:28 +08:00
3b934bb910 config: fix error when json config starts with an array 2014-06-11 11:33:32 +03:00
aa275fb5ce beego:fix #639 2014-06-11 13:26:45 +08:00
deb553be7f beego:confgi support difference run mode section
runmode = dev
appname = doraemon
[dev]
httpport = 8880
sessionon = true

[prod]
httpport = 8888
sessionon = true

[test]
httpport = 8080
sessionon = false
2014-06-11 12:00:50 +08:00
3db9633ebd remove websocket logic because not support handler 2014-06-11 11:12:17 +08:00
2f8a70d548 beego: router support param has _ 2014-06-11 09:33:35 +08:00
7c0d0900ac beego:fix static file router 2014-06-11 01:19:39 +08:00
6809c97611 beego: improve performance 2014-06-11 01:11:32 +08:00
675643c68d beego: run mode support test 2014-06-10 22:47:48 +08:00
06f4bf493d ignore nil time 2014-06-10 22:10:58 +08:00
4786fb0948 beego:fix typo NewControllerRegister 2014-06-10 20:12:57 +08:00
fdb5672b7a beego:delete debug information 2014-06-10 18:10:32 +08:00
107a7a21c0 beego: dev mode print request router & pattern 2014-06-10 18:09:07 +08:00
dbebf8df4b beego:namespace support nest
ns := NewNamespace("/v3",
		NSAutoRouter(&TestController{}),
		NSNamespace("/shop",
			NSGet("/order/:id", func(ctx *context.Context) {
				ctx.Output.Body([]byte(ctx.Input.Param(":id")))
			}),
		),
	)
2014-06-10 17:11:02 +08:00
f7b01aab13 beego: modify the filter sequence 2014-06-10 11:02:41 +08:00
2570f075d9 beego:change ControllerComments exported 2014-06-09 17:46:13 +08:00
21cb8ea4a3 beego:AST code 2014-06-09 17:33:04 +08:00
6c8a7f1382 beego: router change to method Tree 2014-06-09 10:11:37 +08:00
e00eab7f49 beego: change to tree 2014-06-08 20:24:07 +08:00
bfabcfcb6b beego:router tree 2014-06-08 20:24:07 +08:00
f06ba52ede Merge pull request #633 from dlt/develop
fixed typo on constant applicationXml
2014-06-07 01:10:42 +08:00
fcae000a79 fixed typo on constant applicationXml 2014-06-06 13:56:34 -03:00
3e4c015982 Merge pull request #631 from curvesoft/master
cookiejar support
2014-06-04 23:02:52 +08:00
d689be30e8 remove httplib_test.php 2014-06-04 22:12:37 +08:00
7b110a0b73 remove httplib_test.php 2014-06-04 22:09:43 +08:00
e3033b57a6 1.gofmt httplib.go httplib_test.go
2.replace test url to http://httpbin.org functions
2014-06-04 21:15:24 +08:00
bd537554ea 1.gofmt httplib.go httplib_test.go
2.replace test url to http://httpbin.org functions
2014-06-04 21:04:50 +08:00
ebb3b91df9 1、增加cookiejar支持
2、增加Setting结构,便于统一设置请求参数
3、增加服务端测试php脚本
2014-06-03 21:20:10 +08:00
a65ad1a4bc fix the time test case 2014-05-31 14:28:44 +08:00
bdc01f52a0 Merge pull request #626 from mvpmvh/michael
Michael
2014-05-31 14:25:40 +08:00
a673a85d4a added tests config/json_test that test missing key usecases. created a template function to fetch AppConfig values 2014-05-30 23:48:23 -05:00
61008fe75c udpated timezone in templatefunc_test. changed error message to be more descriptive when tests fail 2014-05-30 14:12:21 -05:00
5dee6b7d19 beego: fix the namespace cond 2014-05-28 10:23:31 +08:00
f6c7a6bd32 beego: improve the admin router print 2014-05-27 17:27:22 +08:00
d2eece9a39 session: #620 make the session never read empty 2014-05-27 15:45:35 +08:00
c3a23b28ee beego: improve the RandomCreateBytes #620
when rand.Read is failed. will use the math/rand to generate the rand
bytes
2014-05-27 15:29:43 +08:00
9083927c6a beego: enhance the XSRFKEY from 15 to 32 #620 2014-05-27 15:00:10 +08:00
3f7e91e6a4 beego:fix *.* router bug 2014-05-26 10:15:56 +08:00
a2a6f47afa beego: support other config provider 2014-05-25 22:37:38 +08:00
23229ef9ef beego: BeegoServerName & beego.Run
BeegoServerName change to beegoServer+Version
beego.Run(“:8089”)
2014-05-25 22:35:20 +08:00
0d17d974cd beego: update namespace 2014-05-23 15:56:25 +08:00
17104c25a2 beego: Refactoring Filter & add comments 2014-05-20 18:47:41 +08:00
8b374d7f90 beego: add benchmark 2014-05-20 18:20:44 +08:00
fa3234147a httplib:drone can't upload file 2014-05-20 17:34:52 +08:00
33ad6c1370 beego: remove app funciont & fix #590
config := tls.Config{
    ClientAuth: tls.RequireAndVerifyClientCert,
    Certificates: []tls.Certificate{cert},
    ClientCAs: pool,
}
config.Rand = rand.Reader

beego.BeeApp.Server. TLSConfig = &config
2014-05-20 17:28:06 +08:00
04290dfc68 beego: delete hotupdate 2014-05-20 16:41:39 +08:00
03080b3ef2 beego:1.2.0 2014-05-20 15:53:41 +08:00
3f2a712ba8 beego:change default port 2014-05-20 15:40:05 +08:00
f215aa4810 beego: change the error tips 2014-05-20 15:34:27 +08:00
18a02d7d60 beego:support https & http listen 2014-05-20 15:30:17 +08:00
3f4d750dc4 utils: improve the file grep 2014-05-20 14:32:06 +08:00
9f01aeed31 beego:remove unused code 2014-05-19 18:52:48 +08:00
b45f0b9bf6 beego: fix #478 2014-05-17 02:56:50 +08:00
cf04ade603 merger master 2014-05-17 02:29:41 +08:00
2c59ff1cc6 beego: admin support link 2014-05-17 02:20:48 +08:00
6bdf0838ce beego: controller add ServeFormatted
ServeFormatted serve Xml OR Json, depending on the value of the Accept
header
2014-05-17 01:26:59 +08:00
31a63c5d50 session: support memcache interface 2014-05-17 01:19:47 +08:00
237aaadd65 session:support struct.
gob.Register(v)
2014-05-17 00:43:51 +08:00
34ddcef1dc beego: XSRF support Controller level fix #610
default value is true when you Enable Global XSRF, also can control in
the prepare function to change the value.
2014-05-17 00:12:25 +08:00
f6ce2656db beego: support namespace
ns := beego.NewNamespace("/v1/api/")
ns.Cond(func(ctx *context.Context)bool{
	    if ctx.Input.Domain() == "www.beego.me" {
	    	return true
	    }
	    return false
	})
.Filter("before", Authenticate)
.Router("/order",	&admin.OrderController{})
.Get("/version",func (ctx *context.Context) {
	ctx.Output.Body([]byte("1.0.0"))
})
.Post("/login",func (ctx *context.Context) {
	if ctx.Query("username") == "admin" && ctx.Query("username") ==
"password" {

	}
})
.Namespace(
	NewNamespace("/shop").
		Get("/order/:id", func(ctx *context.Context) {
		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
	}),
)
2014-05-16 23:47:29 +08:00
b647026dff orm: add test for unexported struct field 2014-05-16 13:14:15 +08:00
568c0c47f0 Merge pull request #542 from kylemcc/develop
orm: allow unexported fields on model structs
2014-05-16 13:11:55 +08:00
2629de28f2 beego: support more router
//design model
	beego.Get(router, beego.FilterFunc)
	beego.Post(router, beego.FilterFunc)
	beego.Put(router, beego.FilterFunc)
	beego.Head(router, beego.FilterFunc)
	beego.Options(router, beego.FilterFunc)
	beego.Delete(router, beego.FilterFunc)
	beego.Handler(router, http.Handler)

//example

beego.Get("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("Get userlist"))
})

beego.Post("/user", func(ctx *context.Context) {
	ctx.Output.Body([]byte("add userlist"))
})

beego.Delete("/user/:id", func(ctx *context.Context) {
	ctx.Output.Body([]byte([]byte(ctx.Input.Param(":id")))
})

import (
    "http"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
)

func init() {
    s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterService(new(HelloService), "")
    beego.Handler("/rpc", s)
}
2014-05-16 10:18:19 +08:00
10d2c7c328 config: fix the import issue 2014-05-16 10:18:19 +08:00
af7ac98bd6 Merge pull request #609 from JessonChan/develop
[important] bug fixed
2014-05-15 11:47:07 +08:00
6f78f1d4b2 bug fixed 2014-05-15 11:34:44 +08:00
9f95fd3309 Merge pull request #608 from JessonChan/develop
refactor func
2014-05-14 20:47:01 +08:00
74c309cefd refator func 2014-05-14 20:08:51 +08:00
29e113a48a Merge pull request #597 from tobyzxj/develop
httplib support to set the protocol version for incoming requests
2014-05-09 16:25:33 +08:00
3caf1896d6 httplib support to set the protocol version for incoming requests 2014-05-09 15:48:50 +08:00
d5d5f23756 httplib:support file upload 2014-05-08 16:58:08 +08:00
46641ef3b6 fix the typo 2014-05-08 10:51:29 +08:00
b2a69f505c beego: support other analisys & fix typo 2014-04-28 18:07:30 +08:00
b2bd829d39 beego: add link in the admin console 2014-04-15 05:03:20 +08:00
f9b8617fa3 context: fix multipart/form-data 2014-04-15 05:02:50 +08:00
6c6e4ecfbc update all files License 2014-04-12 13:18:18 +08:00
8bcf03c652 fix #576 2014-04-11 16:08:43 +08:00
1ea449aa3a beego:add post test case 2014-04-10 22:33:32 +08:00
b212ec8dab beego:query data from Form & params 2014-04-10 22:20:46 +08:00
e50cbecf80 beego: fix flash errors 2014-04-10 18:14:18 +08:00
127b85bcaa context:add Bind function
// Bind data from request.Form[key] to dest
// like
/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=
astaxie
// var id int  beegoInput.Bind(&id, "id")  id ==123
// var isok bool  beegoInput.Bind(&isok, "isok")  id ==true
// var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
// ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
// ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str
array]
// user struct{Name}  beegoInput.Bind(&user, "user")  user ==
{Name:"astaxie"}
2014-04-10 00:31:16 +08:00
89dde6cd9d Merge branch 'develop' of https://github.com/astaxie/beego into develop 2014-04-09 21:43:32 +08:00
5a52949761 beego: support not-empty value in router fix #555 2014-04-09 21:42:57 +08:00
a78162e9e4 Merge pull request #573 from linluxiang/master
Make the Maxage Config of cookie session work
2014-04-09 14:42:29 +08:00
931e6162ac Merge pull request #574 from dz1984/fix/cache_test
Fix/cache test
2014-04-09 14:42:01 +08:00
1bb876f2df make Maxage work 2014-04-09 13:18:44 +08:00
18f70e6ee4 update the error message 2014-04-09 12:39:12 +08:00
4785ac14d7 allow unexported fields on model structs 2014-03-18 18:00:07 -05:00
35 changed files with 2562 additions and 1396 deletions

View File

@ -88,7 +88,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintln(rw, "StaticExtensionsToGzip:", StaticExtensionsToGzip) fmt.Fprintln(rw, "StaticExtensionsToGzip:", StaticExtensionsToGzip)
fmt.Fprintln(rw, "HttpAddr:", HttpAddr) fmt.Fprintln(rw, "HttpAddr:", HttpAddr)
fmt.Fprintln(rw, "HttpPort:", HttpPort) fmt.Fprintln(rw, "HttpPort:", HttpPort)
fmt.Fprintln(rw, "HttpTLS:", HttpTLS) fmt.Fprintln(rw, "HttpTLS:", EnableHttpTLS)
fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile) fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile)
fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile) fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile)
fmt.Fprintln(rw, "RecoverPanic:", RecoverPanic) fmt.Fprintln(rw, "RecoverPanic:", RecoverPanic)
@ -107,7 +107,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintln(rw, "MaxMemory:", MaxMemory) fmt.Fprintln(rw, "MaxMemory:", MaxMemory)
fmt.Fprintln(rw, "EnableGzip:", EnableGzip) fmt.Fprintln(rw, "EnableGzip:", EnableGzip)
fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex) fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex)
fmt.Fprintln(rw, "EnableHotUpdate:", EnableHotUpdate)
fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut) fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut)
fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow) fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow)
fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY) fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY)
@ -122,28 +121,13 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort) fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort)
case "router": case "router":
fmt.Fprintln(rw, "Print all router infomation:") fmt.Fprintln(rw, "Print all router infomation:")
for _, router := range BeeApp.Handlers.fixrouters { for method, t := range BeeApp.Handlers.routers {
if router.hasMethod { fmt.Fprintln(rw)
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name()) fmt.Fprintln(rw)
} else { fmt.Fprintln(rw, " Method:", method)
fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name()) printTree(rw, t)
}
}
for _, router := range BeeApp.Handlers.routers {
if router.hasMethod {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
} else {
fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
}
}
if BeeApp.Handlers.enableAuto {
for controllerName, methodObj := range BeeApp.Handlers.autoRouter {
fmt.Fprintln(rw, controllerName, "----")
for methodName, obj := range methodObj {
fmt.Fprintln(rw, " ", methodName, "-----", obj.Name())
}
}
} }
// @todo print routers
case "filter": case "filter":
fmt.Fprintln(rw, "Print all filter infomation:") fmt.Fprintln(rw, "Print all filter infomation:")
if BeeApp.Handlers.enableFilter { if BeeApp.Handlers.enableFilter {
@ -153,12 +137,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc)) fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
} }
} }
fmt.Fprintln(rw, "AfterStatic:")
if bf, ok := BeeApp.Handlers.filters[AfterStatic]; ok {
for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
}
}
fmt.Fprintln(rw, "BeforeExec:") fmt.Fprintln(rw, "BeforeExec:")
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
for _, f := range bf { for _, f := range bf {
@ -191,6 +169,26 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
} }
} }
func printTree(rw http.ResponseWriter, t *Tree) {
for _, tr := range t.fixrouters {
printTree(rw, tr)
}
if t.wildcard != nil {
printTree(rw, t.wildcard)
}
for _, l := range t.leaves {
if v, ok := l.runObject.(*controllerInfo); ok {
if v.routerType == routerTypeBeego {
fmt.Fprintln(rw, v.pattern, v.methods, v.controllerType.Name())
} else if v.routerType == routerTypeRESTFul {
fmt.Fprintln(rw, v.pattern, v.methods)
} else if v.routerType == routerTypeHandler {
fmt.Fprintln(rw, v.pattern, "handler")
}
}
}
}
// ProfIndex is a http.Handler for showing profile command. // ProfIndex is a http.Handler for showing profile command.
// it's in url pattern "/prof" in admin module. // it's in url pattern "/prof" in admin module.
func profIndex(rw http.ResponseWriter, r *http.Request) { func profIndex(rw http.ResponseWriter, r *http.Request) {
@ -219,9 +217,9 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
func healthcheck(rw http.ResponseWriter, req *http.Request) { func healthcheck(rw http.ResponseWriter, req *http.Request) {
for name, h := range toolbox.AdminCheckList { for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil { if err := h.Check(); err != nil {
fmt.Fprintf(rw, "%s : ok\n", name)
} else {
fmt.Fprintf(rw, "%s : %s\n", name, err.Error()) fmt.Fprintf(rw, "%s : %s\n", name, err.Error())
} else {
fmt.Fprintf(rw, "%s : ok\n", name)
} }
} }
} }

206
app.go
View File

@ -22,12 +22,13 @@ type FilterFunc func(*context.Context)
// App defines beego application with a new PatternServeMux. // App defines beego application with a new PatternServeMux.
type App struct { type App struct {
Handlers *ControllerRegistor Handlers *ControllerRegistor
Server *http.Server
} }
// NewApp returns a new beego application. // NewApp returns a new beego application.
func NewApp() *App { func NewApp() *App {
cr := NewControllerRegistor() cr := NewControllerRegister()
app := &App{Handlers: cr} app := &App{Handlers: cr, Server: &http.Server{}}
return app return app
} }
@ -45,6 +46,7 @@ func (app *App) Run() {
err error err error
l net.Listener l net.Listener
) )
endRunning := make(chan bool, 1)
if UseFcgi { if UseFcgi {
if HttpPort == 0 { if HttpPort == 0 {
@ -57,180 +59,36 @@ func (app *App) Run() {
} }
err = fcgi.Serve(l, app.Handlers) err = fcgi.Serve(l, app.Handlers)
} else { } else {
if EnableHotUpdate { app.Server.Addr = addr
server := &http.Server{ app.Server.Handler = app.Handlers
Handler: app.Handlers, app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second, app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
} if EnableHttpTLS {
laddr, err := net.ResolveTCPAddr("tcp", addr) go func() {
if nil != err { if HttpsPort != 0 {
BeeLogger.Critical("ResolveTCPAddr:", err) app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
}
l, err = GetInitListener(laddr)
if err == nil {
theStoppable = newStoppable(l)
err = server.Serve(theStoppable)
if err == nil {
theStoppable.wg.Wait()
err = CloseSelf()
} }
} err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
} else { if err != nil {
s := &http.Server{ BeeLogger.Critical("ListenAndServeTLS: ", err)
Addr: addr, time.Sleep(100 * time.Microsecond)
Handler: app.Handlers, endRunning <- true
ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second, }
WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second, }()
} }
if HttpTLS {
err = s.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if EnableHttpListen {
} else { go func() {
err = s.ListenAndServe() err := app.Server.ListenAndServe()
} if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
} }
} }
if err != nil { <-endRunning
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
}
}
// Router adds a url-patterned controller handler.
// The path argument supports regex rules and specific placeholders.
// The c argument needs a controller handler implemented beego.ControllerInterface.
// The mapping methods argument only need one string to define custom router rules.
// usage:
// simple router
// beego.Router("/admin", &admin.UserController{})
// beego.Router("/admin/index", &admin.ArticleController{})
//
// regex router
//
// beego.Router(“/api/:id([0-9]+)“, &controllers.RController{})
//
// custom rules
// beego.Router("/api/list",&RestController{},"*:ListFood")
// beego.Router("/api/create",&RestController{},"post:CreateFood")
// beego.Router("/api/update",&RestController{},"put:UpdateFood")
// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
func (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
app.Handlers.Add(path, c, mappingMethods...)
return app
}
// AutoRouter adds beego-defined controller handler.
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
// visit the url /main/list to exec List function or /main/page to exec Page function.
func (app *App) AutoRouter(c ControllerInterface) *App {
app.Handlers.AddAuto(c)
return app
}
// AutoRouterWithPrefix adds beego-defined controller handler with prefix.
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
func (app *App) AutoRouterWithPrefix(prefix string, c ControllerInterface) *App {
app.Handlers.AddAutoPrefix(prefix, c)
return app
}
// add router for Get method
func (app *App) Get(rootpath string, f FilterFunc) *App {
app.Handlers.Get(rootpath, f)
return app
}
// add router for Post method
func (app *App) Post(rootpath string, f FilterFunc) *App {
app.Handlers.Post(rootpath, f)
return app
}
// add router for Put method
func (app *App) Put(rootpath string, f FilterFunc) *App {
app.Handlers.Put(rootpath, f)
return app
}
// add router for Delete method
func (app *App) Delete(rootpath string, f FilterFunc) *App {
app.Handlers.Delete(rootpath, f)
return app
}
// add router for Options method
func (app *App) Options(rootpath string, f FilterFunc) *App {
app.Handlers.Options(rootpath, f)
return app
}
// add router for Head method
func (app *App) Head(rootpath string, f FilterFunc) *App {
app.Handlers.Head(rootpath, f)
return app
}
// add router for Patch method
func (app *App) Patch(rootpath string, f FilterFunc) *App {
app.Handlers.Patch(rootpath, f)
return app
}
// add router for Patch method
func (app *App) Any(rootpath string, f FilterFunc) *App {
app.Handlers.Any(rootpath, f)
return app
}
// add router for http.Handler
func (app *App) Handler(rootpath string, h http.Handler, options ...interface{}) *App {
app.Handlers.Handler(rootpath, h, options...)
return app
}
// UrlFor creates a url with another registered controller handler with params.
// The endpoint is formed as path.controller.name to defined the controller method which will run.
// The values need key-pair data to assign into controller method.
func (app *App) UrlFor(endpoint string, values ...string) string {
return app.Handlers.UrlFor(endpoint, values...)
}
// [Deprecated] use InsertFilter.
// Filter adds a FilterFunc under pattern condition and named action.
// The actions contains BeforeRouter,AfterStatic,BeforeExec,AfterExec and FinishRouter.
func (app *App) Filter(pattern, action string, filter FilterFunc) *App {
app.Handlers.AddFilter(pattern, action, filter)
return app
}
// 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 (app *App) InsertFilter(pattern string, pos int, filter FilterFunc) *App {
app.Handlers.InsertFilter(pattern, pos, filter)
return app
}
// SetViewsPath sets view directory path in beego application.
// it returns beego application self.
func (app *App) SetViewsPath(path string) *App {
ViewsPath = path
return app
}
// SetStaticPath sets static directory path and proper url pattern in beego application.
// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public".
// it returns beego application self.
func (app *App) SetStaticPath(url string, path string) *App {
StaticDir[url] = path
return app
}
// DelStaticPath removes the static folder setting in this url pattern in beego application.
// it returns beego application self.
func (app *App) DelStaticPath(url string) *App {
delete(StaticDir, url)
return app
} }

166
beego.go
View File

@ -19,7 +19,7 @@ import (
) )
// beego web framework version. // beego web framework version.
const VERSION = "1.2.0" const VERSION = "1.3.0"
type hookfunc func() error //hook function to run type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc var hooks []hookfunc //hook function slice to store the hookfunc
@ -80,11 +80,11 @@ func (gr *GroupRouters) AddAuto(c ControllerInterface) {
func AddGroupRouter(prefix string, groups GroupRouters) *App { func AddGroupRouter(prefix string, groups GroupRouters) *App {
for _, v := range groups { for _, v := range groups {
if v.pattern == "" { if v.pattern == "" {
BeeApp.AutoRouterWithPrefix(prefix, v.controller) BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
} else if v.mappingMethods != "" { } else if v.mappingMethods != "" {
BeeApp.Router(prefix+v.pattern, v.controller, v.mappingMethods) BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
} else { } else {
BeeApp.Router(prefix+v.pattern, v.controller) BeeApp.Handlers.Add(prefix+v.pattern, v.controller)
} }
} }
@ -93,8 +93,54 @@ func AddGroupRouter(prefix string, groups GroupRouters) *App {
// Router adds a patterned controller handler to BeeApp. // Router adds a patterned controller handler to BeeApp.
// it's an alias method of App.Router. // it's an alias method of App.Router.
// usage:
// simple router
// beego.Router("/admin", &admin.UserController{})
// beego.Router("/admin/index", &admin.ArticleController{})
//
// regex router
//
// beego.Router("/api/:id([0-9]+)", &controllers.RController{})
//
// custom rules
// beego.Router("/api/list",&RestController{},"*:ListFood")
// beego.Router("/api/create",&RestController{},"post:CreateFood")
// beego.Router("/api/update",&RestController{},"put:UpdateFood")
// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
BeeApp.Router(rootpath, c, mappingMethods...) BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
return BeeApp
}
// Router add list from
// usage:
// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
// type BankAccount struct{
// beego.Controller
// }
//
// register the function
// func (b *BankAccount)Mapping(){
// b.Mapping("ShowAccount" , b.ShowAccount)
// b.Mapping("ModifyAccount", b.ModifyAccount)
//}
//
// //@router /account/:id [get]
// func (b *BankAccount) ShowAccount(){
// //logic
// }
//
//
// //@router /account/:id [post]
// func (b *BankAccount) ModifyAccount(){
// //logic
// }
//
// the comments @router url methodlist
// url support all the function Router's pattern
// methodlist [get post head put delete options *]
func Include(cList ...ControllerInterface) *App {
BeeApp.Handlers.Include(cList...)
return BeeApp return BeeApp
} }
@ -109,69 +155,109 @@ func RESTRouter(rootpath string, c ControllerInterface) *App {
// AutoRouter adds defined controller handler to BeeApp. // AutoRouter adds defined controller handler to BeeApp.
// it's same to App.AutoRouter. // it's same to App.AutoRouter.
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
// visit the url /main/list to exec List function or /main/page to exec Page function.
func AutoRouter(c ControllerInterface) *App { func AutoRouter(c ControllerInterface) *App {
BeeApp.AutoRouter(c) BeeApp.Handlers.AddAuto(c)
return BeeApp return BeeApp
} }
// AutoPrefix adds controller handler to BeeApp with prefix. // AutoPrefix adds controller handler to BeeApp with prefix.
// it's same to App.AutoRouterWithPrefix. // it's same to App.AutoRouterWithPrefix.
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
func AutoPrefix(prefix string, c ControllerInterface) *App { func AutoPrefix(prefix string, c ControllerInterface) *App {
BeeApp.AutoRouterWithPrefix(prefix, c) BeeApp.Handlers.AddAutoPrefix(prefix, c)
return BeeApp return BeeApp
} }
// register router for Get method // register router for Get method
// usage:
// beego.Get("/", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Get(rootpath string, f FilterFunc) *App { func Get(rootpath string, f FilterFunc) *App {
BeeApp.Get(rootpath, f) BeeApp.Handlers.Get(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Post method // register router for Post method
// usage:
// beego.Post("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Post(rootpath string, f FilterFunc) *App { func Post(rootpath string, f FilterFunc) *App {
BeeApp.Post(rootpath, f) BeeApp.Handlers.Post(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Delete method // register router for Delete method
// usage:
// beego.Delete("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Delete(rootpath string, f FilterFunc) *App { func Delete(rootpath string, f FilterFunc) *App {
BeeApp.Delete(rootpath, f) BeeApp.Handlers.Delete(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Put method // register router for Put method
// usage:
// beego.Put("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Put(rootpath string, f FilterFunc) *App { func Put(rootpath string, f FilterFunc) *App {
BeeApp.Put(rootpath, f) BeeApp.Handlers.Put(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Head method // register router for Head method
// usage:
// beego.Head("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Head(rootpath string, f FilterFunc) *App { func Head(rootpath string, f FilterFunc) *App {
BeeApp.Head(rootpath, f) BeeApp.Handlers.Head(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Options method // register router for Options method
// usage:
// beego.Options("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Options(rootpath string, f FilterFunc) *App { func Options(rootpath string, f FilterFunc) *App {
BeeApp.Options(rootpath, f) BeeApp.Handlers.Options(rootpath, f)
return BeeApp return BeeApp
} }
// register router for Patch method // register router for Patch method
// usage:
// beego.Patch("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Patch(rootpath string, f FilterFunc) *App { func Patch(rootpath string, f FilterFunc) *App {
BeeApp.Patch(rootpath, f) BeeApp.Handlers.Patch(rootpath, f)
return BeeApp return BeeApp
} }
// register router for all method // register router for all method
// usage:
// beego.Any("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Any(rootpath string, f FilterFunc) *App { func Any(rootpath string, f FilterFunc) *App {
BeeApp.Any(rootpath, f) BeeApp.Handlers.Any(rootpath, f)
return BeeApp return BeeApp
} }
// register router for own Handler // register router for own Handler
// usage:
// beego.Handler("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Handler(rootpath string, h http.Handler, options ...interface{}) *App { func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
BeeApp.Handler(rootpath, h, options...) BeeApp.Handlers.Handler(rootpath, h, options...)
return BeeApp return BeeApp
} }
@ -184,15 +270,14 @@ func Errorhandler(err string, h http.HandlerFunc) *App {
return BeeApp return BeeApp
} }
// SetViewsPath sets view directory to BeeApp. // SetViewsPath sets view directory path in beego application.
// it's alias of App.SetViewsPath.
func SetViewsPath(path string) *App { func SetViewsPath(path string) *App {
BeeApp.SetViewsPath(path) ViewsPath = path
return BeeApp return BeeApp
} }
// SetStaticPath sets static directory and url prefix to BeeApp. // SetStaticPath sets static directory path and proper url pattern in beego application.
// it's alias of App.SetStaticPath. // if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public".
func SetStaticPath(url string, path string) *App { func SetStaticPath(url string, path string) *App {
if !strings.HasPrefix(url, "/") { if !strings.HasPrefix(url, "/") {
url = "/" + url url = "/" + url
@ -203,27 +288,16 @@ func SetStaticPath(url string, path string) *App {
} }
// DelStaticPath removes the static folder setting in this url pattern in beego application. // DelStaticPath removes the static folder setting in this url pattern in beego application.
// it's alias of App.DelStaticPath.
func DelStaticPath(url string) *App { func DelStaticPath(url string) *App {
delete(StaticDir, url) delete(StaticDir, url)
return BeeApp return BeeApp
} }
// [Deprecated] use InsertFilter.
// Filter adds a FilterFunc under pattern condition and named action.
// The actions contains BeforeRouter,AfterStatic,BeforeExec,AfterExec and FinishRouter.
// it's alias of App.Filter.
func AddFilter(pattern, action string, filter FilterFunc) *App {
BeeApp.Filter(pattern, action, filter)
return BeeApp
}
// InsertFilter adds a FilterFunc with pattern condition and action constant. // InsertFilter adds a FilterFunc with pattern condition and action constant.
// The pos means action constant including // The pos means action constant including
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. // beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
// it's alias of App.InsertFilter.
func InsertFilter(pattern string, pos int, filter FilterFunc) *App { func InsertFilter(pattern string, pos int, filter FilterFunc) *App {
BeeApp.InsertFilter(pattern, pos, filter) BeeApp.Handlers.InsertFilter(pattern, pos, filter)
return BeeApp return BeeApp
} }
@ -234,8 +308,19 @@ func AddAPPStartHook(hf hookfunc) {
} }
// Run beego application. // Run beego application.
// it's alias of App.Run. // beego.Run() default run on HttpPort
func Run() { // beego.Run(":8089")
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {
if len(params) > 0 && params[0] != "" {
strs := strings.Split(params[0], ":")
if len(strs) > 0 && strs[0] != "" {
HttpAddr = strs[0]
}
if len(strs) > 1 && strs[1] != "" {
HttpPort, _ = strconv.Atoi(strs[1])
}
}
initBeforeHttpRun() initBeforeHttpRun()
if EnableAdmin { if EnableAdmin {
@ -270,7 +355,7 @@ func initBeforeHttpRun() {
sessionConfig = `{"cookieName":"` + SessionName + `",` + sessionConfig = `{"cookieName":"` + SessionName + `",` +
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + SessionSavePath + `",` + `"providerConfig":"` + SessionSavePath + `",` +
`"secure":` + strconv.FormatBool(HttpTLS) + `,` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` + `"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` + `"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
@ -294,10 +379,19 @@ func initBeforeHttpRun() {
middleware.VERSION = VERSION middleware.VERSION = VERSION
middleware.AppName = AppName middleware.AppName = AppName
middleware.RegisterErrorHandler() middleware.RegisterErrorHandler()
for u, _ := range StaticDir {
Get(u+"/*", serverStaticRouter)
}
if EnableDocs {
Get("/docs/*", serverDocs)
}
} }
// this function is for test package init
func TestBeegoInit(apppath string) { func TestBeegoInit(apppath string) {
AppPath = apppath AppPath = apppath
RunMode = "test"
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
err := ParseConfig() err := ParseConfig()
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {

227
config.go
View File

@ -7,12 +7,12 @@
package beego package beego
import ( import (
"errors"
"fmt" "fmt"
"html/template" "html/template"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"github.com/astaxie/beego/config" "github.com/astaxie/beego/config"
@ -30,9 +30,11 @@ var (
StaticDir map[string]string StaticDir map[string]string
TemplateCache map[string]*template.Template // template caching map TemplateCache map[string]*template.Template // template caching map
StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc) StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc)
EnableHttpListen bool
HttpAddr string HttpAddr string
HttpPort int HttpPort int
HttpTLS bool EnableHttpTLS bool
HttpsPort int
HttpCertFile string HttpCertFile string
HttpKeyFile string HttpKeyFile string
RecoverPanic bool // flag of auto recover panic RecoverPanic bool // flag of auto recover panic
@ -54,7 +56,6 @@ var (
MaxMemory int64 MaxMemory int64
EnableGzip bool // flag of enable gzip EnableGzip bool // flag of enable gzip
DirectoryIndex bool // flag of display directory index. default is false. DirectoryIndex bool // flag of display directory index. default is false.
EnableHotUpdate bool // flag of hot update checking by app self. default is false.
HttpServerTimeOut int64 HttpServerTimeOut int64
ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template. ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template.
XSRFKEY string // xsrf hash salt string. XSRFKEY string // xsrf hash salt string.
@ -69,6 +70,8 @@ var (
AdminHttpPort int AdminHttpPort int
FlashName string // name of the flash variable found in response header and cookie FlashName string // name of the flash variable found in response header and cookie
FlashSeperator string // used to seperate flash key:value FlashSeperator string // used to seperate flash key:value
AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger
) )
func init() { func init() {
@ -90,6 +93,8 @@ func init() {
} }
} }
AppConfigProvider = "ini"
StaticDir = make(map[string]string) StaticDir = make(map[string]string)
StaticDir["/static"] = "static" StaticDir["/static"] = "static"
@ -98,9 +103,13 @@ func init() {
TemplateCache = make(map[string]*template.Template) TemplateCache = make(map[string]*template.Template)
// set this to 0.0.0.0 to make this app available to externally // set this to 0.0.0.0 to make this app available to externally
EnableHttpListen = true //default enable http Listen
HttpAddr = "" HttpAddr = ""
HttpPort = 8080 HttpPort = 8080
HttpsPort = 10443
AppName = "beego" AppName = "beego"
RunMode = "dev" //default runmod RunMode = "dev" //default runmod
@ -137,7 +146,7 @@ func init() {
TemplateLeft = "{{" TemplateLeft = "{{"
TemplateRight = "}}" TemplateRight = "}}"
BeegoServerName = "beegoServer" BeegoServerName = "beegoServer:" + VERSION
EnableAdmin = false EnableAdmin = false
AdminHttpAddr = "127.0.0.1" AdminHttpAddr = "127.0.0.1"
@ -165,151 +174,157 @@ func init() {
// ParseConfig parsed default config file. // ParseConfig parsed default config file.
// now only support ini, next will support json. // now only support ini, next will support json.
func ParseConfig() (err error) { func ParseConfig() (err error) {
AppConfig, err = config.NewConfig("ini", AppConfigPath) AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath)
if err != nil { if err != nil {
AppConfig = config.NewFakeConfig() AppConfig = config.NewFakeConfig()
return err return err
} else { } else {
HttpAddr = AppConfig.String("HttpAddr")
if v, err := AppConfig.Int("HttpPort"); err == nil { if v, err := getConfig("string", "HttpAddr"); err == nil {
HttpPort = v HttpAddr = v.(string)
} }
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil { if v, err := getConfig("int", "HttpPort"); err == nil {
MaxMemory = maxmemory HttpPort = v.(int)
} }
if appname := AppConfig.String("AppName"); appname != "" { if v, err := getConfig("bool", "EnableHttpListen"); err == nil {
AppName = appname EnableHttpListen = v.(bool)
} }
if runmode := AppConfig.String("RunMode"); runmode != "" { if maxmemory, err := getConfig("int64", "MaxMemory"); err == nil {
RunMode = runmode MaxMemory = maxmemory.(int64)
} }
if autorender, err := AppConfig.Bool("AutoRender"); err == nil { if appname, _ := getConfig("string", "AppName"); appname != "" {
AutoRender = autorender AppName = appname.(string)
} }
if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil { if runmode, _ := getConfig("string", "RunMode"); runmode != "" {
RecoverPanic = autorecover RunMode = runmode.(string)
} }
if views := AppConfig.String("ViewsPath"); views != "" { if autorender, err := getConfig("bool", "AutoRender"); err == nil {
ViewsPath = views AutoRender = autorender.(bool)
} }
if sessionon, err := AppConfig.Bool("SessionOn"); err == nil { if autorecover, err := getConfig("bool", "RecoverPanic"); err == nil {
SessionOn = sessionon RecoverPanic = autorecover.(bool)
} }
if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" { if views, _ := getConfig("string", "ViewsPath"); views != "" {
SessionProvider = sessProvider ViewsPath = views.(string)
} }
if sessName := AppConfig.String("SessionName"); sessName != "" { if sessionon, err := getConfig("bool", "SessionOn"); err == nil {
SessionName = sessName SessionOn = sessionon.(bool)
} }
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" { if sessProvider, _ := getConfig("string", "SessionProvider"); sessProvider != "" {
SessionSavePath = sesssavepath SessionProvider = sessProvider.(string)
} }
if sesshashfunc := AppConfig.String("SessionHashFunc"); sesshashfunc != "" { if sessName, _ := getConfig("string", "SessionName"); sessName != "" {
SessionHashFunc = sesshashfunc SessionName = sessName.(string)
} }
if sesshashkey := AppConfig.String("SessionHashKey"); sesshashkey != "" { if sesssavepath, _ := getConfig("string", "SessionSavePath"); sesssavepath != "" {
SessionHashKey = sesshashkey SessionSavePath = sesssavepath.(string)
} }
if sessMaxLifeTime, err := AppConfig.Int("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { if sesshashfunc, _ := getConfig("string", "SessionHashFunc"); sesshashfunc != "" {
int64val, _ := strconv.ParseInt(strconv.Itoa(sessMaxLifeTime), 10, 64) SessionHashFunc = sesshashfunc.(string)
SessionGCMaxLifetime = int64val
} }
if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 { if sesshashkey, _ := getConfig("string", "SessionHashKey"); sesshashkey != "" {
SessionCookieLifeTime = sesscookielifetime SessionHashKey = sesshashkey.(string)
} }
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil { if sessMaxLifeTime, err := getConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
UseFcgi = usefcgi SessionGCMaxLifetime = sessMaxLifeTime.(int64)
} }
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil { if sesscookielifetime, err := getConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
EnableGzip = enablegzip SessionCookieLifeTime = sesscookielifetime.(int)
} }
if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil { if usefcgi, err := getConfig("bool", "UseFcgi"); err == nil {
DirectoryIndex = directoryindex UseFcgi = usefcgi.(bool)
} }
if hotupdate, err := AppConfig.Bool("HotUpdate"); err == nil { if enablegzip, err := getConfig("bool", "EnableGzip"); err == nil {
EnableHotUpdate = hotupdate EnableGzip = enablegzip.(bool)
} }
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil { if directoryindex, err := getConfig("bool", "DirectoryIndex"); err == nil {
HttpServerTimeOut = timeout DirectoryIndex = directoryindex.(bool)
} }
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil { if timeout, err := getConfig("int64", "HttpServerTimeOut"); err == nil {
ErrorsShow = errorsshow HttpServerTimeOut = timeout.(int64)
} }
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil { if errorsshow, err := getConfig("bool", "ErrorsShow"); err == nil {
CopyRequestBody = copyrequestbody ErrorsShow = errorsshow.(bool)
} }
if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" { if copyrequestbody, err := getConfig("bool", "CopyRequestBody"); err == nil {
XSRFKEY = xsrfkey CopyRequestBody = copyrequestbody.(bool)
} }
if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil { if xsrfkey, _ := getConfig("string", "XSRFKEY"); xsrfkey != "" {
EnableXSRF = enablexsrf XSRFKEY = xsrfkey.(string)
} }
if expire, err := AppConfig.Int("XSRFExpire"); err == nil { if enablexsrf, err := getConfig("bool", "EnableXSRF"); err == nil {
XSRFExpire = expire EnableXSRF = enablexsrf.(bool)
} }
if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" { if expire, err := getConfig("int", "XSRFExpire"); err == nil {
TemplateLeft = tplleft XSRFExpire = expire.(int)
} }
if tplright := AppConfig.String("TemplateRight"); tplright != "" { if tplleft, _ := getConfig("string", "TemplateLeft"); tplleft != "" {
TemplateRight = tplright TemplateLeft = tplleft.(string)
} }
if httptls, err := AppConfig.Bool("HttpTLS"); err == nil { if tplright, _ := getConfig("string", "TemplateRight"); tplright != "" {
HttpTLS = httptls TemplateRight = tplright.(string)
} }
if certfile := AppConfig.String("HttpCertFile"); certfile != "" { if httptls, err := getConfig("bool", "EnableHttpTLS"); err == nil {
HttpCertFile = certfile EnableHttpTLS = httptls.(bool)
} }
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" { if httpsport, err := getConfig("int", "HttpsPort"); err == nil {
HttpKeyFile = keyfile HttpsPort = httpsport.(int)
} }
if serverName := AppConfig.String("BeegoServerName"); serverName != "" { if certfile, _ := getConfig("string", "HttpCertFile"); certfile != "" {
BeegoServerName = serverName HttpCertFile = certfile.(string)
} }
if flashname := AppConfig.String("FlashName"); flashname != "" { if keyfile, _ := getConfig("string", "HttpKeyFile"); keyfile != "" {
FlashName = flashname HttpKeyFile = keyfile.(string)
} }
if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" { if serverName, _ := getConfig("string", "BeegoServerName"); serverName != "" {
FlashSeperator = flashseperator BeegoServerName = serverName.(string)
} }
if sd := AppConfig.String("StaticDir"); sd != "" { 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 { for k := range StaticDir {
delete(StaticDir, k) delete(StaticDir, k)
} }
sds := strings.Fields(sd) sds := strings.Fields(sd.(string))
for _, v := range sds { for _, v := range sds {
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1] StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
@ -319,8 +334,8 @@ func ParseConfig() (err error) {
} }
} }
if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" { if sgz, _ := getConfig("string", "StaticExtensionsToGzip"); sgz != "" {
extensions := strings.Split(sgz, ",") extensions := strings.Split(sgz.(string), ",")
if len(extensions) > 0 { if len(extensions) > 0 {
StaticExtensionsToGzip = []string{} StaticExtensionsToGzip = []string{}
for _, ext := range extensions { for _, ext := range extensions {
@ -336,17 +351,63 @@ func ParseConfig() (err error) {
} }
} }
if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil { if enableadmin, err := getConfig("bool", "EnableAdmin"); err == nil {
EnableAdmin = enableadmin EnableAdmin = enableadmin.(bool)
} }
if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" { if adminhttpaddr, _ := getConfig("string", "AdminHttpAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr AdminHttpAddr = adminhttpaddr.(string)
} }
if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil { if adminhttpport, err := getConfig("int", "AdminHttpPort"); err == nil {
AdminHttpPort = adminhttpport AdminHttpPort = adminhttpport.(int)
}
if enabledocs, err := getConfig("bool", "EnableDocs"); err == nil {
EnableDocs = enabledocs.(bool)
} }
} }
return nil return nil
} }
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

@ -35,7 +35,12 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
} }
err = json.Unmarshal(content, &x.data) err = json.Unmarshal(content, &x.data)
if err != nil { if err != nil {
return nil, err var wrappingArray []interface{}
err2 := json.Unmarshal(content, &wrappingArray)
if err2 != nil {
return nil, err
}
x.data["rootArray"] = wrappingArray
} }
return x, nil return x, nil
} }

View File

@ -33,6 +33,53 @@ var jsoncontext = `{
} }
}` }`
var jsoncontextwitharray = `[
{
"url": "user",
"serviceAPI": "http://www.test.com/user"
},
{
"url": "employee",
"serviceAPI": "http://www.test.com/employee"
}
]`
func TestJsonStartsWithArray(t *testing.T) {
f, err := os.Create("testjsonWithArray.conf")
if err != nil {
t.Fatal(err)
}
_, err = f.WriteString(jsoncontextwitharray)
if err != nil {
f.Close()
t.Fatal(err)
}
f.Close()
defer os.Remove("testjsonWithArray.conf")
jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
if err != nil {
t.Fatal(err)
}
rootArray, err := jsonconf.DIY("rootArray")
if (err != nil) {
t.Error("array does not exist as element")
}
rootArrayCasted := rootArray.([]interface{})
if (rootArrayCasted == nil) {
t.Error("array from root is nil")
}else {
elem := rootArrayCasted[0].(map[string]interface{})
if elem["url"] != "user" || elem["serviceAPI"] != "http://www.test.com/user" {
t.Error("array[0] values are not valid")
}
elem2 := rootArrayCasted[1].(map[string]interface{})
if elem2["url"] != "employee" || elem2["serviceAPI"] != "http://www.test.com/employee" {
t.Error("array[1] values are not valid")
}
}
}
func TestJson(t *testing.T) { func TestJson(t *testing.T) {
f, err := os.Create("testjson.conf") f, err := os.Create("testjson.conf")
if err != nil { if err != nil {
@ -100,4 +147,28 @@ func TestJson(t *testing.T) {
t.Fatal("get host err") t.Fatal("get host err")
} }
} }
if _, err := jsonconf.Int("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an Int")
}
if _, err := jsonconf.Int64("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an Int64")
}
if _, err := jsonconf.Float("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Float")
}
if _, err := jsonconf.DIY("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting an interface{}")
}
if val := jsonconf.String("unknown"); val != "" {
t.Error("unknown keys should return an empty string when expecting a String")
}
if _, err := jsonconf.Bool("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Bool")
}
} }

View File

@ -161,7 +161,8 @@ func (input *BeegoInput) IsUpload() bool {
func (input *BeegoInput) IP() string { func (input *BeegoInput) IP() string {
ips := input.Proxy() ips := input.Proxy()
if len(ips) > 0 && ips[0] != "" { if len(ips) > 0 && ips[0] != "" {
return ips[0] rip := strings.Split(ips[0], ":")
return rip[0]
} }
ip := strings.Split(input.Request.RemoteAddr, ":") ip := strings.Split(input.Request.RemoteAddr, ":")
if len(ip) > 0 { if len(ip) > 0 {

View File

@ -237,10 +237,14 @@ func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
// Download forces response for download file. // Download forces response for download file.
// it prepares the download response header automatically. // it prepares the download response header automatically.
func (output *BeegoOutput) Download(file string) { func (output *BeegoOutput) Download(file string, filename ...string) {
output.Header("Content-Description", "File Transfer") output.Header("Content-Description", "File Transfer")
output.Header("Content-Type", "application/octet-stream") output.Header("Content-Type", "application/octet-stream")
output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file)) if len(filename) > 0 && filename[0] != "" {
output.Header("Content-Disposition", "attachment; filename="+filename[0])
} else {
output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file))
}
output.Header("Content-Transfer-Encoding", "binary") output.Header("Content-Transfer-Encoding", "binary")
output.Header("Expires", "0") output.Header("Expires", "0")
output.Header("Cache-Control", "must-revalidate") output.Header("Cache-Control", "must-revalidate")

View File

@ -28,15 +28,24 @@ import (
//commonly used mime-types //commonly used mime-types
const ( const (
applicationJson = "application/json" applicationJson = "application/json"
applicationXml = "applicatoin/xml" applicationXml = "application/xml"
textXml = "text/xml" textXml = "text/xml"
) )
var ( var (
// custom error when user stop request handler manually. // custom error when user stop request handler manually.
USERSTOPRUN = errors.New("User stop run") USERSTOPRUN = errors.New("User stop run")
GlobalControllerRouter map[string][]ControllerComments = make(map[string][]ControllerComments) //pkgpath+controller:comments
) )
// store the comment for the controller method
type ControllerComments struct {
Method string
Router string
AllowHTTPMethods []string
Params []map[string]string
}
// Controller defines some basic http request handler operations, such as // Controller defines some basic http request handler operations, such as
// http context, template and view, session and xsrf. // http context, template and view, session and xsrf.
type Controller struct { type Controller struct {
@ -55,6 +64,7 @@ type Controller struct {
AppController interface{} AppController interface{}
EnableRender bool EnableRender bool
EnableXSRF bool EnableXSRF bool
methodMapping map[string]func() //method:routertree
} }
// ControllerInterface is an interface to uniform all controller handler. // ControllerInterface is an interface to uniform all controller handler.
@ -72,6 +82,8 @@ type ControllerInterface interface {
Render() error Render() error
XsrfToken() string XsrfToken() string
CheckXsrfCookie() bool CheckXsrfCookie() bool
HandlerFunc(fn string) bool
URLMapping()
} }
// Init generates default values of controller operations. // Init generates default values of controller operations.
@ -86,6 +98,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin
c.EnableRender = true c.EnableRender = true
c.EnableXSRF = true c.EnableXSRF = true
c.Data = ctx.Input.Data c.Data = ctx.Input.Data
c.methodMapping = make(map[string]func())
} }
// Prepare runs after Init before request function execution. // Prepare runs after Init before request function execution.
@ -133,6 +146,24 @@ func (c *Controller) Options() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
} }
// call function fn
func (c *Controller) HandlerFunc(fnname string) bool {
if v, ok := c.methodMapping[fnname]; ok {
v()
return true
} else {
return false
}
}
// URLMapping register the internal Controller router.
func (c *Controller) URLMapping() {
}
func (c *Controller) Mapping(method string, fn func()) {
c.methodMapping[method] = fn
}
// Render sends the response with rendered template bytes as text/html type. // Render sends the response with rendered template bytes as text/html type.
func (c *Controller) Render() error { func (c *Controller) Render() error {
if !c.EnableRender { if !c.EnableRender {
@ -295,7 +326,6 @@ func (c *Controller) ServeXml() {
} }
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header // ServeFormatted serve Xml OR Json, depending on the value of the Accept header
func (c *Controller) ServeFormatted() { func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept") accept := c.Ctx.Input.Header("Accept")
switch accept { switch accept {
@ -452,7 +482,7 @@ func (c *Controller) XsrfToken() string {
} else { } else {
expire = int64(XSRFExpire) expire = int64(XSRFExpire)
} }
token = string(utils.RandomCreateBytes(15)) token = string(utils.RandomCreateBytes(32))
c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire) c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
} }
c._xsrf_token = token c._xsrf_token = token

38
docs.go Normal file
View File

@ -0,0 +1,38 @@
package beego
import (
"encoding/json"
"github.com/astaxie/beego/context"
)
var GlobalDocApi map[string]interface{}
func init() {
if EnableDocs {
GlobalDocApi = make(map[string]interface{})
}
}
func serverDocs(ctx *context.Context) {
var obj interface{}
if splat := ctx.Input.Param(":splat"); splat == "" {
obj = GlobalDocApi["Root"]
} else {
if v, ok := GlobalDocApi[splat]; ok {
obj = v
}
}
if obj != nil {
bt, err := json.Marshal(obj)
if err != nil {
ctx.Output.SetStatus(504)
return
}
ctx.Output.Header("Content-Type", "application/json;charset=UTF-8")
ctx.Output.Header("Access-Control-Allow-Origin", "*")
ctx.Output.Body(bt)
return
}
ctx.Output.SetStatus(404)
}

View File

@ -60,9 +60,9 @@ func (c *connection) readPump() {
break break
} }
switch op { switch op {
case websocket.OpPong: case websocket.PongMessage:
c.ws.SetReadDeadline(time.Now().Add(readWait)) c.ws.SetReadDeadline(time.Now().Add(readWait))
case websocket.OpText: case websocket.TextMessage:
message, err := ioutil.ReadAll(r) message, err := ioutil.ReadAll(r)
if err != nil { if err != nil {
break break
@ -89,14 +89,14 @@ func (c *connection) writePump() {
select { select {
case message, ok := <-c.send: case message, ok := <-c.send:
if !ok { if !ok {
c.write(websocket.OpClose, []byte{}) c.write(websocket.CloseMessage, []byte{})
return return
} }
if err := c.write(websocket.OpText, message); err != nil { if err := c.write(websocket.TextMessage, message); err != nil {
return return
} }
case <-ticker.C: case <-ticker.C:
if err := c.write(websocket.OpPing, []byte{}); err != nil { if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return return
} }
} }
@ -149,8 +149,13 @@ type WSController struct {
beego.Controller beego.Controller
} }
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func (this *WSController) Get() { func (this *WSController) Get() {
ws, err := websocket.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request.Header, nil, 1024, 1024) ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil)
if _, ok := err.(websocket.HandshakeError); ok { if _, ok := err.(websocket.HandshakeError); ok {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return return

152
filter.go
View File

@ -6,154 +6,24 @@
package beego package beego
import (
"regexp"
"strings"
)
// FilterRouter defines filter operation before controller handler execution. // FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives. // it can match patterned url and do filter function when action arrives.
type FilterRouter struct { type FilterRouter struct {
pattern string filterFunc FilterFunc
regex *regexp.Regexp tree *Tree
filterFunc FilterFunc pattern string
hasregex bool
params map[int]string
parseParams map[string]string
} }
// ValidRouter check current request is valid for this filter. // ValidRouter check current request is valid for this filter.
// if matched, returns parsed params in this request by defined filter router pattern. // if matched, returns parsed params in this request by defined filter router pattern.
func (mr *FilterRouter) ValidRouter(router string) (bool, map[string]string) { func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
if mr.pattern == "" { isok, params := f.tree.Match(router)
return true, nil if isok == nil {
return false, nil
} }
if mr.pattern == "*" { if isok, ok := isok.(bool); ok {
return true, nil return isok, params
} else {
return false, nil
} }
if router == mr.pattern {
return true, nil
}
//pattern /admin router /admin/ match
//pattern /admin/ router /admin don't match, because url will 301 in router
if n := len(router); n > 1 && router[n-1] == '/' && router[:n-2] == mr.pattern {
return true, nil
}
if mr.hasregex {
if !mr.regex.MatchString(router) {
return false, nil
}
matches := mr.regex.FindStringSubmatch(router)
if len(matches) > 0 {
if len(matches[0]) == len(router) {
params := make(map[string]string)
for i, match := range matches[1:] {
params[mr.params[i]] = match
}
return true, params
}
}
}
return false, nil
}
func buildFilter(pattern string, filter FilterFunc) (*FilterRouter, error) {
mr := new(FilterRouter)
mr.params = make(map[int]string)
mr.filterFunc = filter
parts := strings.Split(pattern, "/")
j := 0
for i, part := range parts {
if strings.HasPrefix(part, ":") {
expr := "(.*)"
//a user may choose to override the default expression
// similar to expressjs: /user/:id([0-9]+)
if index := strings.Index(part, "("); index != -1 {
expr = part[index:]
part = part[:index]
//match /user/:id:int ([0-9]+)
//match /post/:username:string ([\w]+)
} else if lindex := strings.LastIndex(part, ":"); lindex != 0 {
switch part[lindex:] {
case ":int":
expr = "([0-9]+)"
part = part[:lindex]
case ":string":
expr = `([\w]+)`
part = part[:lindex]
}
}
mr.params[j] = part
parts[i] = expr
j++
}
if strings.HasPrefix(part, "*") {
expr := "(.*)"
if part == "*.*" {
mr.params[j] = ":path"
parts[i] = "([^.]+).([^.]+)"
j++
mr.params[j] = ":ext"
j++
} else {
mr.params[j] = ":splat"
parts[i] = expr
j++
}
}
//url like someprefix:id(xxx).html
if strings.Contains(part, ":") && strings.Contains(part, "(") && strings.Contains(part, ")") {
var out []rune
var start bool
var startexp bool
var param []rune
var expt []rune
for _, v := range part {
if start {
if v != '(' {
param = append(param, v)
continue
}
}
if startexp {
if v != ')' {
expt = append(expt, v)
continue
}
}
if v == ':' {
param = make([]rune, 0)
param = append(param, ':')
start = true
} else if v == '(' {
startexp = true
start = false
mr.params[j] = string(param)
j++
expt = make([]rune, 0)
expt = append(expt, '(')
} else if v == ')' {
startexp = false
expt = append(expt, ')')
out = append(out, expt...)
} else {
out = append(out, v)
}
}
parts[i] = string(out)
}
}
if j != 0 {
pattern = strings.Join(parts, "/")
regex, regexErr := regexp.Compile(pattern)
if regexErr != nil {
return nil, regexErr
}
mr.regex = regex
mr.hasregex = true
}
mr.pattern = pattern
return mr, nil
} }

View File

@ -21,8 +21,8 @@ var FilterUser = func(ctx *context.Context) {
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/person/asta/Xie", nil) r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddFilter("/person/:last/:first", "AfterStatic", FilterUser) handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
handler.Add("/person/:last/:first", &TestController{}) handler.Add("/person/:last/:first", &TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am astaXie" { if w.Body.String() != "i am astaXie" {
@ -40,8 +40,8 @@ var FilterAdminUser = func(ctx *context.Context) {
func TestPatternTwo(t *testing.T) { func TestPatternTwo(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/", nil) r, _ := http.NewRequest("GET", "/admin/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" { if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/ can't run") t.Errorf("filter /admin/ can't run")
@ -51,8 +51,8 @@ func TestPatternTwo(t *testing.T) {
func TestPatternThree(t *testing.T) { func TestPatternThree(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/astaxie", nil) r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" { if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/astaxie can't run") t.Errorf("filter /admin/astaxie can't run")

View File

@ -31,7 +31,7 @@ func TestFlashHeader(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
// setup the handler // setup the handler
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/", &TestFlashController{}, "get:TestWriteFlash") handler.Add("/", &TestFlashController{}, "get:TestWriteFlash")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)

View File

@ -16,22 +16,45 @@ import (
"mime/multipart" "mime/multipart"
"net" "net"
"net/http" "net/http"
"net/http/cookiejar"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"sync"
"time" "time"
) )
var defaultUserAgent = "beegoServer" var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false}
var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex
// createDefaultCookieJar creates a global cookiejar to store cookies.
func createDefaultCookie() {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultCookieJar, _ = cookiejar.New(nil)
}
// Overwrite default settings
func SetDefaultSetting(setting BeegoHttpSettings) {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultSetting = setting
if defaultSetting.ConnectTimeout == 0 {
defaultSetting.ConnectTimeout = 60 * time.Second
}
if defaultSetting.ReadWriteTimeout == 0 {
defaultSetting.ReadWriteTimeout = 60 * time.Second
}
}
// Get returns *BeegoHttpRequest with GET method. // Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHttpRequest { func Get(url string) *BeegoHttpRequest {
var req http.Request var req http.Request
req.Method = "GET" req.Method = "GET"
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent) return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
} }
// Post returns *BeegoHttpRequest with POST method. // Post returns *BeegoHttpRequest with POST method.
@ -39,8 +62,7 @@ func Post(url string) *BeegoHttpRequest {
var req http.Request var req http.Request
req.Method = "POST" req.Method = "POST"
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent) return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
} }
// Put returns *BeegoHttpRequest with PUT method. // Put returns *BeegoHttpRequest with PUT method.
@ -48,8 +70,7 @@ func Put(url string) *BeegoHttpRequest {
var req http.Request var req http.Request
req.Method = "PUT" req.Method = "PUT"
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent) return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
} }
// Delete returns *BeegoHttpRequest DELETE GET method. // Delete returns *BeegoHttpRequest DELETE GET method.
@ -57,8 +78,7 @@ func Delete(url string) *BeegoHttpRequest {
var req http.Request var req http.Request
req.Method = "DELETE" req.Method = "DELETE"
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent) return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
} }
// Head returns *BeegoHttpRequest with HEAD method. // Head returns *BeegoHttpRequest with HEAD method.
@ -66,40 +86,64 @@ func Head(url string) *BeegoHttpRequest {
var req http.Request var req http.Request
req.Method = "HEAD" req.Method = "HEAD"
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent) return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil} }
// BeegoHttpSettings
type BeegoHttpSettings struct {
ShowDebug bool
UserAgent string
ConnectTimeout time.Duration
ReadWriteTimeout time.Duration
TlsClientConfig *tls.Config
Proxy func(*http.Request) (*url.URL, error)
Transport http.RoundTripper
EnableCookie bool
} }
// BeegoHttpRequest provides more useful methods for requesting one url than http.Request. // BeegoHttpRequest provides more useful methods for requesting one url than http.Request.
type BeegoHttpRequest struct { type BeegoHttpRequest struct {
url string url string
req *http.Request req *http.Request
params map[string]string params map[string]string
files map[string]string files map[string]string
showdebug bool setting BeegoHttpSettings
connectTimeout time.Duration }
readWriteTimeout time.Duration
tlsClientConfig *tls.Config // Change request settings
proxy func(*http.Request) (*url.URL, error) func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest {
transport http.RoundTripper b.setting = setting
return b
}
// SetEnableCookie sets enable/disable cookiejar
func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest {
b.setting.EnableCookie = enable
return b
}
// SetUserAgent sets User-Agent header field
func (b *BeegoHttpRequest) SetAgent(useragent string) *BeegoHttpRequest {
b.setting.UserAgent = useragent
return b
} }
// Debug sets show debug or not when executing request. // Debug sets show debug or not when executing request.
func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
b.showdebug = isdebug b.setting.ShowDebug = isdebug
return b return b
} }
// SetTimeout sets connect time out and read-write time out for BeegoRequest. // SetTimeout sets connect time out and read-write time out for BeegoRequest.
func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
b.connectTimeout = connectTimeout b.setting.ConnectTimeout = connectTimeout
b.readWriteTimeout = readWriteTimeout b.setting.ReadWriteTimeout = readWriteTimeout
return b return b
} }
// SetTLSClientConfig sets tls connection configurations if visiting https url. // SetTLSClientConfig sets tls connection configurations if visiting https url.
func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest {
b.tlsClientConfig = config b.setting.TlsClientConfig = config
return b return b
} }
@ -134,7 +178,7 @@ func (b *BeegoHttpRequest) SetCookie(cookie *http.Cookie) *BeegoHttpRequest {
// Set transport to // Set transport to
func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest {
b.transport = transport b.setting.Transport = transport
return b return b
} }
@ -146,7 +190,7 @@ func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpR
// return u, nil // return u, nil
// } // }
func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest { func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest {
b.proxy = proxy b.setting.Proxy = proxy
return b return b
} }
@ -242,7 +286,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} }
b.req.URL = url b.req.URL = url
if b.showdebug { if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true) dump, err := httputil.DumpRequest(b.req, true)
if err != nil { if err != nil {
println(err.Error()) println(err.Error())
@ -250,32 +294,47 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
println(string(dump)) println(string(dump))
} }
trans := b.transport trans := b.setting.Transport
if trans == nil { if trans == nil {
// create default transport // create default transport
trans = &http.Transport{ trans = &http.Transport{
TLSClientConfig: b.tlsClientConfig, TLSClientConfig: b.setting.TlsClientConfig,
Proxy: b.proxy, Proxy: b.setting.Proxy,
Dial: TimeoutDialer(b.connectTimeout, b.readWriteTimeout), Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
} }
} else { } else {
// if b.transport is *http.Transport then set the settings. // if b.transport is *http.Transport then set the settings.
if t, ok := trans.(*http.Transport); ok { if t, ok := trans.(*http.Transport); ok {
if t.TLSClientConfig == nil { if t.TLSClientConfig == nil {
t.TLSClientConfig = b.tlsClientConfig t.TLSClientConfig = b.setting.TlsClientConfig
} }
if t.Proxy == nil { if t.Proxy == nil {
t.Proxy = b.proxy t.Proxy = b.setting.Proxy
} }
if t.Dial == nil { if t.Dial == nil {
t.Dial = TimeoutDialer(b.connectTimeout, b.readWriteTimeout) t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
} }
} }
} }
var jar http.CookieJar
if b.setting.EnableCookie {
if defaultCookieJar == nil {
createDefaultCookie()
}
jar = defaultCookieJar
} else {
jar = nil
}
client := &http.Client{ client := &http.Client{
Transport: trans, Transport: trans,
Jar: jar,
}
if b.setting.UserAgent != "" {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
} }
resp, err := client.Do(b.req) resp, err := client.Do(b.req)

View File

@ -1,4 +1,4 @@
// Beego (http://beego.me/) // Beego (http://beego.me)
// @description beego is an open-source, high-performance web framework for the Go programming language. // @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository // @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE // @license http://github.com/astaxie/beego/blob/master/LICENSE
@ -13,7 +13,7 @@ import (
) )
func TestGetUrl(t *testing.T) { func TestGetUrl(t *testing.T) {
resp, err := Get("http://beego.me/").Debug(true).Response() resp, err := Get("http://beego.me").Debug(true).Response()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -29,7 +29,7 @@ func TestGetUrl(t *testing.T) {
t.Fatal("data is no") t.Fatal("data is no")
} }
str, err := Get("http://beego.me/").String() str, err := Get("http://beego.me").String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -38,14 +38,63 @@ func TestGetUrl(t *testing.T) {
} }
} }
func TestPost(t *testing.T) { func ExamplePost(t *testing.T) {
b := Post("http://beego.me/").Debug(true) b := Post("http://beego.me/").Debug(true)
b.Param("username", "astaxie") b.Param("username", "astaxie")
b.Param("password", "hello") b.Param("password", "hello")
b.PostFile("uploadfile", "httplib.go") b.PostFile("uploadfile", "httplib_test.go")
str, err := b.String() str, err := b.String()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
fmt.Println(str) fmt.Println(str)
} }
func TestSimpleGetString(t *testing.T) {
fmt.Println("TestSimpleGetString==========================================")
html, err := Get("http://httpbin.org/headers").SetAgent("beegoooooo").String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
fmt.Println("TestSimpleGetString==========================================")
}
func TestSimpleGetStringWithDefaultCookie(t *testing.T) {
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================")
html, err := Get("http://httpbin.org/cookies/set?k1=v1").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
html, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
fmt.Println("TestSimpleGetStringWithDefaultCookie==========================================")
}
func TestDefaultSetting(t *testing.T) {
fmt.Println("TestDefaultSetting==========================================")
var def BeegoHttpSettings
def.EnableCookie = true
//def.ShowDebug = true
def.UserAgent = "UserAgent"
//def.ConnectTimeout = 60*time.Second
//def.ReadWriteTimeout = 60*time.Second
def.Transport = nil //http.DefaultTransport
SetDefaultSetting(def)
html, err := Get("http://httpbin.org/headers").String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
html, err = Get("http://httpbin.org/headers").String()
if err != nil {
t.Fatal(err)
}
fmt.Println(html)
fmt.Println("TestDefaultSetting==========================================")
}

View File

@ -7,131 +7,372 @@ package beego
import ( import (
"net/http" "net/http"
"strings"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
) )
type namespaceCond func(*beecontext.Context) bool type namespaceCond func(*beecontext.Context) bool
type innnerNamespace func(*Namespace)
// Namespace is store all the info
type Namespace struct { type Namespace struct {
prefix string prefix string
condition namespaceCond handlers *ControllerRegistor
handlers *ControllerRegistor
} }
func NewNamespace(prefix string) *Namespace { // get new Namespace
cr := NewControllerRegistor() func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
return &Namespace{ ns := &Namespace{
prefix: prefix, prefix: prefix,
handlers: cr, handlers: NewControllerRegister(),
} }
for _, p := range params {
p(ns)
}
return ns
} }
// set condtion function
// if cond return true can run this namespace, else can't
// usage:
// ns.Cond(func (ctx *context.Context) bool{
// if ctx.Input.Domain() == "api.beego.me" {
// return true
// }
// return false
// })
// Cond as the first filter
func (n *Namespace) Cond(cond namespaceCond) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace {
n.condition = cond fn := func(ctx *beecontext.Context) {
return n if !cond(ctx) {
} middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
}
func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace { }
if action == "before" { if v, ok := n.handlers.filters[BeforeRouter]; ok {
action = "BeforeRouter" mr := new(FilterRouter)
} else if action == "after" { mr.tree = NewTree()
action = "FinishRouter" mr.pattern = "*"
mr.filterFunc = fn
mr.tree.AddRouter("*", true)
n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
} else {
n.handlers.InsertFilter("*", BeforeRouter, fn)
} }
n.handlers.AddFilter("*", action, filter)
return n return n
} }
// add filter in the Namespace
// action has before & after
// FilterFunc
// usage:
// Filter("before", func (ctx *context.Context){
// _, ok := ctx.Input.Session("uid").(int)
// if !ok && ctx.Request.RequestURI != "/login" {
// ctx.Redirect(302, "/login")
// }
// })
func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace {
var a int
if action == "before" {
a = BeforeRouter
} else if action == "after" {
a = FinishRouter
}
for _, f := range filter {
n.handlers.InsertFilter("*", a, f)
}
return n
}
// same as beego.Rourer
// refer: https://godoc.org/github.com/astaxie/beego#Router
func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace {
n.handlers.Add(rootpath, c, mappingMethods...) n.handlers.Add(rootpath, c, mappingMethods...)
return n return n
} }
// same as beego.AutoRouter
// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
n.handlers.AddAuto(c) n.handlers.AddAuto(c)
return n return n
} }
// same as beego.AutoPrefix
// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
n.handlers.AddAutoPrefix(prefix, c) n.handlers.AddAutoPrefix(prefix, c)
return n return n
} }
// same as beego.Get
// refer: https://godoc.org/github.com/astaxie/beego#Get
func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
n.handlers.Get(rootpath, f) n.handlers.Get(rootpath, f)
return n return n
} }
// same as beego.Post
// refer: https://godoc.org/github.com/astaxie/beego#Post
func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
n.handlers.Post(rootpath, f) n.handlers.Post(rootpath, f)
return n return n
} }
// same as beego.Delete
// refer: https://godoc.org/github.com/astaxie/beego#Delete
func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
n.handlers.Delete(rootpath, f) n.handlers.Delete(rootpath, f)
return n return n
} }
// same as beego.Put
// refer: https://godoc.org/github.com/astaxie/beego#Put
func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
n.handlers.Put(rootpath, f) n.handlers.Put(rootpath, f)
return n return n
} }
// same as beego.Head
// refer: https://godoc.org/github.com/astaxie/beego#Head
func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
n.handlers.Head(rootpath, f) n.handlers.Head(rootpath, f)
return n return n
} }
// same as beego.Options
// refer: https://godoc.org/github.com/astaxie/beego#Options
func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
n.handlers.Options(rootpath, f) n.handlers.Options(rootpath, f)
return n return n
} }
// same as beego.Patch
// refer: https://godoc.org/github.com/astaxie/beego#Patch
func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
n.handlers.Patch(rootpath, f) n.handlers.Patch(rootpath, f)
return n return n
} }
// same as beego.Any
// refer: https://godoc.org/github.com/astaxie/beego#Any
func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
n.handlers.Any(rootpath, f) n.handlers.Any(rootpath, f)
return n return n
} }
// same as beego.Handler
// refer: https://godoc.org/github.com/astaxie/beego#Handler
func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
n.handlers.Handler(rootpath, h) n.handlers.Handler(rootpath, h)
return n return n
} }
func (n *Namespace) Namespace(ns *Namespace) *Namespace { // add include class
n.handlers.Handler(ns.prefix, ns, true) // refer: https://godoc.org/github.com/astaxie/beego#Include
func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
n.handlers.Include(cList...)
return n return n
} }
func (n *Namespace) ServeHTTP(rw http.ResponseWriter, r *http.Request) { // nest Namespace
//trim the preifix from URL.Path // usage:
r.URL.Path = strings.TrimPrefix(r.URL.Path, n.prefix) //ns := beego.NewNamespace(“/v1”).
// init context //Namespace(
context := &beecontext.Context{ // beego.NewNamespace("/shop").
ResponseWriter: rw, // Get("/:id", func(ctx *context.Context) {
Request: r, // ctx.Output.Body([]byte("shopinfo"))
Input: beecontext.NewInput(r), // }),
Output: beecontext.NewOutput(), // beego.NewNamespace("/order").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("orderinfo"))
// }),
// beego.NewNamespace("/crm").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("crminfo"))
// }),
//)
func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
for _, ni := range ns {
for k, v := range ni.handlers.routers {
if t, ok := n.handlers.routers[k]; ok {
addPrefix(v, ni.prefix)
n.handlers.routers[k].AddTree(ni.prefix, v)
} else {
t = NewTree()
t.AddTree(ni.prefix, v)
addPrefix(t, ni.prefix)
n.handlers.routers[k] = t
}
}
if n.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(ni.prefix, mr.tree)
mr.tree = t
n.handlers.insertFilterRouter(pos, mr)
}
}
}
} }
context.Output.Context = context return n
context.Output.EnableGzip = EnableGzip
if context.Input.IsWebsocket() {
context.ResponseWriter = rw
}
if n.condition != nil && !n.condition(context) {
http.Error(rw, "Method Not Allowed", 405)
}
n.handlers.ServeHTTP(rw, r)
} }
// register Namespace into beego.Handler
// support multi Namespace
func AddNamespace(nl ...*Namespace) { func AddNamespace(nl ...*Namespace) {
for _, n := range nl { for _, n := range nl {
Handler(n.prefix, n, true) for k, v := range n.handlers.routers {
if t, ok := BeeApp.Handlers.routers[k]; ok {
addPrefix(v, n.prefix)
BeeApp.Handlers.routers[k].AddTree(n.prefix, v)
} else {
t = NewTree()
t.AddTree(n.prefix, v)
addPrefix(t, n.prefix)
BeeApp.Handlers.routers[k] = t
}
}
if n.handlers.enableFilter {
for pos, filterList := range n.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(n.prefix, mr.tree)
mr.tree = t
BeeApp.Handlers.insertFilterRouter(pos, mr)
}
}
}
}
}
func addPrefix(t *Tree, prefix string) {
for _, v := range t.fixrouters {
addPrefix(v, prefix)
}
if t.wildcard != nil {
addPrefix(t.wildcard, prefix)
}
for _, l := range t.leaves {
if c, ok := l.runObject.(*controllerInfo); ok {
c.pattern = prefix + c.pattern
}
}
}
// Namespace Condition
func NSCond(cond namespaceCond) innnerNamespace {
return func(ns *Namespace) {
ns.Cond(cond)
}
}
// Namespace BeforeRouter filter
func NSBefore(filiterList ...FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Filter("before", filiterList...)
}
}
// Namespace FinishRouter filter
func NSAfter(filiterList ...FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Filter("after", filiterList...)
}
}
// Namespace Include ControllerInterface
func NSInclude(cList ...ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.Include(cList...)
}
}
// Namespace Router
func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innnerNamespace {
return func(ns *Namespace) {
ns.Router(rootpath, c, mappingMethods...)
}
}
// Namespace Get
func NSGet(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Get(rootpath, f)
}
}
// Namespace Post
func NSPost(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Post(rootpath, f)
}
}
// Namespace Head
func NSHead(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Head(rootpath, f)
}
}
// Namespace Put
func NSPut(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Put(rootpath, f)
}
}
// Namespace Delete
func NSDelete(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Delete(rootpath, f)
}
}
// Namespace Any
func NSAny(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Any(rootpath, f)
}
}
// Namespace Options
func NSOptions(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Options(rootpath, f)
}
}
// Namespace Patch
func NSPatch(rootpath string, f FilterFunc) innnerNamespace {
return func(ns *Namespace) {
ns.Patch(rootpath, f)
}
}
//Namespace AutoRouter
func NSAutoRouter(c ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.AutoRouter(c)
}
}
// Namespace AutoPrefix
func NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace {
return func(ns *Namespace) {
ns.AutoPrefix(prefix, c)
}
}
// Namespace add sub Namespace
func NSNamespace(prefix string, params ...innnerNamespace) innnerNamespace {
return func(ns *Namespace) {
n := NewNamespace(prefix, params...)
ns.Namespace(n)
} }
} }

View File

@ -23,7 +23,8 @@ func TestNamespaceGet(t *testing.T) {
ns.Get("/user", func(ctx *context.Context) { ns.Get("/user", func(ctx *context.Context) {
ctx.Output.Body([]byte("v1_user")) ctx.Output.Body([]byte("v1_user"))
}) })
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "v1_user" { if w.Body.String() != "v1_user" {
t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
} }
@ -37,7 +38,8 @@ func TestNamespacePost(t *testing.T) {
ns.Post("/user/:id", func(ctx *context.Context) { ns.Post("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}) })
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" { if w.Body.String() != "123" {
t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
} }
@ -54,7 +56,8 @@ func TestNamespaceNest(t *testing.T) {
ctx.Output.Body([]byte("order")) ctx.Output.Body([]byte("order"))
}), }),
) )
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "order" { if w.Body.String() != "order" {
t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
} }
@ -71,12 +74,39 @@ func TestNamespaceNestParam(t *testing.T) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}), }),
) )
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" { if w.Body.String() != "123" {
t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
} }
} }
func TestNamespaceRouter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/api/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Router("/api/list", &TestController{}, "*:List")
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceAutoFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.AutoRouter(&TestController{})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("user define func can't run")
}
}
func TestNamespaceFilter(t *testing.T) { func TestNamespaceFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/user/123", nil) r, _ := http.NewRequest("GET", "/v1/user/123", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -88,41 +118,18 @@ func TestNamespaceFilter(t *testing.T) {
Get("/user/:id", func(ctx *context.Context) { Get("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}) })
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "this is Filter" { if w.Body.String() != "this is Filter" {
t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
} }
} }
func TestNamespaceRouter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/api/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Router("/api/list", &TestController{}, "*:List")
ns.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceAutoFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.AutoRouter(&TestController{})
ns.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
t.Errorf("user define func can't run")
}
}
func TestNamespaceCond(t *testing.T) { func TestNamespaceCond(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil) r, _ := http.NewRequest("GET", "/v2/test/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
ns := NewNamespace("/v1") ns := NewNamespace("/v2")
ns.Cond(func(ctx *context.Context) bool { ns.Cond(func(ctx *context.Context) bool {
if ctx.Input.Domain() == "beego.me" { if ctx.Input.Domain() == "beego.me" {
return true return true
@ -130,8 +137,27 @@ func TestNamespaceCond(t *testing.T) {
return false return false
}). }).
AutoRouter(&TestController{}) AutoRouter(&TestController{})
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Code != 405 { if w.Code != 405 {
t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code)) t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
} }
} }
func TestNamespaceInside(t *testing.T) {
r, _ := http.NewRequest("GET", "/v3/shop/order/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v3",
NSAutoRouter(&TestController{}),
NSNamespace("/shop",
NSGet("/order/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}),
),
)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
t.Errorf("TestNamespaceInside can't run, get the response is " + w.Body.String())
}
}

View File

@ -144,7 +144,11 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
value = field.Interface() value = field.Interface()
if t, ok := value.(time.Time); ok { if t, ok := value.(time.Time); ok {
d.ins.TimeToDB(&t, tz) d.ins.TimeToDB(&t, tz)
value = t if t.IsZero() {
value = nil
} else {
value = t
}
} }
default: default:
switch { switch {

187
parser.go Normal file
View File

@ -0,0 +1,187 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package beego
import (
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"os"
"path"
"strings"
"github.com/astaxie/beego/utils"
)
var globalRouterTemplate = `package routers
import (
"github.com/astaxie/beego"
)
func init() {
{{.globalinfo}}
}
`
var (
lastupdateFilename string = "lastupdate.tmp"
pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments
)
func init() {
pkgLastupdate = make(map[string]int64)
genInfoList = make(map[string][]ControllerComments)
}
func parserPkg(pkgRealpath, pkgpath string) error {
if !compareFile(pkgRealpath) {
Info(pkgRealpath + " don't has updated")
return nil
}
fileSet := token.NewFileSet()
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments)
if err != nil {
return err
}
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for _, d := range fl.Decls {
switch specDecl := d.(type) {
case *ast.FuncDecl:
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(specDecl.Recv.List[0].Type.(*ast.StarExpr).X), pkgpath)
}
}
}
}
genRouterCode()
savetoFile(pkgRealpath)
return nil
}
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
if comments != nil && comments.List != nil {
for _, c := range comments.List {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@router") {
elements := strings.TrimLeft(t, "@router ")
e1 := strings.SplitN(elements, " ", 2)
if len(e1) < 1 {
return errors.New("you should has router infomation")
}
key := pkgpath + ":" + controllerName
cc := ControllerComments{}
cc.Method = funcName
cc.Router = e1[0]
if len(e1) == 2 && e1[1] != "" {
e1 = strings.SplitN(e1[1], " ", 2)
if len(e1) >= 1 {
cc.AllowHTTPMethods = strings.Split(strings.Trim(e1[0], "[]"), ",")
} else {
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
}
} else {
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
}
if len(e1) == 2 && e1[1] != "" {
keyval := strings.Split(strings.Trim(e1[1], "[]"), " ")
for _, kv := range keyval {
kk := strings.Split(kv, ":")
cc.Params = append(cc.Params, map[string]string{strings.Join(kk[:len(kk)-1], ":"): kk[len(kk)-1]})
}
}
genInfoList[key] = append(genInfoList[key], cc)
}
}
}
return nil
}
func genRouterCode() {
os.Mkdir(path.Join(AppPath, "routers"), 0755)
Info("generate router from comments")
var globalinfo string
for k, cList := range genInfoList {
for _, c := range cList {
allmethod := "nil"
if len(c.AllowHTTPMethods) > 0 {
allmethod = "[]string{"
for _, m := range c.AllowHTTPMethods {
allmethod += `"` + m + `",`
}
allmethod = strings.TrimRight(allmethod, ",") + "}"
}
params := "nil"
if len(c.Params) > 0 {
params = "[]map[string]string{"
for _, p := range c.Params {
for k, v := range p {
params = params + `map[string]string{` + k + `:"` + v + `"},`
}
}
params = strings.TrimRight(params, ",") + "}"
}
globalinfo = globalinfo + `
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
beego.ControllerComments{
"` + strings.TrimSpace(c.Method) + `",
"` + c.Router + `",
` + allmethod + `,
` + params + `})
`
}
}
if globalinfo != "" {
f, err := os.Create(path.Join(AppPath, "routers", "commentsRouter.go"))
if err != nil {
panic(err)
}
defer f.Close()
f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1))
}
}
func compareFile(pkgRealpath string) bool {
if utils.FileExists(path.Join(AppPath, lastupdateFilename)) {
content, err := ioutil.ReadFile(path.Join(AppPath, lastupdateFilename))
if err != nil {
return true
}
json.Unmarshal(content, &pkgLastupdate)
ft, err := os.Lstat(pkgRealpath)
if err != nil {
return true
}
if v, ok := pkgLastupdate[pkgRealpath]; ok {
if ft.ModTime().UnixNano() >= v {
return false
}
}
}
return true
}
func savetoFile(pkgRealpath string) {
ft, err := os.Lstat(pkgRealpath)
if err != nil {
return
}
pkgLastupdate[pkgRealpath] = ft.ModTime().UnixNano()
d, err := json.Marshal(pkgLastupdate)
if err != nil {
return
}
ioutil.WriteFile(path.Join(AppPath, lastupdateFilename), d, os.ModePerm)
}

169
reload.go
View File

@ -1,169 +0,0 @@
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package beego
import (
"errors"
"fmt"
"log"
"net"
"os"
"os/exec"
"os/signal"
"reflect"
"strconv"
"sync"
//"syscall"
)
const (
// An environment variable when restarting application http listener.
FDKey = "BEEGO_HOT_FD"
)
// Export an error equivalent to net.errClosing for use with Accept during
// a graceful exit.
var ErrClosing = errors.New("use of closed network connection")
var ErrInitStart = errors.New("init from")
// Allows for us to notice when the connection is closed.
type conn struct {
net.Conn
wg *sync.WaitGroup
isclose bool
lock *sync.Mutex
}
// Close current processing connection.
func (c conn) Close() error {
c.lock.Lock()
defer c.lock.Unlock()
err := c.Conn.Close()
if !c.isclose && err == nil {
c.wg.Done()
c.isclose = true
}
return err
}
type stoppableListener struct {
net.Listener
count int64
stopped bool
wg sync.WaitGroup
}
var theStoppable *stoppableListener
func newStoppable(l net.Listener) (sl *stoppableListener) {
sl = &stoppableListener{Listener: l}
// this goroutine monitors the channel. Can't do this in
// Accept (below) because once it enters sl.Listener.Accept()
// it blocks. We unblock it by closing the fd it is trying to
// accept(2) on.
go func() {
WaitSignal(l)
sl.stopped = true
sl.Listener.Close()
}()
return
}
// Set stopped Listener to accept requests again.
// it returns the accepted and closable connection or error.
func (sl *stoppableListener) Accept() (c net.Conn, err error) {
c, err = sl.Listener.Accept()
if err != nil {
return
}
sl.wg.Add(1)
// Wrap the returned connection, so that we can observe when
// it is closed.
c = conn{Conn: c, wg: &sl.wg}
return
}
// Listener waits signal to kill or interrupt then restart.
func WaitSignal(l net.Listener) error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill)
for {
sig := <-ch
log.Println(sig.String())
switch sig {
case os.Kill:
return nil
case os.Interrupt:
err := Restart(l)
if nil != err {
return err
}
return nil
}
}
}
// Kill current running os process.
func CloseSelf() error {
ppid := os.Getpid()
if ppid == 1 { // init provided sockets, for example systemd
return nil
}
p, err := os.FindProcess(ppid)
if err != nil {
return err
}
return p.Kill()
}
// Re-exec this image without dropping the listener passed to this function.
func Restart(l net.Listener) error {
argv0, err := exec.LookPath(os.Args[0])
if nil != err {
return err
}
wd, err := os.Getwd()
if nil != err {
return err
}
v := reflect.ValueOf(l).Elem().FieldByName("fd").Elem()
fd := uintptr(v.FieldByName("sysfd").Int())
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr},
os.NewFile(fd, string(v.FieldByName("sysfile").String())))
p, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: wd,
Env: append(os.Environ(), fmt.Sprintf("%s=%d", FDKey, fd)),
Files: allFiles,
})
if nil != err {
return err
}
log.Printf("spawned child %d\n", p.Pid)
return nil
}
// Get current net.Listen in running process.
func GetInitListener(tcpaddr *net.TCPAddr) (l net.Listener, err error) {
countStr := os.Getenv(FDKey)
if countStr == "" {
return net.ListenTCP("tcp", tcpaddr)
}
count, err := strconv.Atoi(countStr)
if err != nil {
return nil, err
}
f := os.NewFile(uintptr(count), "listen socket")
l, err = net.FileListener(f)
if err != nil {
return nil, err
}
return l, nil
}

919
router.go

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,10 @@ func (this *TestController) Post() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
} }
func (this *TestController) Param() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
}
func (this *TestController) List() { func (this *TestController) List() {
this.Ctx.Output.Body([]byte("i am list")) this.Ctx.Output.Body([]byte("i am list"))
} }
@ -43,6 +47,15 @@ func (this *TestController) GetUrl() {
this.Ctx.Output.Body([]byte(this.UrlFor(".Myext"))) this.Ctx.Output.Body([]byte(this.UrlFor(".Myext")))
} }
func (t *TestController) GetParams() {
t.Ctx.WriteString(t.Ctx.Input.Query(":last") + "+" +
t.Ctx.Input.Query(":first") + "+" + t.Ctx.Input.Query("learn"))
}
func (t *TestController) GetManyRouter() {
t.Ctx.WriteString(t.Ctx.Input.Query(":id") + t.Ctx.Input.Query(":page"))
}
type ResStatus struct { type ResStatus struct {
Code int Code int
Msg string Msg string
@ -63,21 +76,45 @@ func (this *JsonController) Get() {
} }
func TestUrlFor(t *testing.T) { func TestUrlFor(t *testing.T) {
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/api/list", &TestController{}, "*:List")
handler.Add("/person/:last/:first", &TestController{}) handler.Add("/person/:last/:first", &TestController{}, "*:Param")
handler.AddAuto(&TestController{}) handler.AddAuto(&TestController{})
if handler.UrlFor("TestController.List") != "/api/list" { if handler.UrlFor("TestController.List") != "/api/list" {
Info(handler.UrlFor("TestController.List"))
t.Errorf("TestController.List must equal to /api/list") t.Errorf("TestController.List must equal to /api/list")
} }
if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" { if handler.UrlFor("TestController.Param", ":last", "xie", ":first", "asta") != "/person/xie/asta" {
t.Errorf("TestController.Get must equal to /person/xie/asta") t.Errorf("TestController.Param must equal to /person/xie/asta, but get " + handler.UrlFor("TestController.Param", ":last", "xie", ":first", "asta"))
} }
if handler.UrlFor("TestController.Myext") != "/Test/Myext" { if handler.UrlFor("TestController.Myext") != "/test/myext" {
t.Errorf("TestController.Myext must equal to /Test/Myext") t.Errorf("TestController.Myext must equal to /test/myext")
} }
if handler.UrlFor("TestController.GetUrl") != "/Test/GetUrl" { if handler.UrlFor("TestController.GetUrl") != "/test/geturl" {
t.Errorf("TestController.GetUrl must equal to /Test/GetUrl") t.Errorf("TestController.GetUrl must equal to /test/geturl")
}
}
func TestUrlFor2(t *testing.T) {
handler := NewControllerRegister()
handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List")
handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param")
handler.Add("/:year:int/:month:int/:title/:entid", &TestController{})
if handler.UrlFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") !=
"/v1/za/cms_12_123.html" {
Info(handler.UrlFor("TestController.List"))
t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html")
}
if handler.UrlFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") !=
"/v1/za_cms/ttt_12_123.html" {
Info(handler.UrlFor("TestController.Param"))
t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html")
}
if handler.UrlFor("TestController.Get", ":year", "1111", ":month", "11",
":title", "aaaa", ":entid", "aaaa") !=
"/1111/11/aaaa/aaaa" {
Info(handler.UrlFor("TestController.Get"))
t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa")
} }
} }
@ -85,7 +122,7 @@ func TestUserFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/api/list", nil) r, _ := http.NewRequest("GET", "/api/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/api/list", &TestController{}, "*:List")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am list" { if w.Body.String() != "i am list" {
@ -97,7 +134,7 @@ func TestPostFunc(t *testing.T) {
r, _ := http.NewRequest("POST", "/astaxie", nil) r, _ := http.NewRequest("POST", "/astaxie", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/:name", &TestController{}) handler.Add("/:name", &TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "astaxie" { if w.Body.String() != "astaxie" {
@ -109,7 +146,7 @@ func TestAutoFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/test/list", nil) r, _ := http.NewRequest("GET", "/test/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddAuto(&TestController{}) handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am list" { if w.Body.String() != "i am list" {
@ -121,7 +158,7 @@ func TestAutoFuncParams(t *testing.T) {
r, _ := http.NewRequest("GET", "/test/params/2009/11/12", nil) r, _ := http.NewRequest("GET", "/test/params/2009/11/12", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddAuto(&TestController{}) handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "20091112" { if w.Body.String() != "20091112" {
@ -133,7 +170,7 @@ func TestAutoExtFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/test/myext.json", nil) r, _ := http.NewRequest("GET", "/test/myext.json", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddAuto(&TestController{}) handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "json" { if w.Body.String() != "json" {
@ -146,22 +183,12 @@ func TestRouteOk(t *testing.T) {
r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil) r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/person/:last/:first", &TestController{}) handler.Add("/person/:last/:first", &TestController{}, "get:GetParams")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
body := w.Body.String()
lastNameParam := r.URL.Query().Get(":last") if body != "anderson+thomas+kungfu" {
firstNameParam := r.URL.Query().Get(":first") t.Errorf("url param set to [%s];", body)
learnParam := r.URL.Query().Get("learn")
if lastNameParam != "anderson" {
t.Errorf("url param set to [%s]; want [%s]", lastNameParam, "anderson")
}
if firstNameParam != "thomas" {
t.Errorf("url param set to [%s]; want [%s]", firstNameParam, "thomas")
}
if learnParam != "kungfu" {
t.Errorf("url param set to [%s]; want [%s]", learnParam, "kungfu")
} }
} }
@ -170,18 +197,14 @@ func TestManyRoute(t *testing.T) {
r, _ := http.NewRequest("GET", "/beego32-12.html", nil) r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}) handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, "get:GetManyRouter")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
id := r.URL.Query().Get(":id") body := w.Body.String()
page := r.URL.Query().Get(":page")
if id != "32" { if body != "3212" {
t.Errorf("url param set to [%s]; want [%s]", id, "32") t.Errorf("url param set to [%s];", body)
}
if page != "12" {
t.Errorf("url param set to [%s]; want [%s]", page, "12")
} }
} }
@ -189,7 +212,7 @@ func TestNotFound(t *testing.T) {
r, _ := http.NewRequest("GET", "/", nil) r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Code != http.StatusNotFound { if w.Code != http.StatusNotFound {
@ -203,7 +226,7 @@ func TestStatic(t *testing.T) {
r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil) r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Code != 404 { if w.Code != 404 {
@ -215,7 +238,7 @@ func TestPrepare(t *testing.T) {
r, _ := http.NewRequest("GET", "/json/list", nil) r, _ := http.NewRequest("GET", "/json/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Add("/json/list", &JsonController{}) handler.Add("/json/list", &JsonController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != `"prepare"` { if w.Body.String() != `"prepare"` {
@ -227,7 +250,7 @@ func TestAutoPrefix(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/test/list", nil) r, _ := http.NewRequest("GET", "/admin/test/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.AddAutoPrefix("/admin", &TestController{}) handler.AddAutoPrefix("/admin", &TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am list" { if w.Body.String() != "i am list" {
@ -239,7 +262,7 @@ func TestRouterGet(t *testing.T) {
r, _ := http.NewRequest("GET", "/user", nil) r, _ := http.NewRequest("GET", "/user", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Get("/user", func(ctx *context.Context) { handler.Get("/user", func(ctx *context.Context) {
ctx.Output.Body([]byte("Get userlist")) ctx.Output.Body([]byte("Get userlist"))
}) })
@ -253,7 +276,7 @@ func TestRouterPost(t *testing.T) {
r, _ := http.NewRequest("POST", "/user/123", nil) r, _ := http.NewRequest("POST", "/user/123", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Post("/user/:id", func(ctx *context.Context) { handler.Post("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}) })
@ -271,10 +294,64 @@ func TestRouterHandler(t *testing.T) {
r, _ := http.NewRequest("POST", "/sayhi", nil) r, _ := http.NewRequest("POST", "/sayhi", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegister()
handler.Handler("/sayhi", http.HandlerFunc(sayhello)) handler.Handler("/sayhi", http.HandlerFunc(sayhello))
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "sayhello" { if w.Body.String() != "sayhello" {
t.Errorf("TestRouterHandler can't run") t.Errorf("TestRouterHandler can't run")
} }
} }
//
// Benchmarks NewApp:
//
func beegoFilterFunc(ctx *context.Context) {
ctx.WriteString("hello")
}
type AdminController struct {
Controller
}
func (a *AdminController) Get() {
a.Ctx.WriteString("hello")
}
func TestRouterFunc(t *testing.T) {
mux := NewControllerRegister()
mux.Get("/action", beegoFilterFunc)
mux.Post("/action", beegoFilterFunc)
rw, r := testRequest("GET", "/action")
mux.ServeHTTP(rw, r)
if rw.Body.String() != "hello" {
t.Errorf("TestRouterFunc can't run")
}
}
func BenchmarkFunc(b *testing.B) {
mux := NewControllerRegister()
mux.Get("/action", beegoFilterFunc)
rw, r := testRequest("GET", "/action")
b.ResetTimer()
for i := 0; i < b.N; i++ {
mux.ServeHTTP(rw, r)
}
}
func BenchmarkController(b *testing.B) {
mux := NewControllerRegister()
mux.Add("/action", &AdminController{})
rw, r := testRequest("GET", "/action")
b.ResetTimer()
for i := 0; i < b.N; i++ {
mux.ServeHTTP(rw, r)
}
}
func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) {
request, _ := http.NewRequest(method, path, nil)
recorder := httptest.NewRecorder()
return recorder, request
}

View File

@ -20,6 +20,8 @@ import (
"io" "io"
"strconv" "strconv"
"time" "time"
"github.com/astaxie/beego/utils"
) )
func init() { func init() {
@ -60,8 +62,8 @@ func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) {
// generateRandomKey creates a random key with the given strength. // generateRandomKey creates a random key with the given strength.
func generateRandomKey(strength int) []byte { func generateRandomKey(strength int) []byte {
k := make([]byte, strength) k := make([]byte, strength)
if _, err := io.ReadFull(rand.Reader, k); err != nil { if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil {
return nil return utils.RandomCreateBytes(strength)
} }
return k return k
} }

View File

@ -18,6 +18,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
"github.com/astaxie/beego/utils"
) )
// SessionStore contains all data for one session process with specific id. // SessionStore contains all data for one session process with specific id.
@ -237,9 +239,9 @@ func (manager *Manager) SetSecure(secure bool) {
// generate session id with rand string, unix nano time, remote addr by hash function. // generate session id with rand string, unix nano time, remote addr by hash function.
func (manager *Manager) sessionId(r *http.Request) (sid string) { func (manager *Manager) sessionId(r *http.Request) (sid string) {
bs := make([]byte, 24) bs := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, bs); err != nil { if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil {
return "" bs = utils.RandomCreateBytes(32)
} }
sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs)
if manager.config.SessionIDHashFunc == "md5" { if manager.config.SessionIDHashFunc == "md5" {

View File

@ -18,7 +18,7 @@ import (
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
func serverStaticRouter(ctx *context.Context) bool { func serverStaticRouter(ctx *context.Context) {
requestPath := path.Clean(ctx.Input.Request.URL.Path) requestPath := path.Clean(ctx.Input.Request.URL.Path)
for prefix, staticDir := range StaticDir { for prefix, staticDir := range StaticDir {
if len(prefix) == 0 { if len(prefix) == 0 {
@ -28,17 +28,13 @@ func serverStaticRouter(ctx *context.Context) bool {
file := path.Join(staticDir, requestPath) file := path.Join(staticDir, requestPath)
if utils.FileExists(file) { if utils.FileExists(file) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file) http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return true return
} }
} }
if strings.HasPrefix(requestPath, prefix) { if strings.HasPrefix(requestPath, prefix) {
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
continue continue
} }
if requestPath == prefix && prefix[len(prefix)-1] != '/' {
http.Redirect(ctx.ResponseWriter, ctx.Request, requestPath+"/", 302)
return true
}
file := path.Join(staticDir, requestPath[len(prefix):]) file := path.Join(staticDir, requestPath[len(prefix):])
finfo, err := os.Stat(file) finfo, err := os.Stat(file)
if err != nil { if err != nil {
@ -46,12 +42,12 @@ func serverStaticRouter(ctx *context.Context) bool {
Warn(err) Warn(err)
} }
http.NotFound(ctx.ResponseWriter, ctx.Request) http.NotFound(ctx.ResponseWriter, ctx.Request)
return true return
} }
//if the request is dir and DirectoryIndex is false then //if the request is dir and DirectoryIndex is false then
if finfo.IsDir() && !DirectoryIndex { if finfo.IsDir() && !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
return true return
} }
//This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request
@ -73,7 +69,7 @@ func serverStaticRouter(ctx *context.Context) bool {
memzipfile, err := openMemZipFile(file, contentEncoding) memzipfile, err := openMemZipFile(file, contentEncoding)
if err != nil { if err != nil {
return true return
} }
if contentEncoding == "gzip" { if contentEncoding == "gzip" {
@ -89,8 +85,7 @@ func serverStaticRouter(ctx *context.Context) bool {
} else { } else {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file) http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
} }
return true return
} }
} }
return false
} }

133
swagger/docsSpec.go Normal file
View File

@ -0,0 +1,133 @@
package swagger
const SwaggerVersion = "1.2"
type ResourceListing struct {
ApiVersion string `json:"apiVersion"`
SwaggerVersion string `json:"swaggerVersion"` // e.g 1.2
// BasePath string `json:"basePath"` obsolete in 1.1
Apis []ApiRef `json:"apis"`
Infos Infomation `json:"info"`
}
type ApiRef struct {
Path string `json:"path"` // relative or absolute, must start with /
Description string `json:"description"`
}
type Infomation struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Contact string `json:"contact,omitempty"`
TermsOfServiceUrl string `json:"termsOfServiceUrl,omitempty"`
License string `json:"license,omitempty"`
LicenseUrl string `json:"licenseUrl,omitempty"`
}
// https://github.com/wordnik/swagger-core/blob/scala_2.10-1.3-RC3/schemas/api-declaration-schema.json
type ApiDeclaration struct {
ApiVersion string `json:"apiVersion"`
SwaggerVersion string `json:"swaggerVersion"`
BasePath string `json:"basePath"`
ResourcePath string `json:"resourcePath"` // must start with /
Consumes []string `json:"consumes,omitempty"`
Produces []string `json:"produces,omitempty"`
Apis []Api `json:"apis,omitempty"`
Models map[string]Model `json:"models,omitempty"`
}
type Api struct {
Path string `json:"path"` // relative or absolute, must start with /
Description string `json:"description"`
Operations []Operation `json:"operations,omitempty"`
}
type Operation struct {
HttpMethod string `json:"httpMethod"`
Nickname string `json:"nickname"`
Type string `json:"type"` // in 1.1 = DataType
// ResponseClass string `json:"responseClass"` obsolete in 1.2
Summary string `json:"summary,omitempty"`
Notes string `json:"notes,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"` // optional
Consumes []string `json:"consumes,omitempty"`
Produces []string `json:"produces,omitempty"`
Authorizations []Authorization `json:"authorizations,omitempty"`
Protocols []Protocol `json:"protocols,omitempty"`
}
type Protocol struct {
}
type ResponseMessage struct {
Code int `json:"code"`
Message string `json:"message"`
ResponseModel string `json:"responseModel"`
}
type Parameter struct {
ParamType string `json:"paramType"` // path,query,body,header,form
Name string `json:"name"`
Description string `json:"description"`
DataType string `json:"dataType"` // 1.2 needed?
Type string `json:"type"` // integer
Format string `json:"format"` // int64
AllowMultiple bool `json:"allowMultiple"`
Required bool `json:"required"`
Minimum int `json:"minimum"`
Maximum int `json:"maximum"`
}
type ErrorResponse struct {
Code int `json:"code"`
Reason string `json:"reason"`
}
type Model struct {
Id string `json:"id"`
Required []string `json:"required,omitempty"`
Properties map[string]ModelProperty `json:"properties"`
}
type ModelProperty struct {
Type string `json:"type"`
Description string `json:"description"`
Items map[string]string `json:"items,omitempty"`
Format string `json:"format"`
}
// https://github.com/wordnik/swagger-core/wiki/authorizations
type Authorization struct {
LocalOAuth OAuth `json:"local-oauth"`
ApiKey ApiKey `json:"apiKey"`
}
// https://github.com/wordnik/swagger-core/wiki/authorizations
type OAuth struct {
Type string `json:"type"` // e.g. oauth2
Scopes []string `json:"scopes"` // e.g. PUBLIC
GrantTypes map[string]GrantType `json:"grantTypes"`
}
// https://github.com/wordnik/swagger-core/wiki/authorizations
type GrantType struct {
LoginEndpoint Endpoint `json:"loginEndpoint"`
TokenName string `json:"tokenName"` // e.g. access_code
TokenRequestEndpoint Endpoint `json:"tokenRequestEndpoint"`
TokenEndpoint Endpoint `json:"tokenEndpoint"`
}
// https://github.com/wordnik/swagger-core/wiki/authorizations
type Endpoint struct {
Url string `json:"url"`
ClientIdName string `json:"clientIdName"`
ClientSecretName string `json:"clientSecretName"`
TokenName string `json:"tokenName"`
}
// https://github.com/wordnik/swagger-core/wiki/authorizations
type ApiKey struct {
Type string `json:"type"` // e.g. apiKey
PassAs string `json:"passAs"` // e.g. header
}

View File

@ -44,6 +44,7 @@ func init() {
beegoTplFuncMap["renderform"] = RenderForm beegoTplFuncMap["renderform"] = RenderForm
beegoTplFuncMap["assets_js"] = AssetsJs beegoTplFuncMap["assets_js"] = AssetsJs
beegoTplFuncMap["assets_css"] = AssetsCss beegoTplFuncMap["assets_css"] = AssetsCss
beegoTplFuncMap["config"] = Config
// go1.2 added template funcs // go1.2 added template funcs
// Comparisons // Comparisons

View File

@ -131,6 +131,43 @@ func Compare(a, b interface{}) (equal bool) {
return return
} }
func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
switch returnType {
case "String":
value = AppConfig.String(key)
case "Bool":
value, err = AppConfig.Bool(key)
case "Int":
value, err = AppConfig.Int(key)
case "Int64":
value, err = AppConfig.Int64(key)
case "Float":
value, err = AppConfig.Float(key)
case "DIY":
value, err = AppConfig.DIY(key)
default:
err = errors.New("Config keys must be of type String, Bool, Int, Int64, Float, or DIY!")
}
if err != nil {
if reflect.TypeOf(returnType) != reflect.TypeOf(defaultVal) {
err = errors.New("defaultVal type does not match returnType!")
} else {
value, err = defaultVal, nil
}
} else if reflect.TypeOf(value).Kind() == reflect.String {
if value == "" {
if reflect.TypeOf(defaultVal).Kind() != reflect.String {
err = errors.New("defaultVal type must be a String if the returnType is a String")
} else {
value = defaultVal.(string)
}
}
}
return
}
// Convert string to template.HTML type. // Convert string to template.HTML type.
func Str2html(raw string) template.HTML { func Str2html(raw string) template.HTML {
return template.HTML(raw) return template.HTML(raw)
@ -202,7 +239,7 @@ func Htmlunquote(src string) string {
// //
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md // more detail http://beego.me/docs/mvc/controller/urlbuilding.md
func UrlFor(endpoint string, values ...string) string { func UrlFor(endpoint string, values ...string) string {
return BeeApp.UrlFor(endpoint, values...) return BeeApp.Handlers.UrlFor(endpoint, values...)
} }
// returns script tag with src string. // returns script tag with src string.

View File

@ -36,25 +36,27 @@ func TestHtml2str(t *testing.T) {
func TestDateFormat(t *testing.T) { func TestDateFormat(t *testing.T) {
ts := "Mon, 01 Jul 2013 13:27:42 CST" ts := "Mon, 01 Jul 2013 13:27:42 CST"
tt, _ := time.Parse(time.RFC1123, ts) tt, _ := time.Parse(time.RFC1123, ts)
if DateFormat(tt, "2006-01-02 15:04:05") != "2013-07-01 13:27:42" {
t.Error("should be equal") if ss := DateFormat(tt, "2006-01-02 15:04:05"); ss != "2013-07-01 13:27:42" {
t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
} }
} }
func TestDate(t *testing.T) { func TestDate(t *testing.T) {
ts := "Mon, 01 Jul 2013 13:27:42 CST" ts := "Mon, 01 Jul 2013 13:27:42 CST"
tt, _ := time.Parse(time.RFC1123, ts) tt, _ := time.Parse(time.RFC1123, ts)
if Date(tt, "Y-m-d H:i:s") != "2013-07-01 13:27:42" {
t.Error("should be equal") if ss := Date(tt, "Y-m-d H:i:s"); ss != "2013-07-01 13:27:42" {
t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
} }
if Date(tt, "y-n-j h:i:s A") != "13-7-1 01:27:42 PM" { if ss := Date(tt, "y-n-j h:i:s A"); ss != "13-7-1 01:27:42 PM" {
t.Error("should be equal") t.Errorf("13-7-1 01:27:42 PM does not equal %v", ss)
} }
if Date(tt, "D, d M Y g:i:s a") != "Mon, 01 Jul 2013 1:27:42 pm" { if ss := Date(tt, "D, d M Y g:i:s a"); ss != "Mon, 01 Jul 2013 1:27:42 pm" {
t.Error("should be equal") t.Errorf("Mon, 01 Jul 2013 1:27:42 pm does not equal %v", ss)
} }
if Date(tt, "l, d F Y G:i:s") != "Monday, 01 July 2013 13:27:42" { if ss := Date(tt, "l, d F Y G:i:s"); ss != "Monday, 01 July 2013 13:27:42" {
t.Error("should be equal") t.Errorf("Monday, 01 July 2013 13:27:42 does not equal %v", ss)
} }
} }

467
tree.go Normal file
View File

@ -0,0 +1,467 @@
package beego
import (
"path"
"regexp"
"strings"
"github.com/astaxie/beego/utils"
)
type Tree struct {
//search fix route first
fixrouters map[string]*Tree
//if set, failure to match fixrouters search then search wildcard
wildcard *Tree
//if set, failure to match wildcard search
leaves []*leafInfo
}
func NewTree() *Tree {
return &Tree{
fixrouters: make(map[string]*Tree),
}
}
// add Tree to the exist Tree
// prefix should has no params
func (t *Tree) AddTree(prefix string, tree *Tree) {
t.addtree(splitPath(prefix), tree, nil, "")
}
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) {
if len(segments) == 0 {
panic("prefix should has path")
}
seg := segments[0]
iswild, params, regexpStr := splitSegment(seg)
if len(segments) == 1 && seg != "" {
if iswild {
wildcards = append(wildcards, params...)
if regexpStr != "" {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr
}
}
reg = reg + regexpStr
filterTreeWithPrefix(tree, wildcards, reg)
t.wildcard = tree
} else {
filterTreeWithPrefix(tree, wildcards, reg)
t.fixrouters[seg] = tree
}
return
}
if iswild {
if t.wildcard == nil {
t.wildcard = NewTree()
}
wildcards = append(wildcards)
if regexpStr != "" {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr
}
}
reg = reg + regexpStr
t.wildcard.addtree(segments[1:], tree, wildcards, reg)
} else {
subTree := NewTree()
t.fixrouters[seg] = subTree
subTree.addtree(segments[1:], tree, wildcards, reg)
}
}
func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
for _, v := range t.fixrouters {
filterTreeWithPrefix(v, wildcards, reg)
}
if t.wildcard != nil {
filterTreeWithPrefix(t.wildcard, wildcards, reg)
}
for _, l := range t.leaves {
l.wildcards = append(wildcards, l.wildcards...)
if reg != "" {
filterCards := []string{}
for _, v := range l.wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
l.wildcards = filterCards
l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
} else {
if l.regexps != nil {
filterCards := []string{}
for _, v := range l.wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
l.wildcards = filterCards
for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
reg = "([^/]+)/" + reg
}
l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
}
}
}
}
// call addseg function
func (t *Tree) AddRouter(pattern string, runObject interface{}) {
t.addseg(splitPath(pattern), runObject, nil, "")
}
// "/"
// "admin" ->
func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) {
if len(segments) == 0 {
if reg != "" {
filterCards := []string{}
for _, v := range wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")})
} else {
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards})
}
} else {
seg := segments[0]
iswild, params, regexpStr := splitSegment(seg)
if iswild {
if t.wildcard == nil {
t.wildcard = NewTree()
}
if regexpStr != "" {
if reg == "" {
for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr
}
} else {
regexpStr = "/" + regexpStr
}
} else if reg != "" {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "/([^/]+)" + regexpStr
}
}
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
} else {
subTree, ok := t.fixrouters[seg]
if !ok {
subTree = NewTree()
t.fixrouters[seg] = subTree
}
subTree.addseg(segments[1:], route, wildcards, reg)
}
}
}
// match router to runObject & params
func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) {
if len(pattern) == 0 || pattern[0] != '/' {
return nil, nil
}
return t.match(splitPath(pattern), nil)
}
func (t *Tree) match(segments []string, wildcardValues []string) (runObject interface{}, params map[string]string) {
// Handle leaf nodes:
if len(segments) == 0 {
for _, l := range t.leaves {
if ok, pa := l.match(wildcardValues); ok {
return l.runObject, pa
}
}
if t.wildcard != nil {
for _, l := range t.wildcard.leaves {
if ok, pa := l.match(wildcardValues); ok {
return l.runObject, pa
}
}
}
return nil, nil
}
seg, segs := segments[0], segments[1:]
subTree, ok := t.fixrouters[seg]
if ok {
runObject, params = subTree.match(segs, wildcardValues)
} else if len(segs) == 0 { //.json .xml
if subindex := strings.LastIndex(seg, "."); subindex != -1 {
subTree, ok = t.fixrouters[seg[:subindex]]
if ok {
runObject, params = subTree.match(segs, wildcardValues)
if runObject != nil {
if params == nil {
params = make(map[string]string)
}
params[":ext"] = seg[subindex+1:]
return runObject, params
}
}
}
}
if runObject == nil && t.wildcard != nil {
runObject, params = t.wildcard.match(segs, append(wildcardValues, seg))
}
if runObject == nil {
for _, l := range t.leaves {
if ok, pa := l.match(append(wildcardValues, segments...)); ok {
return l.runObject, pa
}
}
}
return runObject, params
}
type leafInfo struct {
// names of wildcards that lead to this leaf. eg, ["id" "name"] for the wildcard ":id" and ":name"
wildcards []string
// if the leaf is regexp
regexps *regexp.Regexp
runObject interface{}
}
func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string]string) {
if leaf.regexps == nil {
// has error
if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
if utils.InSlice(":", leaf.wildcards) {
params = make(map[string]string)
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
params[v] = ""
j += 1
}
return true, params
}
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
params = make(map[string]string)
params[":splat"] = ""
return true, params
}
Error("bug of router")
return false, nil
} else if len(wildcardValues) == 0 { // static path
return true, nil
}
// match *
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
params = make(map[string]string)
params[":splat"] = path.Join(wildcardValues...)
return true, params
}
// match *.*
if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." {
params = make(map[string]string)
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
params[":ext"] = strs[1]
} else {
params[":ext"] = ""
}
params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0]
return true, params
}
// match :id
params = make(map[string]string)
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
if v == "." {
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
params[":ext"] = strs[1]
} else {
params[":ext"] = ""
}
if len(wildcardValues[j:]) == 1 {
params[":path"] = strs[0]
} else {
params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
}
return true, params
}
params[v] = wildcardValues[j]
j += 1
}
if len(params) != len(wildcardValues) {
return false, nil
}
return true, params
}
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
return false, nil
}
params = make(map[string]string)
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
for i, match := range matches[1:] {
params[leaf.wildcards[i]] = match
}
return true, params
}
// "/" -> []
// "/admin" -> ["admin"]
// "/admin/" -> ["admin"]
// "/admin/users" -> ["admin", "users"]
func splitPath(key string) []string {
elements := strings.Split(key, "/")
if elements[0] == "" {
elements = elements[1:]
}
if elements[len(elements)-1] == "" {
elements = elements[:len(elements)-1]
}
return elements
}
// "admin" -> false, nil, ""
// ":id" -> true, [:id], ""
// "?:id" -> true, [: :id], "" : meaning can empty
// ":id:int" -> true, [:id], ([0-9]+)
// ":name:string" -> true, [:name], ([\w]+)
// ":id([0-9]+)" -> true, [:id], ([0-9]+)
// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+)
// "cms_:id_:page.html" -> true, [:id :page], cms_(.+)_(.+).html
// "*" -> true, [:splat], ""
// "*.*" -> true,[. :path :ext], "" . meaning separator
func splitSegment(key string) (bool, []string, string) {
if strings.HasPrefix(key, "*") {
if key == "*.*" {
return true, []string{".", ":path", ":ext"}, ""
} else {
return true, []string{":splat"}, ""
}
}
if strings.ContainsAny(key, ":") {
var paramsNum int
var out []rune
var start bool
var startexp bool
var param []rune
var expt []rune
var skipnum int
params := []string{}
reg := regexp.MustCompile(`[a-zA-Z0-9_]+`)
for i, v := range key {
if skipnum > 0 {
skipnum -= 1
continue
}
if start {
//:id:int and :name:string
if v == ':' {
if len(key) >= i+4 {
if key[i+1:i+4] == "int" {
out = append(out, []rune("([0-9]+)")...)
params = append(params, ":"+string(param))
start = false
startexp = false
skipnum = 3
param = make([]rune, 0)
paramsNum += 1
continue
}
}
if len(key) >= i+7 {
if key[i+1:i+7] == "string" {
out = append(out, []rune(`([\w]+)`)...)
params = append(params, ":"+string(param))
paramsNum += 1
start = false
startexp = false
skipnum = 6
param = make([]rune, 0)
continue
}
}
}
// params only support a-zA-Z0-9
if reg.MatchString(string(v)) {
param = append(param, v)
continue
}
if v != '(' {
out = append(out, []rune(`(.+)`)...)
params = append(params, ":"+string(param))
param = make([]rune, 0)
paramsNum += 1
start = false
startexp = false
}
}
if startexp {
if v != ')' {
expt = append(expt, v)
continue
}
}
if v == ':' {
param = make([]rune, 0)
start = true
} else if v == '(' {
startexp = true
start = false
params = append(params, ":"+string(param))
paramsNum += 1
expt = make([]rune, 0)
expt = append(expt, '(')
} else if v == ')' {
startexp = false
expt = append(expt, ')')
out = append(out, expt...)
param = make([]rune, 0)
} else if v == '?' {
params = append(params, ":")
} else {
out = append(out, v)
}
}
if len(param) > 0 {
if paramsNum > 0 {
out = append(out, []rune(`(.+)`)...)
}
params = append(params, ":"+string(param))
}
return true, params, string(out)
} else {
return false, nil, ""
}
}

179
tree_test.go Normal file
View File

@ -0,0 +1,179 @@
package beego
import "testing"
type testinfo struct {
url string
requesturl string
params map[string]string
}
var routers []testinfo
func init() {
routers = make([]testinfo, 0)
routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
routers = append(routers, testinfo{"/", "/", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
routers = append(routers, testinfo{"/customer/*", "/customer", map[string]string{":splat": ""}})
routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
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{"/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"}})
routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
}
func TestTreeRouters(t *testing.T) {
for _, r := range routers {
tr := NewTree()
tr.AddRouter(r.url, "astaxie")
obj, param := tr.Match(r.requesturl)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal(r.url + " can't get obj ")
}
if r.params != nil {
for k, v := range r.params {
if vv, ok := param[k]; !ok {
t.Fatal(r.url + r.requesturl + " get param empty:" + k)
} else if vv != v {
t.Fatal(r.url + " " + r.requesturl + " should be:" + v + " get param:" + vv)
}
}
}
}
}
func TestAddTree(t *testing.T) {
tr := NewTree()
tr.AddRouter("/shop/:id/account", "astaxie")
tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
t1 := NewTree()
t1.AddTree("/v1/zl", tr)
obj, param := t1.Match("/v1/zl/shop/123/account")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl/shop/:id/account can't get obj ")
}
if param == nil {
t.Fatal("get param error")
}
if param[":id"] != "123" {
t.Fatal("get :id param error")
}
obj, param = t1.Match("/v1/zl/shop/123/ttt_1_12.html")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
}
if param == nil {
t.Fatal("get param error")
}
if param[":sd"] != "123" || param[":id"] != "1" || param[":page"] != "12" {
t.Fatal("get :sd :id :page param error")
}
t2 := NewTree()
t2.AddTree("/v1/:shopid", tr)
obj, param = t2.Match("/v1/zl/shop/123/account")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
}
if param == nil {
t.Fatal("get param error")
}
if param[":id"] != "123" || param[":shopid"] != "zl" {
t.Fatal("get :id :shopid param error")
}
obj, param = t2.Match("/v1/zl/shop/123/ttt_1_12.html")
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
}
if param == nil {
t.Fatal("get :shopid param error")
}
if param[":sd"] != "123" || param[":id"] != "1" || param[":page"] != "12" || param[":shopid"] != "zl" {
t.Fatal("get :sd :id :page :shopid param error")
}
}
func TestSplitPath(t *testing.T) {
a := splitPath("/")
if len(a) != 0 {
t.Fatal("/ should retrun []")
}
a = splitPath("/admin")
if len(a) != 1 || a[0] != "admin" {
t.Fatal("/admin should retrun [admin]")
}
a = splitPath("/admin/")
if len(a) != 1 || a[0] != "admin" {
t.Fatal("/admin/ should retrun [admin]")
}
a = splitPath("/admin/users")
if len(a) != 2 || a[0] != "admin" || a[1] != "users" {
t.Fatal("/admin should retrun [admin users]")
}
a = splitPath("/admin/:id:int")
if len(a) != 2 || a[0] != "admin" || a[1] != ":id:int" {
t.Fatal("/admin should retrun [admin :id:int]")
}
}
func TestSplitSegment(t *testing.T) {
b, w, r := splitSegment("admin")
if b || len(w) != 0 || r != "" {
t.Fatal("admin should return false, nil, ''")
}
b, w, r = splitSegment("*")
if !b || len(w) != 1 || w[0] != ":splat" || r != "" {
t.Fatal("* should return true, [:splat], ''")
}
b, w, r = splitSegment("*.*")
if !b || len(w) != 3 || w[1] != ":path" || w[2] != ":ext" || w[0] != "." || r != "" {
t.Fatal("admin should return true,[. :path :ext], ''")
}
b, w, r = splitSegment(":id")
if !b || len(w) != 1 || w[0] != ":id" || r != "" {
t.Fatal(":id should return true, [:id], ''")
}
b, w, r = splitSegment("?:id")
if !b || len(w) != 2 || w[0] != ":" || w[1] != ":id" || r != "" {
t.Fatal("?:id should return true, [: :id], ''")
}
b, w, r = splitSegment(":id:int")
if !b || len(w) != 1 || w[0] != ":id" || r != "([0-9]+)" {
t.Fatal(":id:int should return true, [:id], '([0-9]+)'")
}
b, w, r = splitSegment(":name:string")
if !b || len(w) != 1 || w[0] != ":name" || r != `([\w]+)` {
t.Fatal(`:name:string should return true, [:name], '([\w]+)'`)
}
b, w, r = splitSegment(":id([0-9]+)")
if !b || len(w) != 1 || w[0] != ":id" || r != `([0-9]+)` {
t.Fatal(`:id([0-9]+) should return true, [:id], '([0-9]+)'`)
}
b, w, r = splitSegment(":id([0-9]+)_:name")
if !b || len(w) != 2 || w[0] != ":id" || w[1] != ":name" || r != `([0-9]+)_(.+)` {
t.Fatal(`:id([0-9]+)_:name should return true, [:id :name], '([0-9]+)_(.+)'`)
}
b, w, r = splitSegment(":id(.+)_cms.html")
if !b || len(w) != 1 || w[0] != ":id" || r != `(.+)_cms.html` {
t.Fatal(":id_cms.html should return true, [:id], '(.+)_cms.html'")
}
b, w, r = splitSegment("cms_:id(.+)_:page(.+).html")
if !b || len(w) != 2 || w[0] != ":id" || w[1] != ":page" || r != `cms_(.+)_(.+).html` {
t.Fatal(":id_cms.html should return true, [:id :page], cms_(.+)_(.+).html")
}
}

View File

@ -248,7 +248,7 @@ func NewWithFilter(urlPrefix string, store cache.Cache) *Captcha {
cpt := NewCaptcha(urlPrefix, store) cpt := NewCaptcha(urlPrefix, store)
// create filter for serve captcha image // create filter for serve captcha image
beego.AddFilter(cpt.URLPrefix+":", "BeforeRouter", cpt.Handler) beego.InsertFilter(cpt.URLPrefix+"*", beego.BeforeRouter, cpt.Handler)
// add to template func map // add to template func map
beego.AddFuncMap("create_captcha", cpt.CreateCaptchaHtml) beego.AddFuncMap("create_captcha", cpt.CreateCaptchaHtml)

View File

@ -64,24 +64,30 @@ func GrepFile(patten string, filename string) (lines []string, err error) {
lines = make([]string, 0) lines = make([]string, 0)
reader := bufio.NewReader(fd) reader := bufio.NewReader(fd)
prefix := "" prefix := ""
isLongLine := false
for { for {
byteLine, isPrefix, er := reader.ReadLine() byteLine, isPrefix, er := reader.ReadLine()
if er != nil && er != io.EOF { if er != nil && er != io.EOF {
return nil, er return nil, er
} }
if er == io.EOF {
break
}
line := string(byteLine) line := string(byteLine)
if isPrefix { if isPrefix {
prefix += line prefix += line
continue continue
} else {
isLongLine = true
} }
line = prefix + line line = prefix + line
if isLongLine {
prefix = ""
}
if re.MatchString(line) { if re.MatchString(line) {
lines = append(lines, line) lines = append(lines, line)
} }
if er == io.EOF {
break
}
} }
return lines, nil return lines, nil
} }

View File

@ -8,18 +8,32 @@ package utils
import ( import (
"crypto/rand" "crypto/rand"
r "math/rand"
"time"
) )
// RandomCreateBytes generate random []byte by specify chars. // RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte { func RandomCreateBytes(n int, alphabets ...byte) []byte {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var bytes = make([]byte, n) var bytes = make([]byte, n)
rand.Read(bytes) var randby bool
if num, err := rand.Read(bytes); num != n || err != nil {
r.Seed(time.Now().UnixNano())
randby = true
}
for i, b := range bytes { for i, b := range bytes {
if len(alphabets) == 0 { if len(alphabets) == 0 {
bytes[i] = alphanum[b%byte(len(alphanum))] if randby {
bytes[i] = alphanum[r.Intn(len(alphanum))]
} else {
bytes[i] = alphanum[b%byte(len(alphanum))]
}
} else { } else {
bytes[i] = alphabets[b%byte(len(alphabets))] if randby {
bytes[i] = alphabets[r.Intn(len(alphabets))]
} else {
bytes[i] = alphabets[b%byte(len(alphabets))]
}
} }
} }
return bytes return bytes