1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 21:21:01 +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, "HttpAddr:", HttpAddr)
fmt.Fprintln(rw, "HttpPort:", HttpPort)
fmt.Fprintln(rw, "HttpTLS:", HttpTLS)
fmt.Fprintln(rw, "HttpTLS:", EnableHttpTLS)
fmt.Fprintln(rw, "HttpCertFile:", HttpCertFile)
fmt.Fprintln(rw, "HttpKeyFile:", HttpKeyFile)
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, "EnableGzip:", EnableGzip)
fmt.Fprintln(rw, "DirectoryIndex:", DirectoryIndex)
fmt.Fprintln(rw, "EnableHotUpdate:", EnableHotUpdate)
fmt.Fprintln(rw, "HttpServerTimeOut:", HttpServerTimeOut)
fmt.Fprintln(rw, "ErrorsShow:", ErrorsShow)
fmt.Fprintln(rw, "XSRFKEY:", XSRFKEY)
@ -122,28 +121,13 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort)
case "router":
fmt.Fprintln(rw, "Print all router infomation:")
for _, router := range BeeApp.Handlers.fixrouters {
if router.hasMethod {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
} else {
fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
}
}
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())
}
}
for method, t := range BeeApp.Handlers.routers {
fmt.Fprintln(rw)
fmt.Fprintln(rw)
fmt.Fprintln(rw, " Method:", method)
printTree(rw, t)
}
// @todo print routers
case "filter":
fmt.Fprintln(rw, "Print all filter infomation:")
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, "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:")
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
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.
// it's in url pattern "/prof" in admin module.
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) {
for name, h := range toolbox.AdminCheckList {
if err := h.Check(); err != nil {
fmt.Fprintf(rw, "%s : ok\n", name)
} else {
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.
type App struct {
Handlers *ControllerRegistor
Server *http.Server
}
// NewApp returns a new beego application.
func NewApp() *App {
cr := NewControllerRegistor()
app := &App{Handlers: cr}
cr := NewControllerRegister()
app := &App{Handlers: cr, Server: &http.Server{}}
return app
}
@ -45,6 +46,7 @@ func (app *App) Run() {
err error
l net.Listener
)
endRunning := make(chan bool, 1)
if UseFcgi {
if HttpPort == 0 {
@ -57,180 +59,36 @@ func (app *App) Run() {
}
err = fcgi.Serve(l, app.Handlers)
} else {
if EnableHotUpdate {
server := &http.Server{
Handler: app.Handlers,
ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
}
laddr, err := net.ResolveTCPAddr("tcp", addr)
if nil != err {
BeeLogger.Critical("ResolveTCPAddr:", err)
}
l, err = GetInitListener(laddr)
if err == nil {
theStoppable = newStoppable(l)
err = server.Serve(theStoppable)
if err == nil {
theStoppable.wg.Wait()
err = CloseSelf()
app.Server.Addr = addr
app.Server.Handler = app.Handlers
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second
if EnableHttpTLS {
go func() {
if HttpsPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
}
}
} else {
s := &http.Server{
Addr: addr,
Handler: app.Handlers,
ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
}
if HttpTLS {
err = s.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
} else {
err = s.ListenAndServe()
}
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
if EnableHttpListen {
go func() {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}()
}
}
if err != nil {
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
<-endRunning
}

166
beego.go
View File

@ -19,7 +19,7 @@ import (
)
// beego web framework version.
const VERSION = "1.2.0"
const VERSION = "1.3.0"
type hookfunc func() error //hook function to run
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 {
for _, v := range groups {
if v.pattern == "" {
BeeApp.AutoRouterWithPrefix(prefix, v.controller)
BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
} else if v.mappingMethods != "" {
BeeApp.Router(prefix+v.pattern, v.controller, v.mappingMethods)
BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
} 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.
// 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 {
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
}
@ -109,69 +155,109 @@ func RESTRouter(rootpath string, c ControllerInterface) *App {
// AutoRouter adds defined controller handler to BeeApp.
// 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 {
BeeApp.AutoRouter(c)
BeeApp.Handlers.AddAuto(c)
return BeeApp
}
// AutoPrefix adds controller handler to BeeApp with prefix.
// 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 {
BeeApp.AutoRouterWithPrefix(prefix, c)
BeeApp.Handlers.AddAutoPrefix(prefix, c)
return BeeApp
}
// register router for Get method
// usage:
// beego.Get("/", func(ctx *context.Context){
// ctx.Output.Body("hello world")
// })
func Get(rootpath string, f FilterFunc) *App {
BeeApp.Get(rootpath, f)
BeeApp.Handlers.Get(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Post(rootpath, f)
BeeApp.Handlers.Post(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Delete(rootpath, f)
BeeApp.Handlers.Delete(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Put(rootpath, f)
BeeApp.Handlers.Put(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Head(rootpath, f)
BeeApp.Handlers.Head(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Options(rootpath, f)
BeeApp.Handlers.Options(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Patch(rootpath, f)
BeeApp.Handlers.Patch(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Any(rootpath, f)
BeeApp.Handlers.Any(rootpath, f)
return BeeApp
}
// 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 {
BeeApp.Handler(rootpath, h, options...)
BeeApp.Handlers.Handler(rootpath, h, options...)
return BeeApp
}
@ -184,15 +270,14 @@ func Errorhandler(err string, h http.HandlerFunc) *App {
return BeeApp
}
// SetViewsPath sets view directory to BeeApp.
// it's alias of App.SetViewsPath.
// SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App {
BeeApp.SetViewsPath(path)
ViewsPath = path
return BeeApp
}
// SetStaticPath sets static directory and url prefix to BeeApp.
// it's alias of App.SetStaticPath.
// 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".
func SetStaticPath(url string, path string) *App {
if !strings.HasPrefix(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.
// it's alias of App.DelStaticPath.
func DelStaticPath(url string) *App {
delete(StaticDir, url)
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.
// The pos means action constant including
// 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 {
BeeApp.InsertFilter(pattern, pos, filter)
BeeApp.Handlers.InsertFilter(pattern, pos, filter)
return BeeApp
}
@ -234,8 +308,19 @@ func AddAPPStartHook(hf hookfunc) {
}
// Run beego application.
// it's alias of App.Run.
func Run() {
// beego.Run() default run on HttpPort
// 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()
if EnableAdmin {
@ -270,7 +355,7 @@ func initBeforeHttpRun() {
sessionConfig = `{"cookieName":"` + SessionName + `",` +
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + SessionSavePath + `",` +
`"secure":` + strconv.FormatBool(HttpTLS) + `,` +
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
@ -294,10 +379,19 @@ func initBeforeHttpRun() {
middleware.VERSION = VERSION
middleware.AppName = AppName
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) {
AppPath = apppath
RunMode = "test"
AppConfigPath = filepath.Join(AppPath, "conf", "app.conf")
err := ParseConfig()
if err != nil && !os.IsNotExist(err) {

227
config.go
View File

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

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) {
f, err := os.Create("testjson.conf")
if err != nil {
@ -100,4 +147,28 @@ func TestJson(t *testing.T) {
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 {
ips := input.Proxy()
if len(ips) > 0 && ips[0] != "" {
return ips[0]
rip := strings.Split(ips[0], ":")
return rip[0]
}
ip := strings.Split(input.Request.RemoteAddr, ":")
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.
// 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-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("Expires", "0")
output.Header("Cache-Control", "must-revalidate")

View File

@ -28,15 +28,24 @@ import (
//commonly used mime-types
const (
applicationJson = "application/json"
applicationXml = "applicatoin/xml"
applicationXml = "application/xml"
textXml = "text/xml"
)
var (
// 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
// http context, template and view, session and xsrf.
type Controller struct {
@ -55,6 +64,7 @@ type Controller struct {
AppController interface{}
EnableRender bool
EnableXSRF bool
methodMapping map[string]func() //method:routertree
}
// ControllerInterface is an interface to uniform all controller handler.
@ -72,6 +82,8 @@ type ControllerInterface interface {
Render() error
XsrfToken() string
CheckXsrfCookie() bool
HandlerFunc(fn string) bool
URLMapping()
}
// 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.EnableXSRF = true
c.Data = ctx.Input.Data
c.methodMapping = make(map[string]func())
}
// 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)
}
// 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.
func (c *Controller) Render() error {
if !c.EnableRender {
@ -295,7 +326,6 @@ func (c *Controller) ServeXml() {
}
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept")
switch accept {
@ -452,7 +482,7 @@ func (c *Controller) XsrfToken() string {
} else {
expire = int64(XSRFExpire)
}
token = string(utils.RandomCreateBytes(15))
token = string(utils.RandomCreateBytes(32))
c.SetSecureCookie(XSRFKEY, "_xsrf", token, expire)
}
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
}
switch op {
case websocket.OpPong:
case websocket.PongMessage:
c.ws.SetReadDeadline(time.Now().Add(readWait))
case websocket.OpText:
case websocket.TextMessage:
message, err := ioutil.ReadAll(r)
if err != nil {
break
@ -89,14 +89,14 @@ func (c *connection) writePump() {
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.OpClose, []byte{})
c.write(websocket.CloseMessage, []byte{})
return
}
if err := c.write(websocket.OpText, message); err != nil {
if err := c.write(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.OpPing, []byte{}); err != nil {
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
@ -149,8 +149,13 @@ type WSController struct {
beego.Controller
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
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 {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return

152
filter.go
View File

@ -6,154 +6,24 @@
package beego
import (
"regexp"
"strings"
)
// FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives.
type FilterRouter struct {
pattern string
regex *regexp.Regexp
filterFunc FilterFunc
hasregex bool
params map[int]string
parseParams map[string]string
filterFunc FilterFunc
tree *Tree
pattern string
}
// ValidRouter check current request is valid for this filter.
// if matched, returns parsed params in this request by defined filter router pattern.
func (mr *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
if mr.pattern == "" {
return true, nil
func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
isok, params := f.tree.Match(router)
if isok == nil {
return false, nil
}
if mr.pattern == "*" {
return true, nil
if isok, ok := isok.(bool); ok {
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) {
r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler.AddFilter("/person/:last/:first", "AfterStatic", FilterUser)
handler := NewControllerRegister()
handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
handler.Add("/person/:last/:first", &TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "i am astaXie" {
@ -40,8 +40,8 @@ var FilterAdminUser = func(ctx *context.Context) {
func TestPatternTwo(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser)
handler := NewControllerRegister()
handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/ can't run")
@ -51,8 +51,8 @@ func TestPatternTwo(t *testing.T) {
func TestPatternThree(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser)
handler := NewControllerRegister()
handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/astaxie can't run")

View File

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

View File

@ -16,22 +16,45 @@ import (
"mime/multipart"
"net"
"net/http"
"net/http/cookiejar"
"net/http/httputil"
"net/url"
"os"
"strings"
"sync"
"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.
func Get(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "GET"
req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent)
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
}
// Post returns *BeegoHttpRequest with POST method.
@ -39,8 +62,7 @@ func Post(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "POST"
req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent)
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
}
// Put returns *BeegoHttpRequest with PUT method.
@ -48,8 +70,7 @@ func Put(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "PUT"
req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent)
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
}
// Delete returns *BeegoHttpRequest DELETE GET method.
@ -57,8 +78,7 @@ func Delete(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "DELETE"
req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent)
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
}
// Head returns *BeegoHttpRequest with HEAD method.
@ -66,40 +86,64 @@ func Head(url string) *BeegoHttpRequest {
var req http.Request
req.Method = "HEAD"
req.Header = http.Header{}
req.Header.Set("User-Agent", defaultUserAgent)
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting}
}
// 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.
type BeegoHttpRequest struct {
url string
req *http.Request
params map[string]string
files map[string]string
showdebug bool
connectTimeout time.Duration
readWriteTimeout time.Duration
tlsClientConfig *tls.Config
proxy func(*http.Request) (*url.URL, error)
transport http.RoundTripper
url string
req *http.Request
params map[string]string
files map[string]string
setting BeegoHttpSettings
}
// Change request settings
func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest {
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.
func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
b.showdebug = isdebug
b.setting.ShowDebug = isdebug
return b
}
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
b.connectTimeout = connectTimeout
b.readWriteTimeout = readWriteTimeout
b.setting.ConnectTimeout = connectTimeout
b.setting.ReadWriteTimeout = readWriteTimeout
return b
}
// SetTLSClientConfig sets tls connection configurations if visiting https url.
func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest {
b.tlsClientConfig = config
b.setting.TlsClientConfig = config
return b
}
@ -134,7 +178,7 @@ func (b *BeegoHttpRequest) SetCookie(cookie *http.Cookie) *BeegoHttpRequest {
// Set transport to
func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest {
b.transport = transport
b.setting.Transport = transport
return b
}
@ -146,7 +190,7 @@ func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpR
// return u, nil
// }
func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest {
b.proxy = proxy
b.setting.Proxy = proxy
return b
}
@ -242,7 +286,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
}
b.req.URL = url
if b.showdebug {
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, true)
if err != nil {
println(err.Error())
@ -250,32 +294,47 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
println(string(dump))
}
trans := b.transport
trans := b.setting.Transport
if trans == nil {
// create default transport
trans = &http.Transport{
TLSClientConfig: b.tlsClientConfig,
Proxy: b.proxy,
Dial: TimeoutDialer(b.connectTimeout, b.readWriteTimeout),
TLSClientConfig: b.setting.TlsClientConfig,
Proxy: b.setting.Proxy,
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
}
} else {
// if b.transport is *http.Transport then set the settings.
if t, ok := trans.(*http.Transport); ok {
if t.TLSClientConfig == nil {
t.TLSClientConfig = b.tlsClientConfig
t.TLSClientConfig = b.setting.TlsClientConfig
}
if t.Proxy == nil {
t.Proxy = b.proxy
t.Proxy = b.setting.Proxy
}
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{
Transport: trans,
Jar: jar,
}
if b.setting.UserAgent != "" {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}
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.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
@ -13,7 +13,7 @@ import (
)
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 {
t.Fatal(err)
}
@ -29,7 +29,7 @@ func TestGetUrl(t *testing.T) {
t.Fatal("data is no")
}
str, err := Get("http://beego.me/").String()
str, err := Get("http://beego.me").String()
if err != nil {
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.Param("username", "astaxie")
b.Param("password", "hello")
b.PostFile("uploadfile", "httplib.go")
b.PostFile("uploadfile", "httplib_test.go")
str, err := b.String()
if err != nil {
t.Fatal(err)
}
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 (
"net/http"
"strings"
beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
)
type namespaceCond func(*beecontext.Context) bool
type innnerNamespace func(*Namespace)
// Namespace is store all the info
type Namespace struct {
prefix string
condition namespaceCond
handlers *ControllerRegistor
prefix string
handlers *ControllerRegistor
}
func NewNamespace(prefix string) *Namespace {
cr := NewControllerRegistor()
return &Namespace{
// get new Namespace
func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
ns := &Namespace{
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 {
n.condition = cond
return n
}
func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace {
if action == "before" {
action = "BeforeRouter"
} else if action == "after" {
action = "FinishRouter"
fn := func(ctx *beecontext.Context) {
if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
}
}
if v, ok := n.handlers.filters[BeforeRouter]; ok {
mr := new(FilterRouter)
mr.tree = NewTree()
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
}
// 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 {
n.handlers.Add(rootpath, c, mappingMethods...)
return n
}
// same as beego.AutoRouter
// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
n.handlers.AddAuto(c)
return n
}
// same as beego.AutoPrefix
// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
n.handlers.AddAutoPrefix(prefix, c)
return n
}
// same as beego.Get
// refer: https://godoc.org/github.com/astaxie/beego#Get
func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
n.handlers.Get(rootpath, f)
return n
}
// same as beego.Post
// refer: https://godoc.org/github.com/astaxie/beego#Post
func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
n.handlers.Post(rootpath, f)
return n
}
// same as beego.Delete
// refer: https://godoc.org/github.com/astaxie/beego#Delete
func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
n.handlers.Delete(rootpath, f)
return n
}
// same as beego.Put
// refer: https://godoc.org/github.com/astaxie/beego#Put
func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
n.handlers.Put(rootpath, f)
return n
}
// same as beego.Head
// refer: https://godoc.org/github.com/astaxie/beego#Head
func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
n.handlers.Head(rootpath, f)
return n
}
// same as beego.Options
// refer: https://godoc.org/github.com/astaxie/beego#Options
func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
n.handlers.Options(rootpath, f)
return n
}
// same as beego.Patch
// refer: https://godoc.org/github.com/astaxie/beego#Patch
func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
n.handlers.Patch(rootpath, f)
return n
}
// same as beego.Any
// refer: https://godoc.org/github.com/astaxie/beego#Any
func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
n.handlers.Any(rootpath, f)
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 {
n.handlers.Handler(rootpath, h)
return n
}
func (n *Namespace) Namespace(ns *Namespace) *Namespace {
n.handlers.Handler(ns.prefix, ns, true)
// add include class
// refer: https://godoc.org/github.com/astaxie/beego#Include
func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
n.handlers.Include(cList...)
return n
}
func (n *Namespace) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
//trim the preifix from URL.Path
r.URL.Path = strings.TrimPrefix(r.URL.Path, n.prefix)
// init context
context := &beecontext.Context{
ResponseWriter: rw,
Request: r,
Input: beecontext.NewInput(r),
Output: beecontext.NewOutput(),
// nest Namespace
// usage:
//ns := beego.NewNamespace(“/v1”).
//Namespace(
// beego.NewNamespace("/shop").
// Get("/:id", func(ctx *context.Context) {
// ctx.Output.Body([]byte("shopinfo"))
// }),
// 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
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)
return n
}
// register Namespace into beego.Handler
// support multi Namespace
func AddNamespace(nl ...*Namespace) {
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) {
ctx.Output.Body([]byte("v1_user"))
})
ns.ServeHTTP(w, r)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "v1_user" {
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) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
ns.ServeHTTP(w, r)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
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"))
}),
)
ns.ServeHTTP(w, r)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "order" {
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")))
}),
)
ns.ServeHTTP(w, r)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" {
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) {
r, _ := http.NewRequest("GET", "/v1/user/123", nil)
w := httptest.NewRecorder()
@ -88,41 +118,18 @@ func TestNamespaceFilter(t *testing.T) {
Get("/user/:id", func(ctx *context.Context) {
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" {
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) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil)
r, _ := http.NewRequest("GET", "/v2/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns := NewNamespace("/v2")
ns.Cond(func(ctx *context.Context) bool {
if ctx.Input.Domain() == "beego.me" {
return true
@ -130,8 +137,27 @@ func TestNamespaceCond(t *testing.T) {
return false
}).
AutoRouter(&TestController{})
ns.ServeHTTP(w, r)
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Code != 405 {
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()
if t, ok := value.(time.Time); ok {
d.ins.TimeToDB(&t, tz)
value = t
if t.IsZero() {
value = nil
} else {
value = t
}
}
default:
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")))
}
func (this *TestController) Param() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
}
func (this *TestController) 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")))
}
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 {
Code int
Msg string
@ -63,21 +76,45 @@ func (this *JsonController) Get() {
}
func TestUrlFor(t *testing.T) {
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Add("/api/list", &TestController{}, "*:List")
handler.Add("/person/:last/:first", &TestController{})
handler.Add("/person/:last/:first", &TestController{}, "*:Param")
handler.AddAuto(&TestController{})
if handler.UrlFor("TestController.List") != "/api/list" {
Info(handler.UrlFor("TestController.List"))
t.Errorf("TestController.List must equal to /api/list")
}
if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" {
t.Errorf("TestController.Get must equal to /person/xie/asta")
if handler.UrlFor("TestController.Param", ":last", "xie", ":first", "asta") != "/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" {
t.Errorf("TestController.Myext must equal to /Test/Myext")
if handler.UrlFor("TestController.Myext") != "/test/myext" {
t.Errorf("TestController.Myext must equal to /test/myext")
}
if handler.UrlFor("TestController.GetUrl") != "/Test/GetUrl" {
t.Errorf("TestController.GetUrl must equal to /Test/GetUrl")
if handler.UrlFor("TestController.GetUrl") != "/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)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Add("/api/list", &TestController{}, "*:List")
handler.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
@ -97,7 +134,7 @@ func TestPostFunc(t *testing.T) {
r, _ := http.NewRequest("POST", "/astaxie", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Add("/:name", &TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "astaxie" {
@ -109,7 +146,7 @@ func TestAutoFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/test/list", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r)
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)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "20091112" {
@ -133,7 +170,7 @@ func TestAutoExtFunc(t *testing.T) {
r, _ := http.NewRequest("GET", "/test/myext.json", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.AddAuto(&TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "json" {
@ -146,22 +183,12 @@ func TestRouteOk(t *testing.T) {
r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler.Add("/person/:last/:first", &TestController{})
handler := NewControllerRegister()
handler.Add("/person/:last/:first", &TestController{}, "get:GetParams")
handler.ServeHTTP(w, r)
lastNameParam := r.URL.Query().Get(":last")
firstNameParam := r.URL.Query().Get(":first")
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")
body := w.Body.String()
if body != "anderson+thomas+kungfu" {
t.Errorf("url param set to [%s];", body)
}
}
@ -170,18 +197,14 @@ func TestManyRoute(t *testing.T) {
r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{})
handler := NewControllerRegister()
handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, "get:GetManyRouter")
handler.ServeHTTP(w, r)
id := r.URL.Query().Get(":id")
page := r.URL.Query().Get(":page")
body := w.Body.String()
if id != "32" {
t.Errorf("url param set to [%s]; want [%s]", id, "32")
}
if page != "12" {
t.Errorf("url param set to [%s]; want [%s]", page, "12")
if body != "3212" {
t.Errorf("url param set to [%s];", body)
}
}
@ -189,7 +212,7 @@ func TestNotFound(t *testing.T) {
r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.ServeHTTP(w, r)
if w.Code != http.StatusNotFound {
@ -203,7 +226,7 @@ func TestStatic(t *testing.T) {
r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.ServeHTTP(w, r)
if w.Code != 404 {
@ -215,7 +238,7 @@ func TestPrepare(t *testing.T) {
r, _ := http.NewRequest("GET", "/json/list", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Add("/json/list", &JsonController{})
handler.ServeHTTP(w, r)
if w.Body.String() != `"prepare"` {
@ -227,7 +250,7 @@ func TestAutoPrefix(t *testing.T) {
r, _ := http.NewRequest("GET", "/admin/test/list", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.AddAutoPrefix("/admin", &TestController{})
handler.ServeHTTP(w, r)
if w.Body.String() != "i am list" {
@ -239,7 +262,7 @@ func TestRouterGet(t *testing.T) {
r, _ := http.NewRequest("GET", "/user", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Get("/user", func(ctx *context.Context) {
ctx.Output.Body([]byte("Get userlist"))
})
@ -253,7 +276,7 @@ func TestRouterPost(t *testing.T) {
r, _ := http.NewRequest("POST", "/user/123", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Post("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
@ -271,10 +294,64 @@ func TestRouterHandler(t *testing.T) {
r, _ := http.NewRequest("POST", "/sayhi", nil)
w := httptest.NewRecorder()
handler := NewControllerRegistor()
handler := NewControllerRegister()
handler.Handler("/sayhi", http.HandlerFunc(sayhello))
handler.ServeHTTP(w, r)
if w.Body.String() != "sayhello" {
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"
"strconv"
"time"
"github.com/astaxie/beego/utils"
)
func init() {
@ -60,8 +62,8 @@ func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) {
// generateRandomKey creates a random key with the given strength.
func generateRandomKey(strength int) []byte {
k := make([]byte, strength)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return nil
if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil {
return utils.RandomCreateBytes(strength)
}
return k
}

View File

@ -18,6 +18,8 @@ import (
"net/http"
"net/url"
"time"
"github.com/astaxie/beego/utils"
)
// 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.
func (manager *Manager) sessionId(r *http.Request) (sid string) {
bs := make([]byte, 24)
if _, err := io.ReadFull(rand.Reader, bs); err != nil {
return ""
bs := make([]byte, 32)
if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil {
bs = utils.RandomCreateBytes(32)
}
sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs)
if manager.config.SessionIDHashFunc == "md5" {

View File

@ -18,7 +18,7 @@ import (
"github.com/astaxie/beego/utils"
)
func serverStaticRouter(ctx *context.Context) bool {
func serverStaticRouter(ctx *context.Context) {
requestPath := path.Clean(ctx.Input.Request.URL.Path)
for prefix, staticDir := range StaticDir {
if len(prefix) == 0 {
@ -28,17 +28,13 @@ func serverStaticRouter(ctx *context.Context) bool {
file := path.Join(staticDir, requestPath)
if utils.FileExists(file) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return true
return
}
}
if strings.HasPrefix(requestPath, prefix) {
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
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):])
finfo, err := os.Stat(file)
if err != nil {
@ -46,12 +42,12 @@ func serverStaticRouter(ctx *context.Context) bool {
Warn(err)
}
http.NotFound(ctx.ResponseWriter, ctx.Request)
return true
return
}
//if the request is dir and DirectoryIndex is false then
if finfo.IsDir() && !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
return true
return
}
//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)
if err != nil {
return true
return
}
if contentEncoding == "gzip" {
@ -89,8 +85,7 @@ func serverStaticRouter(ctx *context.Context) bool {
} else {
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["assets_js"] = AssetsJs
beegoTplFuncMap["assets_css"] = AssetsCss
beegoTplFuncMap["config"] = Config
// go1.2 added template funcs
// Comparisons

View File

@ -131,6 +131,43 @@ func Compare(a, b interface{}) (equal bool) {
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.
func Str2html(raw string) template.HTML {
return template.HTML(raw)
@ -202,7 +239,7 @@ func Htmlunquote(src string) string {
//
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md
func UrlFor(endpoint string, values ...string) string {
return BeeApp.UrlFor(endpoint, values...)
return BeeApp.Handlers.UrlFor(endpoint, values...)
}
// returns script tag with src string.

View File

@ -36,25 +36,27 @@ func TestHtml2str(t *testing.T) {
func TestDateFormat(t *testing.T) {
ts := "Mon, 01 Jul 2013 13:27:42 CST"
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) {
ts := "Mon, 01 Jul 2013 13:27:42 CST"
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" {
t.Error("should be equal")
if ss := Date(tt, "y-n-j h:i:s A"); ss != "13-7-1 01:27:42 PM" {
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" {
t.Error("should be equal")
if ss := Date(tt, "D, d M Y g:i:s a"); ss != "Mon, 01 Jul 2013 1:27:42 pm" {
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" {
t.Error("should be equal")
if ss := Date(tt, "l, d F Y G:i:s"); ss != "Monday, 01 July 2013 13:27:42" {
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)
// 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
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)
reader := bufio.NewReader(fd)
prefix := ""
isLongLine := false
for {
byteLine, isPrefix, er := reader.ReadLine()
if er != nil && er != io.EOF {
return nil, er
}
if er == io.EOF {
break
}
line := string(byteLine)
if isPrefix {
prefix += line
continue
} else {
isLongLine = true
}
line = prefix + line
if isLongLine {
prefix = ""
}
if re.MatchString(line) {
lines = append(lines, line)
}
if er == io.EOF {
break
}
}
return lines, nil
}

View File

@ -8,18 +8,32 @@ package utils
import (
"crypto/rand"
r "math/rand"
"time"
)
// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
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 {
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 {
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