mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 23:31:03 +00:00
Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
469f283b68 | |||
085c362ffb | |||
c3a07555c4 | |||
2b8e411174 | |||
720a77c1f9 | |||
b943b74fc5 | |||
67be7b532d | |||
8be83fe488 | |||
cff632f553 | |||
4990d88861 | |||
7075ad8a28 | |||
0e278ae358 | |||
e38a23b30e | |||
117904be73 | |||
3b807845f2 | |||
00b710e168 | |||
e25fcffbc0 | |||
c13141b8bf | |||
7a7ff735e3 | |||
3b934bb910 | |||
aa275fb5ce | |||
deb553be7f | |||
3db9633ebd | |||
2f8a70d548 | |||
7c0d0900ac | |||
6809c97611 | |||
675643c68d | |||
06f4bf493d | |||
4786fb0948 | |||
fdb5672b7a | |||
107a7a21c0 | |||
dbebf8df4b | |||
f7b01aab13 | |||
2570f075d9 | |||
21cb8ea4a3 | |||
6c8a7f1382 | |||
e00eab7f49 | |||
bfabcfcb6b | |||
f06ba52ede | |||
fcae000a79 | |||
3e4c015982 | |||
d689be30e8 | |||
7b110a0b73 | |||
e3033b57a6 | |||
bd537554ea | |||
ebb3b91df9 | |||
a65ad1a4bc | |||
bdc01f52a0 | |||
a673a85d4a | |||
61008fe75c | |||
5dee6b7d19 | |||
f6c7a6bd32 | |||
d2eece9a39 | |||
c3a23b28ee | |||
9083927c6a | |||
3f7e91e6a4 | |||
a2a6f47afa | |||
23229ef9ef | |||
0d17d974cd | |||
17104c25a2 | |||
8b374d7f90 | |||
fa3234147a | |||
33ad6c1370 | |||
04290dfc68 | |||
03080b3ef2 | |||
3f2a712ba8 | |||
f215aa4810 | |||
18a02d7d60 | |||
3f4d750dc4 | |||
9f01aeed31 | |||
b45f0b9bf6 | |||
cf04ade603 | |||
2c59ff1cc6 | |||
6bdf0838ce | |||
31a63c5d50 | |||
237aaadd65 | |||
34ddcef1dc | |||
f6ce2656db | |||
b647026dff | |||
568c0c47f0 | |||
2629de28f2 | |||
10d2c7c328 | |||
af7ac98bd6 | |||
6f78f1d4b2 | |||
9f95fd3309 | |||
74c309cefd | |||
29e113a48a | |||
3caf1896d6 | |||
d5d5f23756 | |||
46641ef3b6 | |||
b2a69f505c | |||
b2bd829d39 | |||
f9b8617fa3 | |||
6c6e4ecfbc | |||
8bcf03c652 | |||
1ea449aa3a | |||
b212ec8dab | |||
e50cbecf80 | |||
127b85bcaa | |||
89dde6cd9d | |||
5a52949761 | |||
a78162e9e4 | |||
931e6162ac | |||
1bb876f2df | |||
18f70e6ee4 | |||
4785ac14d7 |
60
admin.go
60
admin.go
@ -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
206
app.go
@ -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
166
beego.go
@ -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
227
config.go
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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
38
docs.go
Normal 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)
|
||||
}
|
@ -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
152
filter.go
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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==========================================")
|
||||
}
|
||||
|
321
namespace.go
321
namespace.go
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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
187
parser.go
Normal 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
169
reload.go
@ -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
|
||||
}
|
165
router_test.go
165
router_test.go
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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" {
|
||||
|
@ -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
133
swagger/docsSpec.go
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
467
tree.go
Normal 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
179
tree_test.go
Normal 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")
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user