mirror of
https://github.com/astaxie/beego.git
synced 2025-07-11 14:21:00 +00:00
Compare commits
204 Commits
Author | SHA1 | Date | |
---|---|---|---|
1c9898dee5 | |||
2cf7c6a58a | |||
2cee46ab2b | |||
020bfbcc9c | |||
3f8252bffd | |||
6d313aa15f | |||
2a4e2d4a71 | |||
f96a6285bf | |||
e938876c4a | |||
6e9d2dc965 | |||
3aceaf8838 | |||
71b9854f48 | |||
f59ccd3a35 | |||
181a7c35fe | |||
2ed272aeb2 | |||
77c1109134 | |||
29d4823866 | |||
24cf06d288 | |||
0c31c2d689 | |||
f988f035e5 | |||
1b4158c15b | |||
433e8f2ce3 | |||
22ba7fdce4 | |||
2a0f87e810 | |||
19db4b67f6 | |||
b1baf4503d | |||
d536f5b8dc | |||
1cc1d57f55 | |||
73370ade90 | |||
0d3b7dcd07 | |||
d7fe5ef435 | |||
8bd902814f | |||
378356a65e | |||
30871e2617 | |||
3731088b4a | |||
d46833c6d8 | |||
18659e16ba | |||
5d8187d005 | |||
d961ae4cd8 | |||
0e1a0049d1 | |||
0c933643e2 | |||
d2c5daa5ee | |||
d3ab157915 | |||
75d28d49c5 | |||
76bb4827d0 | |||
3caba06189 | |||
572508ddd8 | |||
e34f8479bb | |||
daf85f06f8 | |||
22671c524e | |||
ab99d5f1e2 | |||
c52f634d9c | |||
9c665afc04 | |||
77ed151243 | |||
29d98731c6 | |||
934dd2e8d2 | |||
e65d87974a | |||
db04c3cbb4 | |||
802aa16136 | |||
f2df07f630 | |||
dc89f844f3 | |||
b80cdef20f | |||
98dcee0643 | |||
0ad75cb5fa | |||
6a9d04c269 | |||
93ca11f83d | |||
d0b43ef4f5 | |||
c9bb9d6a09 | |||
f96245786a | |||
1a1b0c14b9 | |||
07c628c7e9 | |||
1e92d17605 | |||
bb795847da | |||
54ba307f7f | |||
950ff91d87 | |||
b43401b9f6 | |||
fe50269b3f | |||
000033e2a7 | |||
15242d89ce | |||
76522d43af | |||
52df1234bd | |||
fc6b9ce009 | |||
b9fdd67519 | |||
7743eecfd4 | |||
c4d8e4a244 | |||
9d4ec508bb | |||
8b747f54bc | |||
a2428af8a7 | |||
90a7ce5c6a | |||
ef3c7c127b | |||
88caf1ed70 | |||
304beaf89f | |||
2288ac868c | |||
8d797a4a5e | |||
10db97b193 | |||
716962672f | |||
90cff5f042 | |||
da127bbc22 | |||
945b1da3a8 | |||
94c84b846f | |||
db43892fe6 | |||
71149218d1 | |||
1636a7271c | |||
68c3bdfdd4 | |||
ecd0a5487e | |||
fda841208d | |||
57e62e5e57 | |||
824e3f8f5b | |||
1822dd95ac | |||
ddbfc25e56 | |||
1f26852610 | |||
0188fb3711 | |||
0bcd828d73 | |||
e11a27f1d1 | |||
90caeb4cf7 | |||
6c9249034d | |||
14114018ea | |||
6f5162461e | |||
c34c514bba | |||
8ac2b9bf66 | |||
2a85c79ce5 | |||
767083bd56 | |||
1a79513293 | |||
c6cb1f92e8 | |||
9c0aad06c5 | |||
180c6aafac | |||
710f5b6234 | |||
dbf944adce | |||
6c9ff81fc1 | |||
ec6383c07d | |||
76db5cded4 | |||
1b3e7de463 | |||
ab28edaf25 | |||
04431a7a15 | |||
b00c42b3df | |||
4cae7af3f9 | |||
e4988b714e | |||
24489df63d | |||
fb8e9ae1a3 | |||
1eb9aef687 | |||
efc14a1e8d | |||
fa1281002e | |||
812950b60d | |||
f9e991b538 | |||
fc07419938 | |||
d69eee23f0 | |||
41de7c7db6 | |||
6a33647f30 | |||
e5134873be | |||
8d20ea04b0 | |||
5c1e8e42b9 | |||
ca3e7568a1 | |||
2823167848 | |||
a27f5c0dc0 | |||
8af0475251 | |||
9c07332cfc | |||
a06e0f27ad | |||
a760e46f98 | |||
262665f4e5 | |||
0b3763cc67 | |||
c147f26cd1 | |||
1ba7847913 | |||
52df979aca | |||
b6f789c497 | |||
fa6cbc08d9 | |||
c4f8f45da4 | |||
6fca4a8218 | |||
aae89576c6 | |||
a907a86476 | |||
8716185de8 | |||
31e6133413 | |||
3a5de83ec2 | |||
f5f3395560 | |||
8164367762 | |||
e1475b72b9 | |||
f267ee8a12 | |||
7e060e6e5c | |||
727d2f9ea1 | |||
67c0c232a1 | |||
6c62198b59 | |||
e48e1ddaa9 | |||
1f9281c830 | |||
ccab9a7044 | |||
9013f5c6c7 | |||
29b7ff84e1 | |||
fb0cc55822 | |||
0820e21738 | |||
38eb29fa7b | |||
cca0a3f76d | |||
f9a9b5a905 | |||
c667895ce5 | |||
b2cdabb8a0 | |||
647a47517d | |||
f7cd1479ba | |||
fcc359af11 | |||
08b3e4191e | |||
4f4f7ce257 | |||
d06e02474f | |||
4d65330ca1 | |||
2dfe1fc61c | |||
29b60d6058 | |||
6eee223352 | |||
db51ddab96 | |||
baf2c63d5c |
16
.go_style
16
.go_style
@ -1,16 +0,0 @@
|
||||
{
|
||||
"file_line": 500,
|
||||
"func_line": 80,
|
||||
"params_num":4,
|
||||
"results_num":3,
|
||||
"formated": true,
|
||||
"pkg_name": true,
|
||||
"camel_name":true,
|
||||
"ignore":[
|
||||
"a/*",
|
||||
"b/*/c/*.go"
|
||||
],
|
||||
"fatal":[
|
||||
"formated"
|
||||
]
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
## Beego
|
||||
|
||||
[](https://drone.io/github.com/astaxie/beego/latest)
|
||||
[](https://godoc.org/github.com/astaxie/beego)
|
||||
[](http://godoc.org/github.com/astaxie/beego)
|
||||
|
||||
beego is an open-source, high-performance, modularity, full-stack web framework.
|
||||
beego is an open-source, high-performance, modular, full-stack web framework.
|
||||
|
||||
More info [beego.me](http://beego.me)
|
||||
|
||||
@ -19,7 +19,7 @@ More info [beego.me](http://beego.me)
|
||||
* Auto API documents
|
||||
* Annotation router
|
||||
* Namespace
|
||||
* Powerful develop tools
|
||||
* Powerful development tools
|
||||
* Full stack for Web & API
|
||||
|
||||
## Documentation
|
||||
|
9
admin.go
9
admin.go
@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
||||
m["SessionName"] = SessionName
|
||||
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
|
||||
m["SessionSavePath"] = SessionSavePath
|
||||
m["SessionHashFunc"] = SessionHashFunc
|
||||
m["SessionHashKey"] = SessionHashKey
|
||||
m["SessionCookieLifeTime"] = SessionCookieLifeTime
|
||||
m["UseFcgi"] = UseFcgi
|
||||
m["MaxMemory"] = MaxMemory
|
||||
@ -399,7 +397,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
if err != nil {
|
||||
data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
|
||||
}
|
||||
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())}
|
||||
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
|
||||
} else {
|
||||
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
|
||||
}
|
||||
@ -412,12 +410,14 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
|
||||
var fields = []string{
|
||||
fmt.Sprintf("Task Name"),
|
||||
fmt.Sprintf("Task Spec"),
|
||||
fmt.Sprintf("Task Function"),
|
||||
fmt.Sprintf("Task Status"),
|
||||
fmt.Sprintf("Last Time"),
|
||||
fmt.Sprintf(""),
|
||||
}
|
||||
for tname, tk := range toolbox.AdminTaskList {
|
||||
result = []string{
|
||||
fmt.Sprintf("%s", tname),
|
||||
fmt.Sprintf("%s", tk.GetSpec()),
|
||||
fmt.Sprintf("%s", tk.GetStatus()),
|
||||
fmt.Sprintf("%s", tk.GetPrev().String()),
|
||||
}
|
||||
@ -458,6 +458,7 @@ func (admin *adminApp) Run() {
|
||||
for p, f := range admin.routers {
|
||||
http.Handle(p, f)
|
||||
}
|
||||
BeeLogger.Info("Admin server Running on %s", addr)
|
||||
err := http.ListenAndServe(addr, nil)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("Admin ListenAndServe: ", err)
|
||||
|
@ -186,7 +186,7 @@ bg-warning
|
||||
</td>
|
||||
{{end}}
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 1}}">Run</a>
|
||||
<a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 0}}">Run</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
64
app.go
64
app.go
@ -19,14 +19,12 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
// FilterFunc defines filter function type.
|
||||
type FilterFunc func(*context.Context)
|
||||
|
||||
// App defines beego application with a new PatternServeMux.
|
||||
type App struct {
|
||||
Handlers *ControllerRegistor
|
||||
@ -48,8 +46,6 @@ func (app *App) Run() {
|
||||
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
|
||||
}
|
||||
|
||||
BeeLogger.Info("Running on %s", addr)
|
||||
|
||||
var (
|
||||
err error
|
||||
l net.Listener
|
||||
@ -57,15 +53,28 @@ func (app *App) Run() {
|
||||
endRunning := make(chan bool, 1)
|
||||
|
||||
if UseFcgi {
|
||||
if HttpPort == 0 {
|
||||
l, err = net.Listen("unix", addr)
|
||||
if UseStdIo {
|
||||
err = fcgi.Serve(nil, app.Handlers) // standard I/O
|
||||
if err == nil {
|
||||
BeeLogger.Info("Use FCGI via standard I/O")
|
||||
} else {
|
||||
BeeLogger.Info("Cannot use FCGI via standard I/O", err)
|
||||
}
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
if HttpPort == 0 {
|
||||
// remove the Socket file before start
|
||||
if utils.FileExists(addr) {
|
||||
os.Remove(addr)
|
||||
}
|
||||
l, err = net.Listen("unix", addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
if err != nil {
|
||||
BeeLogger.Critical("Listen: ", err)
|
||||
}
|
||||
err = fcgi.Serve(l, app.Handlers)
|
||||
}
|
||||
if err != nil {
|
||||
BeeLogger.Critical("Listen: ", err)
|
||||
}
|
||||
err = fcgi.Serve(l, app.Handlers)
|
||||
} else {
|
||||
app.Server.Addr = addr
|
||||
app.Server.Handler = app.Handlers
|
||||
@ -78,6 +87,7 @@ func (app *App) Run() {
|
||||
if HttpsPort != 0 {
|
||||
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
|
||||
}
|
||||
BeeLogger.Info("https server Running on %s", app.Server.Addr)
|
||||
err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServeTLS: ", err)
|
||||
@ -90,11 +100,29 @@ func (app *App) Run() {
|
||||
if EnableHttpListen {
|
||||
go func() {
|
||||
app.Server.Addr = addr
|
||||
err := app.Server.ListenAndServe()
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServe: ", err)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
BeeLogger.Info("http server Running on %s", app.Server.Addr)
|
||||
if ListenTCP4 && HttpAddr == "" {
|
||||
ln, err := net.Listen("tcp4", app.Server.Addr)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServe: ", err)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
return
|
||||
}
|
||||
err = app.Server.Serve(ln)
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServe: ", err)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err := app.Server.ListenAndServe()
|
||||
if err != nil {
|
||||
BeeLogger.Critical("ListenAndServe: ", err)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
endRunning <- true
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
104
beego.go
104
beego.go
@ -33,83 +33,15 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/session"
|
||||
)
|
||||
|
||||
// beego web framework version.
|
||||
const VERSION = "1.4.1"
|
||||
const VERSION = "1.4.3"
|
||||
|
||||
type hookfunc func() error //hook function to run
|
||||
var hooks []hookfunc //hook function slice to store the hookfunc
|
||||
|
||||
type groupRouter struct {
|
||||
pattern string
|
||||
controller ControllerInterface
|
||||
mappingMethods string
|
||||
}
|
||||
|
||||
// RouterGroups which will store routers
|
||||
type GroupRouters []groupRouter
|
||||
|
||||
// Get a new GroupRouters
|
||||
func NewGroupRouters() GroupRouters {
|
||||
return make(GroupRouters, 0)
|
||||
}
|
||||
|
||||
// Add Router in the GroupRouters
|
||||
// it is for plugin or module to register router
|
||||
func (gr *GroupRouters) AddRouter(pattern string, c ControllerInterface, mappingMethod ...string) {
|
||||
var newRG groupRouter
|
||||
if len(mappingMethod) > 0 {
|
||||
newRG = groupRouter{
|
||||
pattern,
|
||||
c,
|
||||
mappingMethod[0],
|
||||
}
|
||||
} else {
|
||||
newRG = groupRouter{
|
||||
pattern,
|
||||
c,
|
||||
"",
|
||||
}
|
||||
}
|
||||
*gr = append(*gr, newRG)
|
||||
}
|
||||
|
||||
func (gr *GroupRouters) AddAuto(c ControllerInterface) {
|
||||
newRG := groupRouter{
|
||||
"",
|
||||
c,
|
||||
"",
|
||||
}
|
||||
*gr = append(*gr, newRG)
|
||||
}
|
||||
|
||||
// AddGroupRouter with the prefix
|
||||
// it will register the router in BeeApp
|
||||
// the follow code is write in modules:
|
||||
// GR:=NewGroupRouters()
|
||||
// GR.AddRouter("/login",&UserController,"get:Login")
|
||||
// GR.AddRouter("/logout",&UserController,"get:Logout")
|
||||
// GR.AddRouter("/register",&UserController,"get:Reg")
|
||||
// the follow code is write in app:
|
||||
// import "github.com/beego/modules/auth"
|
||||
// AddRouterGroup("/admin", auth.GR)
|
||||
func AddGroupRouter(prefix string, groups GroupRouters) *App {
|
||||
for _, v := range groups {
|
||||
if v.pattern == "" {
|
||||
BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
|
||||
} else if v.mappingMethods != "" {
|
||||
BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
|
||||
} else {
|
||||
BeeApp.Handlers.Add(prefix+v.pattern, v.controller)
|
||||
}
|
||||
|
||||
}
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// Router adds a patterned controller handler to BeeApp.
|
||||
// it's an alias method of App.Router.
|
||||
// usage:
|
||||
@ -280,15 +212,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// ErrorHandler registers http.HandlerFunc to each http err code string.
|
||||
// usage:
|
||||
// beego.ErrorHandler("404",NotFound)
|
||||
// beego.ErrorHandler("500",InternalServerError)
|
||||
func Errorhandler(err string, h http.HandlerFunc) *App {
|
||||
middleware.Errorhandler(err, h)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// SetViewsPath sets view directory path in beego application.
|
||||
func SetViewsPath(path string) *App {
|
||||
ViewsPath = path
|
||||
@ -308,15 +231,20 @@ func SetStaticPath(url string, path string) *App {
|
||||
|
||||
// DelStaticPath removes the static folder setting in this url pattern in beego application.
|
||||
func DelStaticPath(url string) *App {
|
||||
if !strings.HasPrefix(url, "/") {
|
||||
url = "/" + url
|
||||
}
|
||||
url = strings.TrimRight(url, "/")
|
||||
delete(StaticDir, url)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// InsertFilter adds a FilterFunc with pattern condition and action constant.
|
||||
// The pos means action constant including
|
||||
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
|
||||
func InsertFilter(pattern string, pos int, filter FilterFunc) *App {
|
||||
BeeApp.Handlers.InsertFilter(pattern, pos, filter)
|
||||
// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
|
||||
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
|
||||
func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App {
|
||||
BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...)
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
@ -359,6 +287,9 @@ func initBeforeHttpRun() {
|
||||
}
|
||||
}
|
||||
|
||||
//init mime
|
||||
AddAPPStartHook(initMime)
|
||||
|
||||
// do hooks function
|
||||
for _, hk := range hooks {
|
||||
err := hk()
|
||||
@ -373,10 +304,8 @@ func initBeforeHttpRun() {
|
||||
if sessionConfig == "" {
|
||||
sessionConfig = `{"cookieName":"` + SessionName + `",` +
|
||||
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
|
||||
`"providerConfig":"` + SessionSavePath + `",` +
|
||||
`"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
|
||||
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
|
||||
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
|
||||
`"sessionIDHashKey":"` + SessionHashKey + `",` +
|
||||
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
|
||||
`"domain":"` + SessionDomain + `",` +
|
||||
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
|
||||
@ -396,17 +325,12 @@ func initBeforeHttpRun() {
|
||||
}
|
||||
}
|
||||
|
||||
middleware.VERSION = VERSION
|
||||
middleware.AppName = AppName
|
||||
middleware.RegisterErrorHandler()
|
||||
registerDefaultErrorHandler()
|
||||
|
||||
if EnableDocs {
|
||||
Get("/docs", serverDocs)
|
||||
Get("/docs/*", serverDocs)
|
||||
}
|
||||
|
||||
//init mime
|
||||
AddAPPStartHook(initMime)
|
||||
}
|
||||
|
||||
// this function is for test package init
|
||||
|
6
cache/cache.go
vendored
6
cache/cache.go
vendored
@ -81,13 +81,13 @@ func Register(name string, adapter Cache) {
|
||||
// Create a new cache driver by adapter name and config string.
|
||||
// config need to be correct JSON as string: {"interval":360}.
|
||||
// it will start gc automatically.
|
||||
func NewCache(adapterName, config string) (adapter Cache, e error) {
|
||||
func NewCache(adapterName, config string) (adapter Cache, err error) {
|
||||
adapter, ok := adapters[adapterName]
|
||||
if !ok {
|
||||
e = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
|
||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
|
||||
return
|
||||
}
|
||||
err := adapter.StartAndGC(config)
|
||||
err = adapter.StartAndGC(config)
|
||||
if err != nil {
|
||||
adapter = nil
|
||||
}
|
||||
|
4
cache/cache_test.go
vendored
4
cache/cache_test.go
vendored
@ -15,6 +15,7 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -67,7 +68,7 @@ func TestCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFileCache(t *testing.T) {
|
||||
bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
|
||||
bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
@ -112,4 +113,5 @@ func TestFileCache(t *testing.T) {
|
||||
if v := bm.Get("astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
os.RemoveAll("cache")
|
||||
}
|
||||
|
2
cache/file.go
vendored
2
cache/file.go
vendored
@ -92,8 +92,6 @@ func (fc *FileCache) StartAndGC(config string) error {
|
||||
|
||||
// Init will make new dir for file cache if not exist.
|
||||
func (fc *FileCache) Init() {
|
||||
app := filepath.Dir(os.Args[0])
|
||||
fc.CachePath = filepath.Join(app, fc.CachePath)
|
||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
|
||||
}
|
||||
|
17
cache/redis/redis.go
vendored
17
cache/redis/redis.go
vendored
@ -32,6 +32,7 @@ package redis
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
@ -48,6 +49,7 @@ var (
|
||||
type RedisCache struct {
|
||||
p *redis.Pool // redis connection pool
|
||||
conninfo string
|
||||
dbNum int
|
||||
key string
|
||||
}
|
||||
|
||||
@ -75,14 +77,13 @@ func (rc *RedisCache) Get(key string) interface{} {
|
||||
// put cache to redis.
|
||||
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
|
||||
var err error
|
||||
if _, err = rc.do("SET", key, val); err != nil {
|
||||
if _, err = rc.do("SETEX", key, timeout, val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = rc.do("HSET", rc.key, key, true); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = rc.do("EXPIRE", key, timeout)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -138,7 +139,7 @@ func (rc *RedisCache) ClearAll() error {
|
||||
}
|
||||
|
||||
// start redis cache adapter.
|
||||
// config is like {"key":"collection key","conn":"connection info"}
|
||||
// config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
|
||||
// the cache item in redis are stored forever,
|
||||
// so no gc operation.
|
||||
func (rc *RedisCache) StartAndGC(config string) error {
|
||||
@ -152,9 +153,12 @@ func (rc *RedisCache) StartAndGC(config string) error {
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
}
|
||||
|
||||
if _, ok := cf["dbNum"]; !ok {
|
||||
cf["dbNum"] = "0"
|
||||
}
|
||||
rc.key = cf["key"]
|
||||
rc.conninfo = cf["conn"]
|
||||
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
|
||||
rc.connectInit()
|
||||
|
||||
c := rc.p.Get()
|
||||
@ -167,6 +171,11 @@ func (rc *RedisCache) StartAndGC(config string) error {
|
||||
func (rc *RedisCache) connectInit() {
|
||||
dialFunc := func() (c redis.Conn, err error) {
|
||||
c, err = redis.Dial("tcp", rc.conninfo)
|
||||
_, selecterr := c.Do("SELECT", rc.dbNum)
|
||||
if selecterr != nil {
|
||||
c.Close()
|
||||
return nil, selecterr
|
||||
}
|
||||
return
|
||||
}
|
||||
// initialize a new pool
|
||||
|
534
config.go
534
config.go
@ -15,7 +15,6 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
@ -41,6 +40,7 @@ var (
|
||||
EnableHttpListen bool
|
||||
HttpAddr string
|
||||
HttpPort int
|
||||
ListenTCP4 bool
|
||||
EnableHttpTLS bool
|
||||
HttpsPort int
|
||||
HttpCertFile string
|
||||
@ -48,20 +48,19 @@ var (
|
||||
RecoverPanic bool // flag of auto recover panic
|
||||
AutoRender bool // flag of render template automatically
|
||||
ViewsPath string
|
||||
RunMode string // run mode, "dev" or "prod"
|
||||
AppConfig config.ConfigContainer
|
||||
AppConfig *beegoAppConfig
|
||||
RunMode string // run mode, "dev" or "prod"
|
||||
GlobalSessions *session.Manager // global session mananger
|
||||
SessionOn bool // flag of starting session auto. default is false.
|
||||
SessionProvider string // default session provider, memory, mysql , redis ,etc.
|
||||
SessionName string // the cookie name when saving session id into cookie.
|
||||
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
|
||||
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info.
|
||||
SessionHashFunc string // session hash generation func.
|
||||
SessionHashKey string // session hash salt string.
|
||||
SessionCookieLifeTime int // the life time of session id in cookie.
|
||||
SessionAutoSetCookie bool // auto setcookie
|
||||
SessionDomain string // the cookie domain default is empty
|
||||
UseFcgi bool
|
||||
UseStdIo bool
|
||||
MaxMemory int64
|
||||
EnableGzip bool // flag of enable gzip
|
||||
DirectoryIndex bool // flag of display directory index. default is false.
|
||||
@ -81,8 +80,111 @@ var (
|
||||
FlashSeperator string // used to seperate flash key:value
|
||||
AppConfigProvider string // config provider
|
||||
EnableDocs bool // enable generate docs & server docs API Swagger
|
||||
RouterCaseSensitive bool // router case sensitive default is true
|
||||
AccessLogs bool // print access logs, default is false
|
||||
)
|
||||
|
||||
type beegoAppConfig struct {
|
||||
innerConfig config.ConfigContainer
|
||||
}
|
||||
|
||||
func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, error) {
|
||||
ac, err := config.NewConfig(AppConfigProvider, AppConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rac := &beegoAppConfig{ac}
|
||||
return rac, nil
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Set(key, val string) error {
|
||||
return b.innerConfig.Set(key, val)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) String(key string) string {
|
||||
v := b.innerConfig.String(RunMode + "::" + key)
|
||||
if v == "" {
|
||||
return b.innerConfig.String(key)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Strings(key string) []string {
|
||||
v := b.innerConfig.Strings(RunMode + "::" + key)
|
||||
if v[0] == "" {
|
||||
return b.innerConfig.Strings(key)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Int(key string) (int, error) {
|
||||
v, err := b.innerConfig.Int(RunMode + "::" + key)
|
||||
if err != nil {
|
||||
return b.innerConfig.Int(key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Int64(key string) (int64, error) {
|
||||
v, err := b.innerConfig.Int64(RunMode + "::" + key)
|
||||
if err != nil {
|
||||
return b.innerConfig.Int64(key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Bool(key string) (bool, error) {
|
||||
v, err := b.innerConfig.Bool(RunMode + "::" + key)
|
||||
if err != nil {
|
||||
return b.innerConfig.Bool(key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) Float(key string) (float64, error) {
|
||||
v, err := b.innerConfig.Float(RunMode + "::" + key)
|
||||
if err != nil {
|
||||
return b.innerConfig.Float(key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultString(key string, defaultval string) string {
|
||||
return b.innerConfig.DefaultString(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string {
|
||||
return b.innerConfig.DefaultStrings(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int {
|
||||
return b.innerConfig.DefaultInt(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 {
|
||||
return b.innerConfig.DefaultInt64(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool {
|
||||
return b.innerConfig.DefaultBool(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 {
|
||||
return b.innerConfig.DefaultFloat(key, defaultval)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) DIY(key string) (interface{}, error) {
|
||||
return b.innerConfig.DIY(key)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) {
|
||||
return b.innerConfig.GetSection(section)
|
||||
}
|
||||
|
||||
func (b *beegoAppConfig) SaveConfigFile(filename string) error {
|
||||
return b.innerConfig.SaveConfigFile(filename)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// create beego application
|
||||
BeeApp = NewApp()
|
||||
@ -134,12 +236,11 @@ func init() {
|
||||
SessionName = "beegosessionID"
|
||||
SessionGCMaxLifetime = 3600
|
||||
SessionSavePath = ""
|
||||
SessionHashFunc = "sha1"
|
||||
SessionHashKey = "beegoserversessionkey"
|
||||
SessionCookieLifeTime = 0 //set cookie default is the brower life
|
||||
SessionAutoSetCookie = true
|
||||
|
||||
UseFcgi = false
|
||||
UseStdIo = false
|
||||
|
||||
MaxMemory = 1 << 26 //64MB
|
||||
|
||||
@ -164,6 +265,8 @@ func init() {
|
||||
FlashName = "BEEGO_FLASH"
|
||||
FlashSeperator = "BEEGOFLASH"
|
||||
|
||||
RouterCaseSensitive = true
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
// init BeeLogger
|
||||
@ -172,262 +275,211 @@ func init() {
|
||||
if err != nil {
|
||||
fmt.Println("init console log error:", err)
|
||||
}
|
||||
SetLogFuncCall(true)
|
||||
|
||||
err = ParseConfig()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
// for init if doesn't have app.conf will not panic
|
||||
Info(err)
|
||||
ac := config.NewFakeConfig()
|
||||
AppConfig = &beegoAppConfig{ac}
|
||||
Warning(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig parsed default config file.
|
||||
// now only support ini, next will support json.
|
||||
func ParseConfig() (err error) {
|
||||
AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath)
|
||||
AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath)
|
||||
if err != nil {
|
||||
AppConfig = config.NewFakeConfig()
|
||||
return err
|
||||
} else {
|
||||
}
|
||||
envRunMode := os.Getenv("BEEGO_RUNMODE")
|
||||
// set the runmode first
|
||||
if envRunMode != "" {
|
||||
RunMode = envRunMode
|
||||
} else if runmode := AppConfig.String("RunMode"); runmode != "" {
|
||||
RunMode = runmode
|
||||
}
|
||||
|
||||
if v, err := GetConfig("string", "HttpAddr"); err == nil {
|
||||
HttpAddr = v.(string)
|
||||
HttpAddr = AppConfig.String("HttpAddr")
|
||||
|
||||
if v, err := AppConfig.Int("HttpPort"); err == nil {
|
||||
HttpPort = v
|
||||
}
|
||||
|
||||
if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
|
||||
ListenTCP4 = v
|
||||
}
|
||||
|
||||
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil {
|
||||
EnableHttpListen = v
|
||||
}
|
||||
|
||||
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil {
|
||||
MaxMemory = maxmemory
|
||||
}
|
||||
|
||||
if appname := AppConfig.String("AppName"); appname != "" {
|
||||
AppName = appname
|
||||
}
|
||||
|
||||
if autorender, err := AppConfig.Bool("AutoRender"); err == nil {
|
||||
AutoRender = autorender
|
||||
}
|
||||
|
||||
if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil {
|
||||
RecoverPanic = autorecover
|
||||
}
|
||||
|
||||
if views := AppConfig.String("ViewsPath"); views != "" {
|
||||
ViewsPath = views
|
||||
}
|
||||
|
||||
if sessionon, err := AppConfig.Bool("SessionOn"); err == nil {
|
||||
SessionOn = sessionon
|
||||
}
|
||||
|
||||
if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" {
|
||||
SessionProvider = sessProvider
|
||||
}
|
||||
|
||||
if sessName := AppConfig.String("SessionName"); sessName != "" {
|
||||
SessionName = sessName
|
||||
}
|
||||
|
||||
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" {
|
||||
SessionSavePath = sesssavepath
|
||||
}
|
||||
|
||||
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
|
||||
SessionGCMaxLifetime = sessMaxLifeTime
|
||||
}
|
||||
|
||||
if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
|
||||
SessionCookieLifeTime = sesscookielifetime
|
||||
}
|
||||
|
||||
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil {
|
||||
UseFcgi = usefcgi
|
||||
}
|
||||
|
||||
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil {
|
||||
EnableGzip = enablegzip
|
||||
}
|
||||
|
||||
if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil {
|
||||
DirectoryIndex = directoryindex
|
||||
}
|
||||
|
||||
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil {
|
||||
HttpServerTimeOut = timeout
|
||||
}
|
||||
|
||||
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil {
|
||||
ErrorsShow = errorsshow
|
||||
}
|
||||
|
||||
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil {
|
||||
CopyRequestBody = copyrequestbody
|
||||
}
|
||||
|
||||
if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" {
|
||||
XSRFKEY = xsrfkey
|
||||
}
|
||||
|
||||
if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil {
|
||||
EnableXSRF = enablexsrf
|
||||
}
|
||||
|
||||
if expire, err := AppConfig.Int("XSRFExpire"); err == nil {
|
||||
XSRFExpire = expire
|
||||
}
|
||||
|
||||
if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" {
|
||||
TemplateLeft = tplleft
|
||||
}
|
||||
|
||||
if tplright := AppConfig.String("TemplateRight"); tplright != "" {
|
||||
TemplateRight = tplright
|
||||
}
|
||||
|
||||
if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil {
|
||||
EnableHttpTLS = httptls
|
||||
}
|
||||
|
||||
if httpsport, err := AppConfig.Int("HttpsPort"); err == nil {
|
||||
HttpsPort = httpsport
|
||||
}
|
||||
|
||||
if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
|
||||
HttpCertFile = certfile
|
||||
}
|
||||
|
||||
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
|
||||
HttpKeyFile = keyfile
|
||||
}
|
||||
|
||||
if serverName := AppConfig.String("BeegoServerName"); serverName != "" {
|
||||
BeegoServerName = serverName
|
||||
}
|
||||
|
||||
if flashname := AppConfig.String("FlashName"); flashname != "" {
|
||||
FlashName = flashname
|
||||
}
|
||||
|
||||
if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" {
|
||||
FlashSeperator = flashseperator
|
||||
}
|
||||
|
||||
if sd := AppConfig.String("StaticDir"); sd != "" {
|
||||
for k := range StaticDir {
|
||||
delete(StaticDir, k)
|
||||
}
|
||||
|
||||
if v, err := GetConfig("int", "HttpPort"); err == nil {
|
||||
HttpPort = v.(int)
|
||||
}
|
||||
|
||||
if v, err := GetConfig("bool", "EnableHttpListen"); err == nil {
|
||||
EnableHttpListen = v.(bool)
|
||||
}
|
||||
|
||||
if maxmemory, err := GetConfig("int64", "MaxMemory"); err == nil {
|
||||
MaxMemory = maxmemory.(int64)
|
||||
}
|
||||
|
||||
if appname, _ := GetConfig("string", "AppName"); appname != "" {
|
||||
AppName = appname.(string)
|
||||
}
|
||||
|
||||
if runmode, _ := GetConfig("string", "RunMode"); runmode != "" {
|
||||
RunMode = runmode.(string)
|
||||
}
|
||||
|
||||
if autorender, err := GetConfig("bool", "AutoRender"); err == nil {
|
||||
AutoRender = autorender.(bool)
|
||||
}
|
||||
|
||||
if autorecover, err := GetConfig("bool", "RecoverPanic"); err == nil {
|
||||
RecoverPanic = autorecover.(bool)
|
||||
}
|
||||
|
||||
if views, _ := GetConfig("string", "ViewsPath"); views != "" {
|
||||
ViewsPath = views.(string)
|
||||
}
|
||||
|
||||
if sessionon, err := GetConfig("bool", "SessionOn"); err == nil {
|
||||
SessionOn = sessionon.(bool)
|
||||
}
|
||||
|
||||
if sessProvider, _ := GetConfig("string", "SessionProvider"); sessProvider != "" {
|
||||
SessionProvider = sessProvider.(string)
|
||||
}
|
||||
|
||||
if sessName, _ := GetConfig("string", "SessionName"); sessName != "" {
|
||||
SessionName = sessName.(string)
|
||||
}
|
||||
|
||||
if sesssavepath, _ := GetConfig("string", "SessionSavePath"); sesssavepath != "" {
|
||||
SessionSavePath = sesssavepath.(string)
|
||||
}
|
||||
|
||||
if sesshashfunc, _ := GetConfig("string", "SessionHashFunc"); sesshashfunc != "" {
|
||||
SessionHashFunc = sesshashfunc.(string)
|
||||
}
|
||||
|
||||
if sesshashkey, _ := GetConfig("string", "SessionHashKey"); sesshashkey != "" {
|
||||
SessionHashKey = sesshashkey.(string)
|
||||
}
|
||||
|
||||
if sessMaxLifeTime, err := GetConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
|
||||
SessionGCMaxLifetime = sessMaxLifeTime.(int64)
|
||||
}
|
||||
|
||||
if sesscookielifetime, err := GetConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 {
|
||||
SessionCookieLifeTime = sesscookielifetime.(int)
|
||||
}
|
||||
|
||||
if usefcgi, err := GetConfig("bool", "UseFcgi"); err == nil {
|
||||
UseFcgi = usefcgi.(bool)
|
||||
}
|
||||
|
||||
if enablegzip, err := GetConfig("bool", "EnableGzip"); err == nil {
|
||||
EnableGzip = enablegzip.(bool)
|
||||
}
|
||||
|
||||
if directoryindex, err := GetConfig("bool", "DirectoryIndex"); err == nil {
|
||||
DirectoryIndex = directoryindex.(bool)
|
||||
}
|
||||
|
||||
if timeout, err := GetConfig("int64", "HttpServerTimeOut"); err == nil {
|
||||
HttpServerTimeOut = timeout.(int64)
|
||||
}
|
||||
|
||||
if errorsshow, err := GetConfig("bool", "ErrorsShow"); err == nil {
|
||||
ErrorsShow = errorsshow.(bool)
|
||||
}
|
||||
|
||||
if copyrequestbody, err := GetConfig("bool", "CopyRequestBody"); err == nil {
|
||||
CopyRequestBody = copyrequestbody.(bool)
|
||||
}
|
||||
|
||||
if xsrfkey, _ := GetConfig("string", "XSRFKEY"); xsrfkey != "" {
|
||||
XSRFKEY = xsrfkey.(string)
|
||||
}
|
||||
|
||||
if enablexsrf, err := GetConfig("bool", "EnableXSRF"); err == nil {
|
||||
EnableXSRF = enablexsrf.(bool)
|
||||
}
|
||||
|
||||
if expire, err := GetConfig("int", "XSRFExpire"); err == nil {
|
||||
XSRFExpire = expire.(int)
|
||||
}
|
||||
|
||||
if tplleft, _ := GetConfig("string", "TemplateLeft"); tplleft != "" {
|
||||
TemplateLeft = tplleft.(string)
|
||||
}
|
||||
|
||||
if tplright, _ := GetConfig("string", "TemplateRight"); tplright != "" {
|
||||
TemplateRight = tplright.(string)
|
||||
}
|
||||
|
||||
if httptls, err := GetConfig("bool", "EnableHttpTLS"); err == nil {
|
||||
EnableHttpTLS = httptls.(bool)
|
||||
}
|
||||
|
||||
if httpsport, err := GetConfig("int", "HttpsPort"); err == nil {
|
||||
HttpsPort = httpsport.(int)
|
||||
}
|
||||
|
||||
if certfile, _ := GetConfig("string", "HttpCertFile"); certfile != "" {
|
||||
HttpCertFile = certfile.(string)
|
||||
}
|
||||
|
||||
if keyfile, _ := GetConfig("string", "HttpKeyFile"); keyfile != "" {
|
||||
HttpKeyFile = keyfile.(string)
|
||||
}
|
||||
|
||||
if serverName, _ := GetConfig("string", "BeegoServerName"); serverName != "" {
|
||||
BeegoServerName = serverName.(string)
|
||||
}
|
||||
|
||||
if flashname, _ := GetConfig("string", "FlashName"); flashname != "" {
|
||||
FlashName = flashname.(string)
|
||||
}
|
||||
|
||||
if flashseperator, _ := GetConfig("string", "FlashSeperator"); flashseperator != "" {
|
||||
FlashSeperator = flashseperator.(string)
|
||||
}
|
||||
|
||||
if sd, _ := GetConfig("string", "StaticDir"); sd != "" {
|
||||
for k := range StaticDir {
|
||||
delete(StaticDir, k)
|
||||
sds := strings.Fields(sd)
|
||||
for _, v := range sds {
|
||||
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
|
||||
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
|
||||
} else {
|
||||
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
|
||||
}
|
||||
sds := strings.Fields(sd.(string))
|
||||
for _, v := range sds {
|
||||
if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 {
|
||||
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1]
|
||||
} else {
|
||||
StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0]
|
||||
}
|
||||
}
|
||||
|
||||
if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" {
|
||||
extensions := strings.Split(sgz, ",")
|
||||
if len(extensions) > 0 {
|
||||
StaticExtensionsToGzip = []string{}
|
||||
for _, ext := range extensions {
|
||||
if len(ext) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sgz, _ := GetConfig("string", "StaticExtensionsToGzip"); sgz != "" {
|
||||
extensions := strings.Split(sgz.(string), ",")
|
||||
if len(extensions) > 0 {
|
||||
StaticExtensionsToGzip = []string{}
|
||||
for _, ext := range extensions {
|
||||
if len(ext) == 0 {
|
||||
continue
|
||||
}
|
||||
extWithDot := ext
|
||||
if extWithDot[:1] != "." {
|
||||
extWithDot = "." + extWithDot
|
||||
}
|
||||
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
|
||||
extWithDot := ext
|
||||
if extWithDot[:1] != "." {
|
||||
extWithDot = "." + extWithDot
|
||||
}
|
||||
StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if enableadmin, err := GetConfig("bool", "EnableAdmin"); err == nil {
|
||||
EnableAdmin = enableadmin.(bool)
|
||||
}
|
||||
if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil {
|
||||
EnableAdmin = enableadmin
|
||||
}
|
||||
|
||||
if adminhttpaddr, _ := GetConfig("string", "AdminHttpAddr"); adminhttpaddr != "" {
|
||||
AdminHttpAddr = adminhttpaddr.(string)
|
||||
}
|
||||
if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" {
|
||||
AdminHttpAddr = adminhttpaddr
|
||||
}
|
||||
|
||||
if adminhttpport, err := GetConfig("int", "AdminHttpPort"); err == nil {
|
||||
AdminHttpPort = adminhttpport.(int)
|
||||
}
|
||||
if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil {
|
||||
AdminHttpPort = adminhttpport
|
||||
}
|
||||
|
||||
if enabledocs, err := GetConfig("bool", "EnableDocs"); err == nil {
|
||||
EnableDocs = enabledocs.(bool)
|
||||
}
|
||||
if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil {
|
||||
EnableDocs = enabledocs
|
||||
}
|
||||
|
||||
if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil {
|
||||
RouterCaseSensitive = casesensitive
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Getconfig throw the Runmode
|
||||
// [dev]
|
||||
// name = astaixe
|
||||
// IsEnable = false
|
||||
// [prod]
|
||||
// name = slene
|
||||
// IsEnable = true
|
||||
//
|
||||
// usage:
|
||||
// GetConfig("string", "name")
|
||||
// GetConfig("bool", "IsEnable")
|
||||
func GetConfig(typ, key string) (interface{}, error) {
|
||||
switch typ {
|
||||
case "string":
|
||||
v := AppConfig.String(RunMode + "::" + key)
|
||||
if v == "" {
|
||||
v = AppConfig.String(key)
|
||||
}
|
||||
return v, nil
|
||||
case "strings":
|
||||
v := AppConfig.Strings(RunMode + "::" + key)
|
||||
if len(v) == 0 {
|
||||
v = AppConfig.Strings(key)
|
||||
}
|
||||
return v, nil
|
||||
case "int":
|
||||
v, err := AppConfig.Int(RunMode + "::" + key)
|
||||
if err != nil || v == 0 {
|
||||
return AppConfig.Int(key)
|
||||
}
|
||||
return v, nil
|
||||
case "bool":
|
||||
v, err := AppConfig.Bool(RunMode + "::" + key)
|
||||
if err != nil {
|
||||
return AppConfig.Bool(key)
|
||||
}
|
||||
return v, nil
|
||||
case "int64":
|
||||
v, err := AppConfig.Int64(RunMode + "::" + key)
|
||||
if err != nil || v == 0 {
|
||||
return AppConfig.Int64(key)
|
||||
}
|
||||
return v, nil
|
||||
case "float":
|
||||
v, err := AppConfig.Float(RunMode + "::" + key)
|
||||
if err != nil || v == 0 {
|
||||
return AppConfig.Float(key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return "", errors.New("not support type")
|
||||
}
|
||||
|
@ -48,6 +48,10 @@ type IniConfig struct {
|
||||
|
||||
// ParseFile creates a new Config and parses the file configuration from the named file.
|
||||
func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
|
||||
return ini.parseFile(name)
|
||||
}
|
||||
|
||||
func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
||||
file, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -66,6 +70,13 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
|
||||
|
||||
var comment bytes.Buffer
|
||||
buf := bufio.NewReader(file)
|
||||
// check the BOM
|
||||
head, err := buf.Peek(3)
|
||||
if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 {
|
||||
for i := 1; i <= 3; i++ {
|
||||
buf.ReadByte()
|
||||
}
|
||||
}
|
||||
section := DEFAULT_SECTION
|
||||
for {
|
||||
line, _, err := buf.ReadLine()
|
||||
@ -108,13 +119,48 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) {
|
||||
cfg.data[section] = make(map[string]string)
|
||||
}
|
||||
keyValue := bytes.SplitN(line, bEqual, 2)
|
||||
|
||||
key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
|
||||
key = strings.ToLower(key)
|
||||
|
||||
// handle include "other.conf"
|
||||
if len(keyValue) == 1 && strings.HasPrefix(key, "include") {
|
||||
includefiles := strings.Fields(key)
|
||||
if includefiles[0] == "include" && len(includefiles) == 2 {
|
||||
otherfile := strings.Trim(includefiles[1], "\"")
|
||||
if !path.IsAbs(otherfile) {
|
||||
otherfile = path.Join(path.Dir(name), otherfile)
|
||||
}
|
||||
i, err := ini.parseFile(otherfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for sec, dt := range i.data {
|
||||
if _, ok := cfg.data[sec]; !ok {
|
||||
cfg.data[sec] = make(map[string]string)
|
||||
}
|
||||
for k, v := range dt {
|
||||
cfg.data[sec][k] = v
|
||||
}
|
||||
}
|
||||
for sec, comm := range i.sectionComment {
|
||||
cfg.sectionComment[sec] = comm
|
||||
}
|
||||
for k, comm := range i.keyComment {
|
||||
cfg.keyComment[k] = comm
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(keyValue) != 2 {
|
||||
return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val")
|
||||
}
|
||||
val := bytes.TrimSpace(keyValue[1])
|
||||
if bytes.HasPrefix(val, bDQuote) {
|
||||
val = bytes.Trim(val, `"`)
|
||||
}
|
||||
|
||||
key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive
|
||||
key = strings.ToLower(key)
|
||||
cfg.data[section][key] = string(val)
|
||||
if comment.Len() > 0 {
|
||||
cfg.keyComment[section+"."+key] = comment.String()
|
||||
|
@ -17,13 +17,10 @@ package config
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// JsonConfig is a json config parser and implements Config interface.
|
||||
@ -41,13 +38,19 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return js.ParseData(content)
|
||||
}
|
||||
|
||||
// ParseData returns a ConfigContainer with json string
|
||||
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
|
||||
x := &JsonConfigContainer{
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
err = json.Unmarshal(content, &x.data)
|
||||
err := json.Unmarshal(data, &x.data)
|
||||
if err != nil {
|
||||
var wrappingArray []interface{}
|
||||
err2 := json.Unmarshal(content, &wrappingArray)
|
||||
err2 := json.Unmarshal(data, &wrappingArray)
|
||||
if err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -56,16 +59,6 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
|
||||
return x, nil
|
||||
}
|
||||
|
||||
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
|
||||
// Save memory data to temporary file
|
||||
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
|
||||
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
|
||||
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return js.Parse(tmpName)
|
||||
}
|
||||
|
||||
// A Config represents the json configuration.
|
||||
// Only when get value, support key as section:name type.
|
||||
type JsonConfigContainer struct {
|
||||
@ -88,11 +81,10 @@ func (c *JsonConfigContainer) Bool(key string) (bool, error) {
|
||||
// DefaultBool return the bool value if has no error
|
||||
// otherwise return the defaultval
|
||||
func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||
if v, err := c.Bool(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
if v, err := c.Bool(key); err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// Int returns the integer value for a given key.
|
||||
@ -110,11 +102,10 @@ func (c *JsonConfigContainer) Int(key string) (int, error) {
|
||||
// DefaultInt returns the integer value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||
if v, err := c.Int(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
if v, err := c.Int(key); err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// Int64 returns the int64 value for a given key.
|
||||
@ -132,11 +123,10 @@ func (c *JsonConfigContainer) Int64(key string) (int64, error) {
|
||||
// DefaultInt64 returns the int64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||
if v, err := c.Int64(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
if v, err := c.Int64(key); err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// Float returns the float value for a given key.
|
||||
@ -154,11 +144,10 @@ func (c *JsonConfigContainer) Float(key string) (float64, error) {
|
||||
// DefaultFloat returns the float64 value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||
if v, err := c.Float(key); err != nil {
|
||||
return defaultval
|
||||
} else {
|
||||
if v, err := c.Float(key); err == nil {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// String returns the string value for a given key.
|
||||
@ -175,35 +164,37 @@ func (c *JsonConfigContainer) String(key string) string {
|
||||
// DefaultString returns the string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string {
|
||||
if v := c.String(key); v == "" {
|
||||
return defaultval
|
||||
} else {
|
||||
// TODO FIXME should not use "" to replace non existance
|
||||
if v := c.String(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// Strings returns the []string value for a given key.
|
||||
func (c *JsonConfigContainer) Strings(key string) []string {
|
||||
stringVal := c.String(key)
|
||||
if stringVal == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(c.String(key), ";")
|
||||
}
|
||||
|
||||
// DefaultStrings returns the []string value for a given key.
|
||||
// if err != nil return defaltval
|
||||
func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||
if v := c.Strings(key); len(v) == 0 {
|
||||
return defaultval
|
||||
} else {
|
||||
if v := c.Strings(key); len(v) > 0 {
|
||||
return v
|
||||
}
|
||||
return defaultval
|
||||
}
|
||||
|
||||
// GetSection returns map for the given section
|
||||
func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) {
|
||||
if v, ok := c.data[section]; ok {
|
||||
return v.(map[string]string), nil
|
||||
} else {
|
||||
return nil, errors.New("not exist setction")
|
||||
}
|
||||
return nil, errors.New("nonexist section " + section)
|
||||
}
|
||||
|
||||
// SaveConfigFile save the config into file
|
||||
@ -222,7 +213,7 @@ func (c *JsonConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteValue writes a new value for key.
|
||||
// Set writes a new value for key.
|
||||
func (c *JsonConfigContainer) Set(key, val string) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
@ -241,18 +232,20 @@ func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
|
||||
|
||||
// section.key or key
|
||||
func (c *JsonConfigContainer) getData(key string) interface{} {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
if len(key) == 0 {
|
||||
return nil
|
||||
}
|
||||
sectionKey := strings.Split(key, "::")
|
||||
if len(sectionKey) >= 2 {
|
||||
curValue, ok := c.data[sectionKey[0]]
|
||||
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
sectionKeys := strings.Split(key, "::")
|
||||
if len(sectionKeys) >= 2 {
|
||||
curValue, ok := c.data[sectionKeys[0]]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for _, key := range sectionKey[1:] {
|
||||
for _, key := range sectionKeys[1:] {
|
||||
if v, ok := curValue.(map[string]interface{}); ok {
|
||||
if curValue, ok = v[key]; !ok {
|
||||
return nil
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
var jsoncontext = `{
|
||||
"appname": "beeapi",
|
||||
"testnames": "foo;bar",
|
||||
"httpport": 8080,
|
||||
"mysqlport": 3600,
|
||||
"PI": 3.1415976,
|
||||
@ -28,8 +29,8 @@ var jsoncontext = `{
|
||||
"autorender": false,
|
||||
"copyrequestbody": true,
|
||||
"database": {
|
||||
"host": "host",
|
||||
"port": "port",
|
||||
"host": "host",
|
||||
"port": "port",
|
||||
"database": "database",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
@ -122,6 +123,12 @@ func TestJson(t *testing.T) {
|
||||
if jsonconf.String("runmode") != "dev" {
|
||||
t.Fatal("runmode not equal to dev")
|
||||
}
|
||||
if v := jsonconf.Strings("unknown"); len(v) > 0 {
|
||||
t.Fatal("unknown strings, the length should be 0")
|
||||
}
|
||||
if v := jsonconf.Strings("testnames"); len(v) != 2 {
|
||||
t.Fatal("testnames length should be 2")
|
||||
}
|
||||
if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
|
||||
t.Error(v)
|
||||
t.Fatal(err)
|
||||
@ -179,4 +186,8 @@ func TestJson(t *testing.T) {
|
||||
if _, err := jsonconf.Bool("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting a Bool")
|
||||
}
|
||||
|
||||
if !jsonconf.DefaultBool("unknow", true) {
|
||||
t.Error("unknown keys with default value wrong")
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
@ -53,22 +52,9 @@ func (ctx *Context) Redirect(status int, localurl string) {
|
||||
}
|
||||
|
||||
// Abort stops this request.
|
||||
// if middleware.ErrorMaps exists, panic body.
|
||||
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
|
||||
// if beego.ErrorMaps exists, panic body.
|
||||
func (ctx *Context) Abort(status int, body string) {
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
// first panic from ErrorMaps, is is user defined error functions.
|
||||
if _, ok := middleware.ErrorMaps[body]; ok {
|
||||
panic(body)
|
||||
}
|
||||
// second panic from HTTPExceptionMaps, it is system defined functions.
|
||||
if e, ok := middleware.HTTPExceptionMaps[status]; ok {
|
||||
if len(body) >= 1 {
|
||||
e.Description = body
|
||||
}
|
||||
panic(e)
|
||||
}
|
||||
// last panic user string
|
||||
panic(body)
|
||||
}
|
||||
|
||||
@ -154,8 +140,11 @@ func (ctx *Context) CheckXsrfCookie() bool {
|
||||
}
|
||||
if token == "" {
|
||||
ctx.Abort(403, "'_xsrf' argument missing from POST")
|
||||
} else if ctx._xsrf_token != token {
|
||||
return false
|
||||
}
|
||||
if ctx._xsrf_token != token {
|
||||
ctx.Abort(403, "XSRF cookie does not match POST argument")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"github.com/astaxie/beego/session"
|
||||
)
|
||||
|
||||
// BeegoInput operates the http request header ,data ,cookie and body.
|
||||
// BeegoInput operates the http request header, data, cookie and body.
|
||||
// it also contains router params and current session.
|
||||
type BeegoInput struct {
|
||||
CruSession session.SessionStore
|
||||
@ -72,11 +72,11 @@ func (input *BeegoInput) Site() string {
|
||||
func (input *BeegoInput) Scheme() string {
|
||||
if input.Request.URL.Scheme != "" {
|
||||
return input.Request.URL.Scheme
|
||||
} else if input.Request.TLS == nil {
|
||||
return "http"
|
||||
} else {
|
||||
return "https"
|
||||
}
|
||||
if input.Request.TLS == nil {
|
||||
return "http"
|
||||
}
|
||||
return "https"
|
||||
}
|
||||
|
||||
// Domain returns host name.
|
||||
@ -153,12 +153,12 @@ func (input *BeegoInput) IsSecure() bool {
|
||||
return input.Scheme() == "https"
|
||||
}
|
||||
|
||||
// IsSecure returns boolean of this request is in webSocket.
|
||||
// IsWebsocket returns boolean of this request is in webSocket.
|
||||
func (input *BeegoInput) IsWebsocket() bool {
|
||||
return input.Header("Upgrade") == "websocket"
|
||||
}
|
||||
|
||||
// IsSecure returns boolean of whether file uploads in this request or not..
|
||||
// IsUpload returns boolean of whether file uploads in this request or not..
|
||||
func (input *BeegoInput) IsUpload() bool {
|
||||
return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
|
||||
}
|
||||
@ -189,16 +189,24 @@ func (input *BeegoInput) Proxy() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Referer returns http referer header.
|
||||
func (input *BeegoInput) Referer() string {
|
||||
return input.Header("Referer")
|
||||
}
|
||||
|
||||
// Refer returns http referer header.
|
||||
func (input *BeegoInput) Refer() string {
|
||||
return input.Header("Referer")
|
||||
return input.Referer()
|
||||
}
|
||||
|
||||
// SubDomains returns sub domain string.
|
||||
// if aa.bb.domain.com, returns aa.bb .
|
||||
func (input *BeegoInput) SubDomains() string {
|
||||
parts := strings.Split(input.Host(), ".")
|
||||
return strings.Join(parts[len(parts)-2:], ".")
|
||||
if len(parts) >= 3 {
|
||||
return strings.Join(parts[:len(parts)-2], ".")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Port returns request client port.
|
||||
@ -237,6 +245,7 @@ func (input *BeegoInput) Query(key string) string {
|
||||
}
|
||||
|
||||
// Header returns request header item string by a given string.
|
||||
// if non-existed, return empty string.
|
||||
func (input *BeegoInput) Header(key string) string {
|
||||
return input.Request.Header.Get(key)
|
||||
}
|
||||
@ -252,11 +261,12 @@ func (input *BeegoInput) Cookie(key string) string {
|
||||
}
|
||||
|
||||
// Session returns current session item value by a given key.
|
||||
// if non-existed, return empty string.
|
||||
func (input *BeegoInput) Session(key interface{}) interface{} {
|
||||
return input.CruSession.Get(key)
|
||||
}
|
||||
|
||||
// Body returns the raw request body data as bytes.
|
||||
// CopyBody returns the raw request body data as bytes.
|
||||
func (input *BeegoInput) CopyBody() []byte {
|
||||
requestbody, _ := ioutil.ReadAll(input.Request.Body)
|
||||
input.Request.Body.Close()
|
||||
|
@ -70,3 +70,45 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
fmt.Println(user)
|
||||
}
|
||||
|
||||
func TestSubDomain(t *testing.T) {
|
||||
r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
|
||||
beegoInput := NewInput(r)
|
||||
|
||||
subdomain := beegoInput.SubDomains()
|
||||
if subdomain != "www" {
|
||||
t.Fatal("Subdomain parse error, got" + subdomain)
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("GET", "http://localhost/", nil)
|
||||
beegoInput.Request = r
|
||||
if beegoInput.SubDomains() != "" {
|
||||
t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
|
||||
beegoInput.Request = r
|
||||
if beegoInput.SubDomains() != "aa.bb" {
|
||||
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
||||
}
|
||||
|
||||
/* TODO Fix this
|
||||
r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
|
||||
beegoInput.Request = r
|
||||
if beegoInput.SubDomains() != "" {
|
||||
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
||||
}
|
||||
*/
|
||||
|
||||
r, _ = http.NewRequest("GET", "http://example.com/", nil)
|
||||
beegoInput.Request = r
|
||||
if beegoInput.SubDomains() != "" {
|
||||
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
|
||||
beegoInput.Request = r
|
||||
if beegoInput.SubDomains() != "aa.bb.cc.dd" {
|
||||
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ func sanitizeValue(v string) string {
|
||||
// Json writes json to response body.
|
||||
// if coding is true, it converts utf-8 to \u0000 type.
|
||||
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
|
||||
output.Header("Content-Type", "application/json;charset=UTF-8")
|
||||
output.Header("Content-Type", "application/json; charset=utf-8")
|
||||
var content []byte
|
||||
var err error
|
||||
if hasIndent {
|
||||
@ -209,7 +209,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e
|
||||
|
||||
// Jsonp writes jsonp to response body.
|
||||
func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
|
||||
output.Header("Content-Type", "application/javascript;charset=UTF-8")
|
||||
output.Header("Content-Type", "application/javascript; charset=utf-8")
|
||||
var content []byte
|
||||
var err error
|
||||
if hasIndent {
|
||||
@ -235,7 +235,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
|
||||
|
||||
// Xml writes xml string to response body.
|
||||
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
|
||||
output.Header("Content-Type", "application/xml;charset=UTF-8")
|
||||
output.Header("Content-Type", "application/xml; charset=utf-8")
|
||||
var content []byte
|
||||
var err error
|
||||
if hasIndent {
|
||||
|
156
controller.go
156
controller.go
@ -270,16 +270,22 @@ func (c *Controller) Redirect(url string, code int) {
|
||||
// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string.
|
||||
func (c *Controller) Abort(code string) {
|
||||
status, err := strconv.Atoi(code)
|
||||
if err == nil {
|
||||
c.Ctx.Abort(status, code)
|
||||
} else {
|
||||
c.Ctx.Abort(200, code)
|
||||
if err != nil {
|
||||
status = 200
|
||||
}
|
||||
c.CustomAbort(status, code)
|
||||
}
|
||||
|
||||
// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
|
||||
func (c *Controller) CustomAbort(status int, body string) {
|
||||
c.Ctx.Abort(status, body)
|
||||
c.Ctx.ResponseWriter.WriteHeader(status)
|
||||
// first panic from ErrorMaps, is is user defined error functions.
|
||||
if _, ok := ErrorMaps[body]; ok {
|
||||
panic(body)
|
||||
}
|
||||
// last panic user string
|
||||
c.Ctx.ResponseWriter.Write([]byte(body))
|
||||
panic(USERSTOPRUN)
|
||||
}
|
||||
|
||||
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
|
||||
@ -289,7 +295,7 @@ func (c *Controller) StopRun() {
|
||||
|
||||
// UrlFor does another controller handler in this request function.
|
||||
// it goes to this controller method if endpoint is not clear.
|
||||
func (c *Controller) UrlFor(endpoint string, values ...string) string {
|
||||
func (c *Controller) UrlFor(endpoint string, values ...interface{}) string {
|
||||
if len(endpoint) <= 0 {
|
||||
return ""
|
||||
}
|
||||
@ -363,38 +369,144 @@ func (c *Controller) ParseForm(obj interface{}) error {
|
||||
return ParseForm(c.Input(), obj)
|
||||
}
|
||||
|
||||
// GetString returns the input value by key string.
|
||||
func (c *Controller) GetString(key string) string {
|
||||
return c.Ctx.Input.Query(key)
|
||||
// GetString returns the input value by key string or the default value while it's present and input is blank
|
||||
func (c *Controller) GetString(key string, def ...string) string {
|
||||
var defv string
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if v := c.Ctx.Input.Query(key); v != "" {
|
||||
return v
|
||||
} else {
|
||||
return defv
|
||||
}
|
||||
}
|
||||
|
||||
// GetStrings returns the input string slice by key string.
|
||||
// GetStrings returns the input string slice by key string or the default value while it's present and input is blank
|
||||
// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
|
||||
func (c *Controller) GetStrings(key string) []string {
|
||||
func (c *Controller) GetStrings(key string, def ...[]string) []string {
|
||||
var defv []string
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
f := c.Input()
|
||||
if f == nil {
|
||||
return []string{}
|
||||
return defv
|
||||
}
|
||||
|
||||
vs := f[key]
|
||||
if len(vs) > 0 {
|
||||
return vs
|
||||
} else {
|
||||
return defv
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetInt returns input value as int64.
|
||||
func (c *Controller) GetInt(key string) (int64, error) {
|
||||
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64)
|
||||
// GetInt returns input as an int or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt(key string, def ...int) (int, error) {
|
||||
var defv int
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.Atoi(strv)
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetBool returns input value as bool.
|
||||
func (c *Controller) GetBool(key string) (bool, error) {
|
||||
return strconv.ParseBool(c.Ctx.Input.Query(key))
|
||||
// GetInt8 return input as an int8 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
|
||||
var defv int8
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
i64, err := strconv.ParseInt(strv, 10, 8)
|
||||
i8 := int8(i64)
|
||||
return i8, err
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetFloat returns input value as float64.
|
||||
func (c *Controller) GetFloat(key string) (float64, error) {
|
||||
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
|
||||
// GetInt16 returns input as an int16 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
|
||||
var defv int16
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
i64, err := strconv.ParseInt(strv, 10, 16)
|
||||
i16 := int16(i64)
|
||||
|
||||
return i16, err
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt32 returns input as an int32 or the default value while it's present and input is blank
|
||||
func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
|
||||
var defv int32
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
|
||||
i32 := int32(i64)
|
||||
return i32, err
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt64 returns input value as int64 or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
|
||||
var defv int64
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseInt(strv, 10, 64)
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetBool returns input value as bool or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
|
||||
var defv bool
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseBool(strv)
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetFloat returns input value as float64 or the default value while it's present and input is blank.
|
||||
func (c *Controller) GetFloat(key string, def ...float64) (float64, error) {
|
||||
var defv float64
|
||||
if len(def) > 0 {
|
||||
defv = def[0]
|
||||
}
|
||||
|
||||
if strv := c.Ctx.Input.Query(key); strv != "" {
|
||||
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
|
||||
} else {
|
||||
return defv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetFile returns the file data in file upload field named as key.
|
||||
|
75
controller_test.go
Normal file
75
controller_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package beego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/context"
|
||||
)
|
||||
|
||||
func ExampleGetInt() {
|
||||
|
||||
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
|
||||
val, _ := ctrlr.GetInt("age")
|
||||
fmt.Printf("%T", val)
|
||||
//Output: int
|
||||
}
|
||||
|
||||
func ExampleGetInt8() {
|
||||
|
||||
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
|
||||
val, _ := ctrlr.GetInt8("age")
|
||||
fmt.Printf("%T", val)
|
||||
//Output: int8
|
||||
}
|
||||
|
||||
func ExampleGetInt16() {
|
||||
|
||||
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
|
||||
val, _ := ctrlr.GetInt16("age")
|
||||
fmt.Printf("%T", val)
|
||||
//Output: int16
|
||||
}
|
||||
|
||||
func ExampleGetInt32() {
|
||||
|
||||
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
|
||||
val, _ := ctrlr.GetInt32("age")
|
||||
fmt.Printf("%T", val)
|
||||
//Output: int32
|
||||
}
|
||||
|
||||
func ExampleGetInt64() {
|
||||
|
||||
i := &context.BeegoInput{Params: map[string]string{"age": "40"}}
|
||||
ctx := &context.Context{Input: i}
|
||||
ctrlr := Controller{Ctx: ctx}
|
||||
|
||||
val, _ := ctrlr.GetInt64("age")
|
||||
fmt.Printf("%T", val)
|
||||
//Output: int64
|
||||
}
|
@ -12,20 +12,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package middleware
|
||||
package beego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
AppName string
|
||||
VERSION string
|
||||
const (
|
||||
errorTypeHandler = iota
|
||||
errorTypeController
|
||||
)
|
||||
|
||||
var tpl = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -76,18 +82,18 @@ var tpl = `
|
||||
`
|
||||
|
||||
// render default application error page with error and stack string.
|
||||
func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) {
|
||||
func showErr(err interface{}, ctx *context.Context, Stack string) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(tpl)
|
||||
data := make(map[string]string)
|
||||
data["AppError"] = AppName + ":" + fmt.Sprint(err)
|
||||
data["RequestMethod"] = r.Method
|
||||
data["RequestURL"] = r.RequestURI
|
||||
data["RemoteAddr"] = r.RemoteAddr
|
||||
data["RequestMethod"] = ctx.Input.Method()
|
||||
data["RequestURL"] = ctx.Input.Uri()
|
||||
data["RemoteAddr"] = ctx.Input.IP()
|
||||
data["Stack"] = Stack
|
||||
data["BeegoVersion"] = VERSION
|
||||
data["GoVersion"] = runtime.Version()
|
||||
rw.WriteHeader(500)
|
||||
t.Execute(rw, data)
|
||||
ctx.Output.SetStatus(500)
|
||||
t.Execute(ctx.ResponseWriter, data)
|
||||
}
|
||||
|
||||
var errtpl = `
|
||||
@ -190,15 +196,68 @@ var errtpl = `
|
||||
</html>
|
||||
`
|
||||
|
||||
type errorInfo struct {
|
||||
controllerType reflect.Type
|
||||
handler http.HandlerFunc
|
||||
method string
|
||||
errorType int
|
||||
}
|
||||
|
||||
// map of http handlers for each error string.
|
||||
var ErrorMaps map[string]http.HandlerFunc
|
||||
var ErrorMaps map[string]*errorInfo
|
||||
|
||||
func init() {
|
||||
ErrorMaps = make(map[string]http.HandlerFunc)
|
||||
ErrorMaps = make(map[string]*errorInfo)
|
||||
}
|
||||
|
||||
// show 401 unauthorized error.
|
||||
func unauthorized(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Unauthorized"
|
||||
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
"<br><br><ul>" +
|
||||
"<br>The credentials you supplied are incorrect" +
|
||||
"<br>There are errors in the website address" +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 402 Payment Required
|
||||
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Payment Required"
|
||||
data["Content"] = template.HTML("<br>The page you have requested Payment Required." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
"<br><br><ul>" +
|
||||
"<br>The credentials you supplied are incorrect" +
|
||||
"<br>There are errors in the website address" +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 403 forbidden error.
|
||||
func forbidden(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Forbidden"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is forbidden." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
"<br><br><ul>" +
|
||||
"<br>Your address may be blocked" +
|
||||
"<br>The site may be disabled" +
|
||||
"<br>You need to log in" +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 404 notfound error.
|
||||
func NotFound(rw http.ResponseWriter, r *http.Request) {
|
||||
func notFound(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Page Not Found"
|
||||
@ -211,45 +270,66 @@ func NotFound(rw http.ResponseWriter, r *http.Request) {
|
||||
"<br>You like 404 pages" +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
//rw.WriteHeader(http.StatusNotFound)
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 401 unauthorized error.
|
||||
func Unauthorized(rw http.ResponseWriter, r *http.Request) {
|
||||
// show 405 Method Not Allowed
|
||||
func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Unauthorized"
|
||||
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." +
|
||||
data["Title"] = "Method Not Allowed"
|
||||
data["Content"] = template.HTML("<br>The method you have requested Not Allowed." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
"<br><br><ul>" +
|
||||
"<br>The credentials you supplied are incorrect" +
|
||||
"<br>There are errors in the website address" +
|
||||
"<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
|
||||
"<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
//rw.WriteHeader(http.StatusUnauthorized)
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 403 forbidden error.
|
||||
func Forbidden(rw http.ResponseWriter, r *http.Request) {
|
||||
// show 500 internal server error.
|
||||
func internalServerError(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Forbidden"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is forbidden." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
data["Title"] = "Internal Server Error"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
|
||||
"<br><br><ul>" +
|
||||
"<br>Your address may be blocked" +
|
||||
"<br>The site may be disabled" +
|
||||
"<br>You need to log in" +
|
||||
"</ul>")
|
||||
"<br>Please try again later and report the error to the website administrator" +
|
||||
"<br></ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 501 Not Implemented.
|
||||
func notImplemented(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Not Implemented"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is Not Implemented." +
|
||||
"<br><br><ul>" +
|
||||
"<br>Please try again later and report the error to the website administrator" +
|
||||
"<br></ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 502 Bad Gateway.
|
||||
func badGateway(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Bad Gateway"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
|
||||
"<br><br><ul>" +
|
||||
"<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." +
|
||||
"<br>Please try again later and report the error to the website administrator" +
|
||||
"<br></ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
//rw.WriteHeader(http.StatusForbidden)
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 503 service unavailable error.
|
||||
func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
|
||||
func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Service Unavailable"
|
||||
@ -260,80 +340,151 @@ func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
|
||||
"<br>Please try again later." +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
//rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 500 internal server error.
|
||||
func InternalServerError(rw http.ResponseWriter, r *http.Request) {
|
||||
// show 504 Gateway Timeout.
|
||||
func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
|
||||
t, _ := template.New("beegoerrortemp").Parse(errtpl)
|
||||
data := make(map[string]interface{})
|
||||
data["Title"] = "Internal Server Error"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
|
||||
data["Title"] = "Gateway Timeout"
|
||||
data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
|
||||
"<br>Perhaps you are here because:" +
|
||||
"<br><br><ul>" +
|
||||
"<br>Please try again later and report the error to the website administrator" +
|
||||
"<br></ul>")
|
||||
"<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
|
||||
"<br>Please try again later." +
|
||||
"</ul>")
|
||||
data["BeegoVersion"] = VERSION
|
||||
//rw.WriteHeader(http.StatusInternalServerError)
|
||||
t.Execute(rw, data)
|
||||
}
|
||||
|
||||
// show 500 internal error with simple text string.
|
||||
func SimpleServerError(rw http.ResponseWriter, r *http.Request) {
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// add http handler for given error string.
|
||||
func Errorhandler(err string, h http.HandlerFunc) {
|
||||
ErrorMaps[err] = h
|
||||
}
|
||||
|
||||
// register default error http handlers, 404,401,403,500 and 503.
|
||||
func RegisterErrorHandler() {
|
||||
if _, ok := ErrorMaps["404"]; !ok {
|
||||
ErrorMaps["404"] = NotFound
|
||||
func registerDefaultErrorHandler() {
|
||||
if _, ok := ErrorMaps["401"]; !ok {
|
||||
Errorhandler("401", unauthorized)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["401"]; !ok {
|
||||
ErrorMaps["401"] = Unauthorized
|
||||
if _, ok := ErrorMaps["402"]; !ok {
|
||||
Errorhandler("402", paymentRequired)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["403"]; !ok {
|
||||
ErrorMaps["403"] = Forbidden
|
||||
Errorhandler("403", forbidden)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["503"]; !ok {
|
||||
ErrorMaps["503"] = ServiceUnavailable
|
||||
if _, ok := ErrorMaps["404"]; !ok {
|
||||
Errorhandler("404", notFound)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["405"]; !ok {
|
||||
Errorhandler("405", methodNotAllowed)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["500"]; !ok {
|
||||
ErrorMaps["500"] = InternalServerError
|
||||
Errorhandler("500", internalServerError)
|
||||
}
|
||||
if _, ok := ErrorMaps["501"]; !ok {
|
||||
Errorhandler("501", notImplemented)
|
||||
}
|
||||
if _, ok := ErrorMaps["502"]; !ok {
|
||||
Errorhandler("502", badGateway)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["503"]; !ok {
|
||||
Errorhandler("503", serviceUnavailable)
|
||||
}
|
||||
|
||||
if _, ok := ErrorMaps["504"]; !ok {
|
||||
Errorhandler("504", gatewayTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorHandler registers http.HandlerFunc to each http err code string.
|
||||
// usage:
|
||||
// beego.ErrorHandler("404",NotFound)
|
||||
// beego.ErrorHandler("500",InternalServerError)
|
||||
func Errorhandler(code string, h http.HandlerFunc) *App {
|
||||
errinfo := &errorInfo{}
|
||||
errinfo.errorType = errorTypeHandler
|
||||
errinfo.handler = h
|
||||
errinfo.method = code
|
||||
ErrorMaps[code] = errinfo
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// ErrorController registers ControllerInterface to each http err code string.
|
||||
// usage:
|
||||
// beego.ErrorHandler(&controllers.ErrorController{})
|
||||
func ErrorController(c ControllerInterface) *App {
|
||||
reflectVal := reflect.ValueOf(c)
|
||||
rt := reflectVal.Type()
|
||||
ct := reflect.Indirect(reflectVal).Type()
|
||||
for i := 0; i < rt.NumMethod(); i++ {
|
||||
if !utils.InSlice(rt.Method(i).Name, exceptMethod) && strings.HasPrefix(rt.Method(i).Name, "Error") {
|
||||
errinfo := &errorInfo{}
|
||||
errinfo.errorType = errorTypeController
|
||||
errinfo.controllerType = ct
|
||||
errinfo.method = rt.Method(i).Name
|
||||
errname := strings.TrimPrefix(rt.Method(i).Name, "Error")
|
||||
ErrorMaps[errname] = errinfo
|
||||
}
|
||||
}
|
||||
return BeeApp
|
||||
}
|
||||
|
||||
// show error string as simple text message.
|
||||
// if error string is empty, show 500 error as default.
|
||||
func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg string) {
|
||||
func exception(errcode string, ctx *context.Context) {
|
||||
code, err := strconv.Atoi(errcode)
|
||||
if err != nil {
|
||||
code = 503
|
||||
}
|
||||
ctx.ResponseWriter.WriteHeader(code)
|
||||
if h, ok := ErrorMaps[errcode]; ok {
|
||||
isint, err := strconv.Atoi(errcode)
|
||||
if err != nil {
|
||||
isint = 500
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(isint)
|
||||
h(w, r)
|
||||
executeError(h, ctx)
|
||||
return
|
||||
} else if h, ok := ErrorMaps["503"]; ok {
|
||||
executeError(h, ctx)
|
||||
return
|
||||
} else {
|
||||
isint, err := strconv.Atoi(errcode)
|
||||
if err != nil {
|
||||
isint = 500
|
||||
}
|
||||
if isint == 400 {
|
||||
msg = "404 page not found"
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.WriteHeader(isint)
|
||||
fmt.Fprintln(w, msg)
|
||||
return
|
||||
ctx.WriteString(errcode)
|
||||
}
|
||||
}
|
||||
|
||||
func executeError(err *errorInfo, ctx *context.Context) {
|
||||
if err.errorType == errorTypeHandler {
|
||||
err.handler(ctx.ResponseWriter, ctx.Request)
|
||||
return
|
||||
}
|
||||
if err.errorType == errorTypeController {
|
||||
//Invoke the request handler
|
||||
vc := reflect.New(err.controllerType)
|
||||
execController, ok := vc.Interface().(ControllerInterface)
|
||||
if !ok {
|
||||
panic("controller is not ControllerInterface")
|
||||
}
|
||||
//call the controller init function
|
||||
execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
|
||||
|
||||
//call prepare function
|
||||
execController.Prepare()
|
||||
|
||||
execController.URLMapping()
|
||||
|
||||
in := make([]reflect.Value, 0)
|
||||
method := vc.MethodByName(err.method)
|
||||
method.Call(in)
|
||||
|
||||
//render template
|
||||
if ctx.Output.Status == 0 {
|
||||
if AutoRender {
|
||||
if err := execController.Render(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finish all runrouter. release resource
|
||||
execController.Finish()
|
||||
}
|
||||
}
|
@ -17,47 +17,47 @@ type ObjectController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (this *ObjectController) Post() {
|
||||
func (o *ObjectController) Post() {
|
||||
var ob models.Object
|
||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||
objectid := models.AddOne(ob)
|
||||
this.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||
this.ServeJson()
|
||||
o.Data["json"] = map[string]string{"ObjectId": objectid}
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Get() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Get() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
if objectId != "" {
|
||||
ob, err := models.GetOne(objectId)
|
||||
if err != nil {
|
||||
this.Data["json"] = err
|
||||
o.Data["json"] = err
|
||||
} else {
|
||||
this.Data["json"] = ob
|
||||
o.Data["json"] = ob
|
||||
}
|
||||
} else {
|
||||
obs := models.GetAll()
|
||||
this.Data["json"] = obs
|
||||
o.Data["json"] = obs
|
||||
}
|
||||
this.ServeJson()
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Put() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Put() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
var ob models.Object
|
||||
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
|
||||
json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
|
||||
|
||||
err := models.Update(objectId, ob.Score)
|
||||
if err != nil {
|
||||
this.Data["json"] = err
|
||||
o.Data["json"] = err
|
||||
} else {
|
||||
this.Data["json"] = "update success!"
|
||||
o.Data["json"] = "update success!"
|
||||
}
|
||||
this.ServeJson()
|
||||
o.ServeJson()
|
||||
}
|
||||
|
||||
func (this *ObjectController) Delete() {
|
||||
objectId := this.Ctx.Input.Params[":objectId"]
|
||||
func (o *ObjectController) Delete() {
|
||||
objectId := o.Ctx.Input.Params[":objectId"]
|
||||
models.Delete(objectId)
|
||||
this.Data["json"] = "delete success!"
|
||||
this.ServeJson()
|
||||
o.Data["json"] = "delete success!"
|
||||
o.ServeJson()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ type MainController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (this *MainController) Get() {
|
||||
this.Data["host"] = this.Ctx.Request.Host
|
||||
this.TplNames = "index.tpl"
|
||||
func (m *MainController) Get() {
|
||||
m.Data["host"] = m.Ctx.Request.Host
|
||||
m.TplNames = "index.tpl"
|
||||
}
|
||||
|
@ -150,14 +150,14 @@ type WSController struct {
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
|
||||
func (this *WSController) Get() {
|
||||
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil)
|
||||
func (w *WSController) Get() {
|
||||
ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
|
||||
if _, ok := err.(websocket.HandshakeError); ok {
|
||||
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400)
|
||||
http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400)
|
||||
return
|
||||
} else if err != nil {
|
||||
return
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Chat Example</title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
||||
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
|
12
filter.go
12
filter.go
@ -14,12 +14,18 @@
|
||||
|
||||
package beego
|
||||
|
||||
import "github.com/astaxie/beego/context"
|
||||
|
||||
// FilterFunc defines filter function type.
|
||||
type FilterFunc func(*context.Context)
|
||||
|
||||
// FilterRouter defines filter operation before controller handler execution.
|
||||
// it can match patterned url and do filter function when action arrives.
|
||||
type FilterRouter struct {
|
||||
filterFunc FilterFunc
|
||||
tree *Tree
|
||||
pattern string
|
||||
filterFunc FilterFunc
|
||||
tree *Tree
|
||||
pattern string
|
||||
returnOnOutput bool
|
||||
}
|
||||
|
||||
// ValidRouter check current request is valid for this filter.
|
||||
|
18
flash.go
18
flash.go
@ -32,6 +32,24 @@ func NewFlash() *FlashData {
|
||||
}
|
||||
}
|
||||
|
||||
// Set message to flash
|
||||
func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
fd.Data[key] = msg
|
||||
} else {
|
||||
fd.Data[key] = fmt.Sprintf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Success writes success message to flash.
|
||||
func (fd *FlashData) Success(msg string, args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
fd.Data["success"] = msg
|
||||
} else {
|
||||
fd.Data["success"] = fmt.Sprintf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Notice writes notice message to flash.
|
||||
func (fd *FlashData) Notice(msg string, args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
|
@ -25,12 +25,12 @@ type TestFlashController struct {
|
||||
Controller
|
||||
}
|
||||
|
||||
func (this *TestFlashController) TestWriteFlash() {
|
||||
func (t *TestFlashController) TestWriteFlash() {
|
||||
flash := NewFlash()
|
||||
flash.Notice("TestFlashString")
|
||||
flash.Store(&this.Controller)
|
||||
flash.Store(&t.Controller)
|
||||
// we choose to serve json because we don't want to load a template html file
|
||||
this.ServeJson(true)
|
||||
t.ServeJson(true)
|
||||
}
|
||||
|
||||
func TestFlashHeader(t *testing.T) {
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -252,6 +253,59 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BeegoHttpRequest) buildUrl(paramBody string) {
|
||||
// build GET url with query string
|
||||
if b.req.Method == "GET" && len(paramBody) > 0 {
|
||||
if strings.Index(b.url, "?") != -1 {
|
||||
b.url += "&" + paramBody
|
||||
} else {
|
||||
b.url = b.url + "?" + paramBody
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// build POST url and body
|
||||
if b.req.Method == "POST" && b.req.Body == nil {
|
||||
// with files
|
||||
if len(b.files) > 0 {
|
||||
pr, pw := io.Pipe()
|
||||
bodyWriter := multipart.NewWriter(pw)
|
||||
go func() {
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
for k, v := range b.params {
|
||||
bodyWriter.WriteField(k, v)
|
||||
}
|
||||
bodyWriter.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
b.Header("Content-Type", bodyWriter.FormDataContentType())
|
||||
b.req.Body = ioutil.NopCloser(pr)
|
||||
return
|
||||
}
|
||||
|
||||
// with params
|
||||
if len(paramBody) > 0 {
|
||||
b.Header("Content-Type", "application/x-www-form-urlencoded")
|
||||
b.Body(paramBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
if b.resp.StatusCode != 0 {
|
||||
return b.resp, nil
|
||||
@ -269,46 +323,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
paramBody = paramBody[0 : len(paramBody)-1]
|
||||
}
|
||||
|
||||
if b.req.Method == "GET" && len(paramBody) > 0 {
|
||||
if strings.Index(b.url, "?") != -1 {
|
||||
b.url += "&" + paramBody
|
||||
} else {
|
||||
b.url = b.url + "?" + paramBody
|
||||
}
|
||||
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
|
||||
if len(b.files) > 0 {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for k, v := range b.params {
|
||||
bodyWriter.WriteField(k, v)
|
||||
}
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
b.Header("Content-Type", contentType)
|
||||
b.req.Body = ioutil.NopCloser(bodyBuf)
|
||||
b.req.ContentLength = int64(bodyBuf.Len())
|
||||
} else {
|
||||
b.Header("Content-Type", "application/x-www-form-urlencoded")
|
||||
b.Body(paramBody)
|
||||
}
|
||||
}
|
||||
|
||||
b.buildUrl(paramBody)
|
||||
url, err := url.Parse(b.url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -340,14 +355,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var jar http.CookieJar
|
||||
var jar http.CookieJar = nil
|
||||
if b.setting.EnableCookie {
|
||||
if defaultCookieJar == nil {
|
||||
createDefaultCookie()
|
||||
}
|
||||
jar = defaultCookieJar
|
||||
} else {
|
||||
jar = nil
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
@ -355,7 +368,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
Jar: jar,
|
||||
}
|
||||
|
||||
if b.setting.UserAgent != "" {
|
||||
if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
|
||||
b.req.Header.Set("User-Agent", b.setting.UserAgent)
|
||||
}
|
||||
|
||||
@ -400,12 +413,11 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
b.body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.body = data
|
||||
return data, nil
|
||||
return b.body, nil
|
||||
}
|
||||
|
||||
// ToFile saves the body data in response to one file.
|
||||
@ -436,8 +448,7 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, v)
|
||||
return err
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// ToXml returns the map that marshals from the body bytes as xml in response .
|
||||
@ -447,8 +458,7 @@ func (b *BeegoHttpRequest) ToXml(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = xml.Unmarshal(data, v)
|
||||
return err
|
||||
return xml.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// Response executes request client gets response mannually.
|
||||
|
@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostFile(t *testing.T) {
|
||||
v := "smallfish"
|
||||
req := Post("http://httpbin.org/post")
|
||||
req.Param("username", v)
|
||||
req.PostFile("uploadfile", "httplib_test.go")
|
||||
//func TestPostFile(t *testing.T) {
|
||||
// v := "smallfish"
|
||||
// req := Post("http://httpbin.org/post")
|
||||
// req.Debug(true)
|
||||
// req.Param("username", v)
|
||||
// req.PostFile("uploadfile", "httplib_test.go")
|
||||
|
||||
str, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(str)
|
||||
// str, err := req.String()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// t.Log(str)
|
||||
|
||||
n := strings.Index(str, v)
|
||||
if n == -1 {
|
||||
t.Fatal(v + " not found in post")
|
||||
}
|
||||
}
|
||||
// n := strings.Index(str, v)
|
||||
// if n == -1 {
|
||||
// t.Fatal(v + " not found in post")
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestSimplePut(t *testing.T) {
|
||||
str, err := Put("http://httpbin.org/put").String()
|
||||
@ -203,3 +204,13 @@ func TestToFile(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeader(t *testing.T) {
|
||||
req := Get("http://httpbin.org/headers")
|
||||
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
|
||||
str, err := req.String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(str)
|
||||
}
|
||||
|
11
logs/conn.go
11
logs/conn.go
@ -43,11 +43,7 @@ func NewConn() LoggerInterface {
|
||||
// init connection writer with json config.
|
||||
// json config only need key "level".
|
||||
func (c *ConnWriter) Init(jsonconfig string) error {
|
||||
err := json.Unmarshal([]byte(jsonconfig), c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in connection.
|
||||
@ -77,10 +73,9 @@ func (c *ConnWriter) Flush() {
|
||||
|
||||
// destroy connection writer and close tcp listener.
|
||||
func (c *ConnWriter) Destroy() {
|
||||
if c.innerWriter == nil {
|
||||
return
|
||||
if c.innerWriter != nil {
|
||||
c.innerWriter.Close()
|
||||
}
|
||||
c.innerWriter.Close()
|
||||
}
|
||||
|
||||
func (c *ConnWriter) connect() error {
|
||||
|
@ -50,9 +50,10 @@ type ConsoleWriter struct {
|
||||
|
||||
// create ConsoleWriter returning as LoggerInterface.
|
||||
func NewConsole() LoggerInterface {
|
||||
cw := new(ConsoleWriter)
|
||||
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
cw.Level = LevelDebug
|
||||
cw := &ConsoleWriter{
|
||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Level: LevelDebug,
|
||||
}
|
||||
return cw
|
||||
}
|
||||
|
||||
@ -62,11 +63,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
|
||||
if len(jsonconfig) == 0 {
|
||||
return nil
|
||||
}
|
||||
err := json.Unmarshal([]byte(jsonconfig), c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal([]byte(jsonconfig), c)
|
||||
}
|
||||
|
||||
// write message in console.
|
||||
@ -76,9 +73,10 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
|
||||
}
|
||||
if goos := runtime.GOOS; goos == "windows" {
|
||||
c.lg.Println(msg)
|
||||
} else {
|
||||
c.lg.Println(colors[level](msg))
|
||||
return nil
|
||||
}
|
||||
c.lg.Println(colors[level](msg))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
43
logs/file.go
43
logs/file.go
@ -15,10 +15,11 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -122,11 +123,7 @@ func (w *FileLogWriter) startLogger() error {
|
||||
return err
|
||||
}
|
||||
w.mw.SetFd(fd)
|
||||
err = w.initFd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return w.initFd()
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) docheck(size int) {
|
||||
@ -169,18 +166,44 @@ func (w *FileLogWriter) initFd() error {
|
||||
}
|
||||
w.maxsize_cursize = int(finfo.Size())
|
||||
w.daily_opendate = time.Now().Day()
|
||||
w.maxlines_curlines = 0
|
||||
if finfo.Size() > 0 {
|
||||
content, err := ioutil.ReadFile(w.Filename)
|
||||
count, err := w.lines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.maxlines_curlines = len(strings.Split(string(content), "\n"))
|
||||
} else {
|
||||
w.maxlines_curlines = 0
|
||||
w.maxlines_curlines = count
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) lines() (int, error) {
|
||||
fd, err := os.Open(w.Filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
buf := make([]byte, 32768) // 32k
|
||||
count := 0
|
||||
lineSep := []byte{'\n'}
|
||||
|
||||
for {
|
||||
c, err := fd.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return count, err
|
||||
}
|
||||
|
||||
count += bytes.Count(buf[:c], lineSep)
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// DoRotate means it need to write file in new file.
|
||||
// new file name like xx.log.2013-01-01.2
|
||||
func (w *FileLogWriter) DoRotate() error {
|
||||
|
@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
|
||||
lm.level = loglevel
|
||||
if bl.enableFuncCallDepth {
|
||||
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
||||
if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) {
|
||||
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
|
||||
}
|
||||
if ok {
|
||||
_, filename := path.Split(file)
|
||||
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
|
||||
@ -289,9 +292,9 @@ func (bl *BeeLogger) Close() {
|
||||
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
for _, l := range bl.outputs {
|
||||
l.Flush()
|
||||
|
@ -25,7 +25,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
subjectPhrase = "Diagnostic message from server"
|
||||
// no usage
|
||||
// subjectPhrase = "Diagnostic message from server"
|
||||
)
|
||||
|
||||
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
|
||||
@ -146,9 +147,7 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
|
||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
|
||||
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
|
||||
|
||||
err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||
|
||||
return err
|
||||
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||
}
|
||||
|
||||
// implementing method. empty.
|
||||
|
@ -1,49 +0,0 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package middleware
|
||||
|
||||
import "fmt"
|
||||
|
||||
// http exceptions
|
||||
type HTTPException struct {
|
||||
StatusCode int // http status code 4xx, 5xx
|
||||
Description string
|
||||
}
|
||||
|
||||
// return http exception error string, e.g. "400 Bad Request".
|
||||
func (e *HTTPException) Error() string {
|
||||
return fmt.Sprintf("%d %s", e.StatusCode, e.Description)
|
||||
}
|
||||
|
||||
// map of http exceptions for each http status code int.
|
||||
// defined 400,401,403,404,405,500,502,503 and 504 default.
|
||||
var HTTPExceptionMaps map[int]HTTPException
|
||||
|
||||
func init() {
|
||||
HTTPExceptionMaps = make(map[int]HTTPException)
|
||||
|
||||
// Normal 4XX HTTP Status
|
||||
HTTPExceptionMaps[400] = HTTPException{400, "Bad Request"}
|
||||
HTTPExceptionMaps[401] = HTTPException{401, "Unauthorized"}
|
||||
HTTPExceptionMaps[403] = HTTPException{403, "Forbidden"}
|
||||
HTTPExceptionMaps[404] = HTTPException{404, "Not Found"}
|
||||
HTTPExceptionMaps[405] = HTTPException{405, "Method Not Allowed"}
|
||||
|
||||
// Normal 5XX HTTP Status
|
||||
HTTPExceptionMaps[500] = HTTPException{500, "Internal Server Error"}
|
||||
HTTPExceptionMaps[502] = HTTPException{502, "Bad Gateway"}
|
||||
HTTPExceptionMaps[503] = HTTPException{503, "Service Unavailable"}
|
||||
HTTPExceptionMaps[504] = HTTPException{504, "Gateway Timeout"}
|
||||
}
|
@ -34,7 +34,6 @@ type Translation struct {
|
||||
}
|
||||
|
||||
func NewLocale(filepath string, defaultlocal string) *Translation {
|
||||
i18n := make(map[string]map[string]string)
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
panic("open " + filepath + " err :" + err.Error())
|
||||
@ -43,8 +42,9 @@ func NewLocale(filepath string, defaultlocal string) *Translation {
|
||||
if err != nil {
|
||||
panic("read " + filepath + " err :" + err.Error())
|
||||
}
|
||||
err = json.Unmarshal(data, &i18n)
|
||||
if err != nil {
|
||||
|
||||
i18n := make(map[string]map[string]string)
|
||||
if err = json.Unmarshal(data, &i18n); err != nil {
|
||||
panic("json.Unmarshal " + filepath + " err :" + err.Error())
|
||||
}
|
||||
return &Translation{
|
||||
|
46
migration/ddl.go
Normal file
46
migration/ddl.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package migration
|
||||
|
||||
type Table struct {
|
||||
TableName string
|
||||
Columns []*Column
|
||||
}
|
||||
|
||||
func (t *Table) Create() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *Table) Drop() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type Column struct {
|
||||
Name string
|
||||
Type string
|
||||
Default interface{}
|
||||
}
|
||||
|
||||
func Create(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func Drop(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func TableDDL(tbname string, columns ...Column) string {
|
||||
return ""
|
||||
}
|
@ -19,7 +19,6 @@ import (
|
||||
"strings"
|
||||
|
||||
beecontext "github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/middleware"
|
||||
)
|
||||
|
||||
type namespaceCond func(*beecontext.Context) bool
|
||||
@ -57,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
|
||||
func (n *Namespace) Cond(cond namespaceCond) *Namespace {
|
||||
fn := func(ctx *beecontext.Context) {
|
||||
if !cond(ctx) {
|
||||
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
|
||||
exception("405", ctx)
|
||||
}
|
||||
}
|
||||
if v, ok := n.handlers.filters[BeforeRouter]; ok {
|
||||
@ -217,7 +216,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
|
||||
n.handlers.routers[k] = t
|
||||
}
|
||||
}
|
||||
if n.handlers.enableFilter {
|
||||
if ni.handlers.enableFilter {
|
||||
for pos, filterList := range ni.handlers.filters {
|
||||
for _, mr := range filterList {
|
||||
t := NewTree()
|
||||
|
@ -6,8 +6,6 @@ A powerful orm framework for go.
|
||||
|
||||
It is heavily influenced by Django ORM, SQLAlchemy.
|
||||
|
||||
now, beta, unstable, may be changing some api make your app build failed.
|
||||
|
||||
**Support Database:**
|
||||
|
||||
* MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
|
||||
@ -119,7 +117,7 @@ o.Begin()
|
||||
...
|
||||
user := User{Name: "slene"}
|
||||
id, err := o.Insert(&user)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
o.Commit()
|
||||
} else {
|
||||
o.Rollback()
|
||||
|
@ -104,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
|
||||
typ += " " + "NOT NULL"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ)
|
||||
return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s",
|
||||
Q, fi.mi.table, Q,
|
||||
Q, fi.column, Q,
|
||||
typ, getColumnDefault(fi),
|
||||
)
|
||||
}
|
||||
|
||||
// create database creation string.
|
||||
@ -155,6 +159,9 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
//if fi.initial.String() != "" {
|
||||
// column += " DEFAULT " + fi.initial.String()
|
||||
//}
|
||||
|
||||
// Append attribute DEFAULT
|
||||
column += getColumnDefault(fi)
|
||||
|
||||
if fi.unique {
|
||||
column += " " + "UNIQUE"
|
||||
@ -239,3 +246,44 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands
|
||||
func getColumnDefault(fi *fieldInfo) string {
|
||||
var (
|
||||
v, t, d string
|
||||
)
|
||||
|
||||
// Skip default attribute if field is in relations
|
||||
if fi.rel || fi.reverse {
|
||||
return v
|
||||
}
|
||||
|
||||
t = " DEFAULT '%s' "
|
||||
|
||||
// These defaults will be useful if there no config value orm:"default" and NOT NULL is on
|
||||
switch fi.fieldType {
|
||||
case TypeDateField, TypeDateTimeField:
|
||||
return v;
|
||||
|
||||
case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField,
|
||||
TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField,
|
||||
TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField,
|
||||
TypeDecimalField:
|
||||
d = "0"
|
||||
}
|
||||
|
||||
if fi.colDefault {
|
||||
if !fi.initial.Exist() {
|
||||
v = fmt.Sprintf(t, "")
|
||||
} else {
|
||||
v = fmt.Sprintf(t, fi.initial.String())
|
||||
}
|
||||
} else {
|
||||
if !fi.null {
|
||||
v = fmt.Sprintf(t, d)
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ type fieldInfo struct {
|
||||
null bool
|
||||
index bool
|
||||
unique bool
|
||||
colDefault bool
|
||||
initial StrTo
|
||||
size int
|
||||
auto_now bool
|
||||
@ -280,6 +281,11 @@ checkType:
|
||||
fi.pk = attrs["pk"]
|
||||
fi.unique = attrs["unique"]
|
||||
|
||||
// Mark object property if there is attribute "default" in the orm configuration
|
||||
if _, ok := tags["default"]; ok {
|
||||
fi.colDefault = true
|
||||
}
|
||||
|
||||
switch fieldType {
|
||||
case RelManyToMany, RelReverseMany, RelReverseOne:
|
||||
fi.null = false
|
||||
|
@ -489,10 +489,6 @@ func (o *orm) Driver() Driver {
|
||||
return driver(o.alias.Name)
|
||||
}
|
||||
|
||||
func (o *orm) GetDB() dbQuerier {
|
||||
panic(ErrNotImplement)
|
||||
}
|
||||
|
||||
// create new orm
|
||||
func NewOrm() Ormer {
|
||||
BootStrap() // execute only once
|
||||
|
57
orm/qb.go
Normal file
57
orm/qb.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
import "errors"
|
||||
|
||||
type QueryBuilder interface {
|
||||
Select(fields ...string) QueryBuilder
|
||||
From(tables ...string) QueryBuilder
|
||||
InnerJoin(table string) QueryBuilder
|
||||
LeftJoin(table string) QueryBuilder
|
||||
RightJoin(table string) QueryBuilder
|
||||
On(cond string) QueryBuilder
|
||||
Where(cond string) QueryBuilder
|
||||
And(cond string) QueryBuilder
|
||||
Or(cond string) QueryBuilder
|
||||
In(vals ...string) QueryBuilder
|
||||
OrderBy(fields ...string) QueryBuilder
|
||||
Asc() QueryBuilder
|
||||
Desc() QueryBuilder
|
||||
Limit(limit int) QueryBuilder
|
||||
Offset(offset int) QueryBuilder
|
||||
GroupBy(fields ...string) QueryBuilder
|
||||
Having(cond string) QueryBuilder
|
||||
Update(tables ...string) QueryBuilder
|
||||
Set(kv ...string) QueryBuilder
|
||||
Delete(tables ...string) QueryBuilder
|
||||
InsertInto(table string, fields ...string) QueryBuilder
|
||||
Values(vals ...string) QueryBuilder
|
||||
Subquery(sub string, alias string) string
|
||||
String() string
|
||||
}
|
||||
|
||||
func NewQueryBuilder(driver string) (qb QueryBuilder, err error) {
|
||||
if driver == "mysql" {
|
||||
qb = new(MySQLQueryBuilder)
|
||||
} else if driver == "postgres" {
|
||||
err = errors.New("postgres query builder is not supported yet!")
|
||||
} else if driver == "sqlite" {
|
||||
err = errors.New("sqlite query builder is not supported yet!")
|
||||
} else {
|
||||
err = errors.New("unknown driver for query builder!")
|
||||
}
|
||||
return
|
||||
}
|
153
orm/qb_mysql.go
Normal file
153
orm/qb_mysql.go
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const COMMA_SPACE = ", "
|
||||
|
||||
type MySQLQueryBuilder struct {
|
||||
Tokens []string
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "INNER JOIN", table)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "LEFT JOIN", table)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "ON", cond)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "WHERE", cond)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "AND", cond)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "OR", cond)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, COMMA_SPACE), ")")
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Asc() QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "ASC")
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Desc() QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "DESC")
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "HAVING", cond)
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Update(tables ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, COMMA_SPACE))
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "DELETE")
|
||||
if len(tables) != 0 {
|
||||
qb.Tokens = append(qb.Tokens, strings.Join(tables, COMMA_SPACE))
|
||||
}
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder {
|
||||
qb.Tokens = append(qb.Tokens, "INSERT INTO", table)
|
||||
if len(fields) != 0 {
|
||||
fieldsStr := strings.Join(fields, COMMA_SPACE)
|
||||
qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")")
|
||||
}
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Values(vals ...string) QueryBuilder {
|
||||
valsStr := strings.Join(vals, COMMA_SPACE)
|
||||
qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")")
|
||||
return qb
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string {
|
||||
return fmt.Sprintf("(%s) AS %s", sub, alias)
|
||||
}
|
||||
|
||||
func (qb *MySQLQueryBuilder) String() string {
|
||||
return strings.Join(qb.Tokens, " ")
|
||||
}
|
@ -51,7 +51,6 @@ type Ormer interface {
|
||||
Rollback() error
|
||||
Raw(string, ...interface{}) RawSeter
|
||||
Driver() Driver
|
||||
GetDB() dbQuerier
|
||||
}
|
||||
|
||||
// insert prepared statement
|
||||
|
13
parser.go
13
parser.go
@ -42,20 +42,25 @@ func init() {
|
||||
|
||||
var (
|
||||
lastupdateFilename string = "lastupdate.tmp"
|
||||
commentFilename string
|
||||
pkgLastupdate map[string]int64
|
||||
genInfoList map[string][]ControllerComments
|
||||
)
|
||||
|
||||
const COMMENTFL = "commentsRouter_"
|
||||
|
||||
func init() {
|
||||
pkgLastupdate = make(map[string]int64)
|
||||
genInfoList = make(map[string][]ControllerComments)
|
||||
}
|
||||
|
||||
func parserPkg(pkgRealpath, pkgpath string) error {
|
||||
rep := strings.NewReplacer("/", "_", ".", "_")
|
||||
commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go"
|
||||
if !compareFile(pkgRealpath) {
|
||||
Info(pkgRealpath + " don't has updated")
|
||||
Info(pkgRealpath + " has not changed, not reloading")
|
||||
return nil
|
||||
}
|
||||
genInfoList = make(map[string][]ControllerComments)
|
||||
fileSet := token.NewFileSet()
|
||||
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
|
||||
name := info.Name()
|
||||
@ -155,7 +160,7 @@ func genRouterCode() {
|
||||
}
|
||||
}
|
||||
if globalinfo != "" {
|
||||
f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go"))
|
||||
f, err := os.Create(path.Join(workPath, "routers", commentFilename))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -165,7 +170,7 @@ func genRouterCode() {
|
||||
}
|
||||
|
||||
func compareFile(pkgRealpath string) bool {
|
||||
if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) {
|
||||
if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) {
|
||||
return true
|
||||
}
|
||||
if utils.FileExists(path.Join(workPath, lastupdateFilename)) {
|
||||
|
@ -217,6 +217,7 @@ func Allow(opts *Options) beego.FilterFunc {
|
||||
ctx.Output.Header(key, value)
|
||||
}
|
||||
ctx.Output.SetStatus(http.StatusOK)
|
||||
ctx.WriteString("")
|
||||
return
|
||||
}
|
||||
headers = opts.Header(origin)
|
||||
|
213
router.go
213
router.go
@ -30,7 +30,6 @@ import (
|
||||
"time"
|
||||
|
||||
beecontext "github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/toolbox"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
@ -72,9 +71,31 @@ var (
|
||||
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
||||
"GetControllerAndAction"}
|
||||
|
||||
url_placeholder = "{{placeholder}}"
|
||||
url_placeholder = "{{placeholder}}"
|
||||
DefaultLogFilter FilterHandler = &logFilter{}
|
||||
)
|
||||
|
||||
type FilterHandler interface {
|
||||
Filter(*beecontext.Context) bool
|
||||
}
|
||||
|
||||
// default log filter static file will not show
|
||||
type logFilter struct {
|
||||
}
|
||||
|
||||
func (l *logFilter) Filter(ctx *beecontext.Context) bool {
|
||||
requestPath := path.Clean(ctx.Input.Request.URL.Path)
|
||||
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
|
||||
return true
|
||||
}
|
||||
for prefix, _ := range StaticDir {
|
||||
if strings.HasPrefix(requestPath, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
|
||||
func ExceptMethodAppend(action string) {
|
||||
exceptMethod = append(exceptMethod, action)
|
||||
@ -131,7 +152,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
|
||||
if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
|
||||
methods[strings.ToUpper(m)] = colon[1]
|
||||
} else {
|
||||
panic(colon[1] + " method doesn't exist in the controller " + t.Name())
|
||||
panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
|
||||
}
|
||||
} else {
|
||||
panic(v + " is an invalid method mapping. Method doesn't exist " + m)
|
||||
@ -163,6 +184,9 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
|
||||
}
|
||||
|
||||
func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {
|
||||
if !RouterCaseSensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
if t, ok := p.routers[method]; ok {
|
||||
t.AddRouter(pattern, r)
|
||||
} else {
|
||||
@ -376,11 +400,21 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface)
|
||||
}
|
||||
|
||||
// Add a FilterFunc with pattern rule and action constant.
|
||||
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error {
|
||||
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
|
||||
func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
|
||||
|
||||
mr := new(FilterRouter)
|
||||
mr.tree = NewTree()
|
||||
mr.pattern = pattern
|
||||
mr.filterFunc = filter
|
||||
if !RouterCaseSensitive {
|
||||
pattern = strings.ToLower(pattern)
|
||||
}
|
||||
if len(params) == 0 {
|
||||
mr.returnOnOutput = true
|
||||
} else {
|
||||
mr.returnOnOutput = params[0]
|
||||
}
|
||||
mr.tree.AddRouter(pattern, true)
|
||||
return p.insertFilterRouter(pos, mr)
|
||||
}
|
||||
@ -394,7 +428,7 @@ func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error
|
||||
|
||||
// UrlFor does another controller handler in this request function.
|
||||
// it can access any controller method.
|
||||
func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
|
||||
func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) string {
|
||||
paths := strings.Split(endpoint, ".")
|
||||
if len(paths) <= 1 {
|
||||
Warn("urlfor endpoint must like path.controller.method")
|
||||
@ -409,16 +443,16 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
|
||||
key := ""
|
||||
for k, v := range values {
|
||||
if k%2 == 0 {
|
||||
key = v
|
||||
key = fmt.Sprint(v)
|
||||
} else {
|
||||
params[key] = v
|
||||
params[key] = fmt.Sprint(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
controllName := strings.Join(paths[:len(paths)-1], ".")
|
||||
controllName := strings.Join(paths[:len(paths)-1], "/")
|
||||
methodName := paths[len(paths)-1]
|
||||
for _, t := range p.routers {
|
||||
ok, url := p.geturl(t, "/", controllName, methodName, params)
|
||||
for m, t := range p.routers {
|
||||
ok, url := p.geturl(t, "/", controllName, methodName, params, m)
|
||||
if ok {
|
||||
return url
|
||||
}
|
||||
@ -426,24 +460,25 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) {
|
||||
func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
|
||||
for k, subtree := range t.fixrouters {
|
||||
u := path.Join(url, k)
|
||||
ok, u := p.geturl(subtree, u, controllName, methodName, params)
|
||||
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
|
||||
if ok {
|
||||
return ok, u
|
||||
}
|
||||
}
|
||||
if t.wildcard != nil {
|
||||
url = path.Join(url, url_placeholder)
|
||||
ok, u := p.geturl(t.wildcard, url, controllName, methodName, params)
|
||||
u := path.Join(url, url_placeholder)
|
||||
ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
|
||||
if ok {
|
||||
return ok, u
|
||||
}
|
||||
}
|
||||
for _, l := range t.leaves {
|
||||
if c, ok := l.runObject.(*controllerInfo); ok {
|
||||
if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName {
|
||||
if c.routerType == routerTypeBeego &&
|
||||
strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
|
||||
find := false
|
||||
if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok {
|
||||
if len(c.methods) == 0 {
|
||||
@ -455,8 +490,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
for _, md := range c.methods {
|
||||
if md == methodName {
|
||||
for m, md := range c.methods {
|
||||
if (m == "*" || m == httpMethod) && md == methodName {
|
||||
find = true
|
||||
}
|
||||
}
|
||||
@ -541,7 +576,6 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
|
||||
|
||||
// Implement http.Handler interface.
|
||||
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
defer p.recoverPanic(rw, r)
|
||||
starttime := time.Now()
|
||||
var runrouter reflect.Type
|
||||
var findrouter bool
|
||||
@ -564,15 +598,23 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
context.Output.Context = context
|
||||
context.Output.EnableGzip = EnableGzip
|
||||
|
||||
defer p.recoverPanic(context)
|
||||
|
||||
var urlPath string
|
||||
if !RouterCaseSensitive {
|
||||
urlPath = strings.ToLower(r.URL.Path)
|
||||
} else {
|
||||
urlPath = r.URL.Path
|
||||
}
|
||||
// defined filter function
|
||||
do_filter := func(pos int) (started bool) {
|
||||
if p.enableFilter {
|
||||
if l, ok := p.filters[pos]; ok {
|
||||
for _, filterR := range l {
|
||||
if ok, p := filterR.ValidRouter(r.URL.Path); ok {
|
||||
if ok, p := filterR.ValidRouter(urlPath); ok {
|
||||
context.Input.Params = p
|
||||
filterR.filterFunc(context)
|
||||
if w.started {
|
||||
if filterR.returnOnOutput && w.started {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -602,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
// session init
|
||||
if SessionOn {
|
||||
context.Input.CruSession = GlobalSessions.SessionStart(w, r)
|
||||
var err error
|
||||
context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
|
||||
if err != nil {
|
||||
Error(err)
|
||||
exception("503", context)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
context.Input.CruSession.SessionRelease(w)
|
||||
}()
|
||||
@ -626,8 +674,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
if !findrouter {
|
||||
if t, ok := p.routers[r.Method]; ok {
|
||||
runObject, p := t.Match(r.URL.Path)
|
||||
http_method := r.Method
|
||||
|
||||
if http_method == "POST" && context.Input.Query("_method") == "PUT" {
|
||||
http_method = "PUT"
|
||||
}
|
||||
|
||||
if http_method == "POST" && context.Input.Query("_method") == "DELETE" {
|
||||
http_method = "DELETE"
|
||||
}
|
||||
|
||||
if t, ok := p.routers[http_method]; ok {
|
||||
runObject, p := t.Match(urlPath)
|
||||
if r, ok := runObject.(*controllerInfo); ok {
|
||||
routerInfo = r
|
||||
findrouter = true
|
||||
@ -645,7 +703,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
//if no matches to url, throw a not found exception
|
||||
if !findrouter {
|
||||
middleware.Exception("404", rw, r, "")
|
||||
exception("404", context)
|
||||
goto Admin
|
||||
}
|
||||
|
||||
@ -661,7 +719,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
isRunable = true
|
||||
routerInfo.runfunction(context)
|
||||
} else {
|
||||
middleware.Exception("405", rw, r, "Method Not Allowed")
|
||||
exception("405", context)
|
||||
goto Admin
|
||||
}
|
||||
} else if routerInfo.routerType == routerTypeHandler {
|
||||
@ -705,7 +763,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||
if EnableXSRF {
|
||||
execController.XsrfToken()
|
||||
if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
|
||||
(r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) {
|
||||
(r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) {
|
||||
execController.CheckXsrfCookie()
|
||||
}
|
||||
}
|
||||
@ -772,7 +830,7 @@ Admin:
|
||||
}
|
||||
}
|
||||
|
||||
if RunMode == "dev" {
|
||||
if RunMode == "dev" || AccessLogs {
|
||||
var devinfo string
|
||||
if findrouter {
|
||||
if routerInfo != nil {
|
||||
@ -783,7 +841,9 @@ Admin:
|
||||
} else {
|
||||
devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch")
|
||||
}
|
||||
Debug(devinfo)
|
||||
if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) {
|
||||
Debug(devinfo)
|
||||
}
|
||||
}
|
||||
|
||||
// Call WriteHeader if status code has been set changed
|
||||
@ -792,26 +852,51 @@ Admin:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) {
|
||||
func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) {
|
||||
if err := recover(); err != nil {
|
||||
if err == USERSTOPRUN {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(middleware.HTTPException); ok {
|
||||
// catch intented errors, only for HTTP 4XX and 5XX
|
||||
} else {
|
||||
if RunMode == "dev" {
|
||||
if !RecoverPanic {
|
||||
panic(err)
|
||||
} else {
|
||||
if ErrorsShow {
|
||||
if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
|
||||
handler(rw, r)
|
||||
return
|
||||
}
|
||||
if RunMode == "dev" {
|
||||
if !RecoverPanic {
|
||||
panic(err)
|
||||
} else {
|
||||
if ErrorsShow {
|
||||
if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok {
|
||||
executeError(handler, context)
|
||||
return
|
||||
}
|
||||
var stack string
|
||||
Critical("the request url is ", r.URL.Path)
|
||||
}
|
||||
var stack string
|
||||
Critical("the request url is ", context.Input.Url())
|
||||
Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
showErr(err, context, stack)
|
||||
}
|
||||
} else {
|
||||
if !RecoverPanic {
|
||||
panic(err)
|
||||
} else {
|
||||
// in production model show all infomation
|
||||
if ErrorsShow {
|
||||
if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok {
|
||||
executeError(handler, context)
|
||||
return
|
||||
} else if handler, ok := ErrorMaps["503"]; ok {
|
||||
executeError(handler, context)
|
||||
return
|
||||
} else {
|
||||
context.WriteString(fmt.Sprint(err))
|
||||
}
|
||||
} else {
|
||||
Critical("the request url is ", context.Input.Url())
|
||||
Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
@ -819,55 +904,13 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
|
||||
break
|
||||
}
|
||||
Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
middleware.ShowErr(err, rw, r, stack)
|
||||
}
|
||||
} else {
|
||||
if !RecoverPanic {
|
||||
panic(err)
|
||||
} else {
|
||||
// in production model show all infomation
|
||||
if ErrorsShow {
|
||||
handler := p.getErrorHandler(fmt.Sprint(err))
|
||||
handler(rw, r)
|
||||
return
|
||||
} else {
|
||||
Critical("the request url is ", r.URL.Path)
|
||||
Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i++ {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
Critical(fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// there always should be error handler that sets error code accordingly for all unhandled errors.
|
||||
// in order to have custom UI for error page it's necessary to override "500" error.
|
||||
func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
|
||||
handler := middleware.SimpleServerError
|
||||
ok := true
|
||||
if errorCode != "" {
|
||||
handler, ok = middleware.ErrorMaps[errorCode]
|
||||
if !ok {
|
||||
handler, ok = middleware.ErrorMaps["500"]
|
||||
}
|
||||
if !ok || handler == nil {
|
||||
handler = middleware.SimpleServerError
|
||||
}
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
//responseWriter is a wrapper for the http.ResponseWriter
|
||||
//started set to true if response was written to then don't execute other handler
|
||||
type responseWriter struct {
|
||||
|
224
router_test.go
224
router_test.go
@ -17,6 +17,7 @@ package beego
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
@ -26,33 +27,33 @@ type TestController struct {
|
||||
Controller
|
||||
}
|
||||
|
||||
func (this *TestController) Get() {
|
||||
this.Data["Username"] = "astaxie"
|
||||
this.Ctx.Output.Body([]byte("ok"))
|
||||
func (tc *TestController) Get() {
|
||||
tc.Data["Username"] = "astaxie"
|
||||
tc.Ctx.Output.Body([]byte("ok"))
|
||||
}
|
||||
|
||||
func (this *TestController) Post() {
|
||||
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
|
||||
func (tc *TestController) Post() {
|
||||
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
|
||||
}
|
||||
|
||||
func (this *TestController) Param() {
|
||||
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name")))
|
||||
func (tc *TestController) Param() {
|
||||
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
|
||||
}
|
||||
|
||||
func (this *TestController) List() {
|
||||
this.Ctx.Output.Body([]byte("i am list"))
|
||||
func (tc *TestController) List() {
|
||||
tc.Ctx.Output.Body([]byte("i am list"))
|
||||
}
|
||||
|
||||
func (this *TestController) Params() {
|
||||
this.Ctx.Output.Body([]byte(this.Ctx.Input.Params["0"] + this.Ctx.Input.Params["1"] + this.Ctx.Input.Params["2"]))
|
||||
func (tc *TestController) Params() {
|
||||
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"]))
|
||||
}
|
||||
|
||||
func (this *TestController) Myext() {
|
||||
this.Ctx.Output.Body([]byte(this.Ctx.Input.Param(":ext")))
|
||||
func (tc *TestController) Myext() {
|
||||
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
|
||||
}
|
||||
|
||||
func (this *TestController) GetUrl() {
|
||||
this.Ctx.Output.Body([]byte(this.UrlFor(".Myext")))
|
||||
func (tc *TestController) GetUrl() {
|
||||
tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext")))
|
||||
}
|
||||
|
||||
func (t *TestController) GetParams() {
|
||||
@ -385,3 +386,196 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request
|
||||
|
||||
return recorder, request
|
||||
}
|
||||
|
||||
// Execution point: BeforeRouter
|
||||
// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
|
||||
func TestFilterBeforeRouter(t *testing.T) {
|
||||
testName := "TestFilterBeforeRouter"
|
||||
url := "/beforeRouter"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter1") == false {
|
||||
t.Errorf(testName + " BeforeRouter did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == true {
|
||||
t.Errorf(testName + " BeforeRouter did not return properly")
|
||||
}
|
||||
}
|
||||
|
||||
// Execution point: BeforeExec
|
||||
// expectation: only BeforeExec function is executed, match as router determines route only
|
||||
func TestFilterBeforeExec(t *testing.T) {
|
||||
testName := "TestFilterBeforeExec"
|
||||
url := "/beforeExec"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, BeforeExec, beegoBeforeExec1)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec1") == false {
|
||||
t.Errorf(testName + " BeforeExec did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == true {
|
||||
t.Errorf(testName + " BeforeExec did not return properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
}
|
||||
|
||||
// Execution point: AfterExec
|
||||
// expectation: only AfterExec function is executed, match as router handles
|
||||
func TestFilterAfterExec(t *testing.T) {
|
||||
testName := "TestFilterAfterExec"
|
||||
url := "/afterExec"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, AfterExec, beegoAfterExec1)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "AfterExec1") == false {
|
||||
t.Errorf(testName + " AfterExec did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
|
||||
t.Errorf(testName + " BeforeExec ran in error")
|
||||
}
|
||||
}
|
||||
|
||||
// Execution point: FinishRouter
|
||||
// expectation: only FinishRouter function is executed, match as router handles
|
||||
func TestFilterFinishRouter(t *testing.T) {
|
||||
testName := "TestFilterFinishRouter"
|
||||
url := "/finishRouter"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, AfterExec, beegoFilterNoOutput)
|
||||
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == true {
|
||||
t.Errorf(testName + " FinishRouter did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "AfterExec1") == true {
|
||||
t.Errorf(testName + " AfterExec ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeRouter") == true {
|
||||
t.Errorf(testName + " BeforeRouter ran in error")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "BeforeExec") == true {
|
||||
t.Errorf(testName + " BeforeExec ran in error")
|
||||
}
|
||||
}
|
||||
|
||||
// Execution point: FinishRouter
|
||||
// expectation: only first FinishRouter function is executed, match as router handles
|
||||
func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
|
||||
testName := "TestFilterFinishRouterMultiFirstOnly"
|
||||
url := "/finishRouterMultiFirstOnly"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1)
|
||||
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
|
||||
t.Errorf(testName + " FinishRouter1 did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
// not expected in body
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter2") == true {
|
||||
t.Errorf(testName + " FinishRouter2 did run")
|
||||
}
|
||||
}
|
||||
|
||||
// Execution point: FinishRouter
|
||||
// expectation: both FinishRouter functions execute, match as router handles
|
||||
func TestFilterFinishRouterMulti(t *testing.T) {
|
||||
testName := "TestFilterFinishRouterMulti"
|
||||
url := "/finishRouterMulti"
|
||||
|
||||
mux := NewControllerRegister()
|
||||
mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false)
|
||||
mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)
|
||||
|
||||
mux.Get(url, beegoFilterFunc)
|
||||
|
||||
rw, r := testRequest("GET", url)
|
||||
mux.ServeHTTP(rw, r)
|
||||
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter1") == false {
|
||||
t.Errorf(testName + " FinishRouter1 did not run")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "hello") == false {
|
||||
t.Errorf(testName + " handler did not run properly")
|
||||
}
|
||||
if strings.Contains(rw.Body.String(), "FinishRouter2") == false {
|
||||
t.Errorf(testName + " FinishRouter2 did not run properly")
|
||||
}
|
||||
}
|
||||
|
||||
func beegoFilterNoOutput(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
func beegoBeforeRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeRouter1")
|
||||
}
|
||||
func beegoBeforeRouter2(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeRouter2")
|
||||
}
|
||||
func beegoBeforeExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeExec1")
|
||||
}
|
||||
func beegoBeforeExec2(ctx *context.Context) {
|
||||
ctx.WriteString("|BeforeExec2")
|
||||
}
|
||||
func beegoAfterExec1(ctx *context.Context) {
|
||||
ctx.WriteString("|AfterExec1")
|
||||
}
|
||||
func beegoAfterExec2(ctx *context.Context) {
|
||||
ctx.WriteString("|AfterExec2")
|
||||
}
|
||||
func beegoFinishRouter1(ctx *context.Context) {
|
||||
ctx.WriteString("|FinishRouter1")
|
||||
}
|
||||
func beegoFinishRouter2(ctx *context.Context) {
|
||||
ctx.WriteString("|FinishRouter2")
|
||||
}
|
||||
|
180
session/ledis/ledis_session.go
Normal file
180
session/ledis/ledis_session.go
Normal file
@ -0,0 +1,180 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/astaxie/beego/session"
|
||||
"github.com/siddontang/ledisdb/config"
|
||||
"github.com/siddontang/ledisdb/ledis"
|
||||
)
|
||||
|
||||
var ledispder = &LedisProvider{}
|
||||
var c *ledis.DB
|
||||
|
||||
// ledis session store
|
||||
type LedisSessionStore struct {
|
||||
sid string
|
||||
lock sync.RWMutex
|
||||
values map[interface{}]interface{}
|
||||
maxlifetime int64
|
||||
}
|
||||
|
||||
// set value in ledis session
|
||||
func (ls *LedisSessionStore) Set(key, value interface{}) error {
|
||||
ls.lock.Lock()
|
||||
defer ls.lock.Unlock()
|
||||
ls.values[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// get value in ledis session
|
||||
func (ls *LedisSessionStore) Get(key interface{}) interface{} {
|
||||
ls.lock.RLock()
|
||||
defer ls.lock.RUnlock()
|
||||
if v, ok := ls.values[key]; ok {
|
||||
return v
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// delete value in ledis session
|
||||
func (ls *LedisSessionStore) Delete(key interface{}) error {
|
||||
ls.lock.Lock()
|
||||
defer ls.lock.Unlock()
|
||||
delete(ls.values, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// clear all values in ledis session
|
||||
func (ls *LedisSessionStore) Flush() error {
|
||||
ls.lock.Lock()
|
||||
defer ls.lock.Unlock()
|
||||
ls.values = make(map[interface{}]interface{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// get ledis session id
|
||||
func (ls *LedisSessionStore) SessionID() string {
|
||||
return ls.sid
|
||||
}
|
||||
|
||||
// save session values to ledis
|
||||
func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||
b, err := session.EncodeGob(ls.values)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.Set([]byte(ls.sid), b)
|
||||
c.Expire([]byte(ls.sid), ls.maxlifetime)
|
||||
}
|
||||
|
||||
// ledis session provider
|
||||
type LedisProvider struct {
|
||||
maxlifetime int64
|
||||
savePath string
|
||||
db int
|
||||
}
|
||||
|
||||
// init ledis session
|
||||
// savepath like ledis server saveDataPath,pool size
|
||||
// e.g. 127.0.0.1:6379,100,astaxie
|
||||
func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
var err error
|
||||
lp.maxlifetime = maxlifetime
|
||||
configs := strings.Split(savepath, ",")
|
||||
if len(configs) == 1 {
|
||||
lp.savePath = configs[0]
|
||||
} else if len(configs) == 2 {
|
||||
lp.savePath = configs[0]
|
||||
lp.db, err = strconv.Atoi(configs[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cfg := new(config.Config)
|
||||
cfg.DataDir = lp.savePath
|
||||
nowLedis, err := ledis.Open(cfg)
|
||||
c, err = nowLedis.Select(lp.db)
|
||||
if err != nil {
|
||||
println(err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// read ledis session by sid
|
||||
func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
|
||||
kvs, err := c.Get([]byte(sid))
|
||||
var kv map[interface{}]interface{}
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob(kvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// check ledis session exist by sid
|
||||
func (lp *LedisProvider) SessionExist(sid string) bool {
|
||||
count, _ := c.Exists([]byte(sid))
|
||||
if count == 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// generate new sid for ledis session
|
||||
func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
|
||||
count, _ := c.Exists([]byte(sid))
|
||||
if count == 0 {
|
||||
// oldsid doesn't exists, set the new sid directly
|
||||
// ignore error here, since if it return error
|
||||
// the existed value will be 0
|
||||
c.Set([]byte(sid), []byte(""))
|
||||
c.Expire([]byte(sid), lp.maxlifetime)
|
||||
} else {
|
||||
data, _ := c.Get([]byte(oldsid))
|
||||
c.Set([]byte(sid), data)
|
||||
c.Expire([]byte(sid), lp.maxlifetime)
|
||||
}
|
||||
kvs, err := c.Get([]byte(sid))
|
||||
var kv map[interface{}]interface{}
|
||||
if len(kvs) == 0 {
|
||||
kv = make(map[interface{}]interface{})
|
||||
} else {
|
||||
kv, err = session.DecodeGob([]byte(kvs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
// delete ledis session by id
|
||||
func (lp *LedisProvider) SessionDestroy(sid string) error {
|
||||
c.Del([]byte(sid))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Impelment method, no used.
|
||||
func (lp *LedisProvider) SessionGC() {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo
|
||||
func (lp *LedisProvider) SessionAll() int {
|
||||
return 0
|
||||
}
|
||||
func init() {
|
||||
session.Register("ledis", ledispder)
|
||||
}
|
@ -109,7 +109,7 @@ func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime)
|
||||
c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
|
||||
}
|
||||
|
||||
// redis session provider
|
||||
@ -118,12 +118,13 @@ type RedisProvider struct {
|
||||
savePath string
|
||||
poolsize int
|
||||
password string
|
||||
dbNum int
|
||||
poollist *redis.Pool
|
||||
}
|
||||
|
||||
// init redis session
|
||||
// savepath like redis server addr,pool size,password
|
||||
// e.g. 127.0.0.1:6379,100,astaxie
|
||||
// savepath like redis server addr,pool size,password,dbnum
|
||||
// e.g. 127.0.0.1:6379,100,astaxie,0
|
||||
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
rp.maxlifetime = maxlifetime
|
||||
configs := strings.Split(savePath, ",")
|
||||
@ -143,6 +144,16 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
if len(configs) > 2 {
|
||||
rp.password = configs[2]
|
||||
}
|
||||
if len(configs) > 3 {
|
||||
dbnum, err := strconv.Atoi(configs[1])
|
||||
if err != nil || dbnum < 0 {
|
||||
rp.dbNum = 0
|
||||
} else {
|
||||
rp.dbNum = dbnum
|
||||
}
|
||||
} else {
|
||||
rp.dbNum = 0
|
||||
}
|
||||
rp.poollist = redis.NewPool(func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", rp.savePath)
|
||||
if err != nil {
|
||||
@ -154,6 +165,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = c.Do("SELECT", rp.dbNum)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, err
|
||||
}, rp.poolsize)
|
||||
|
||||
|
@ -29,7 +29,10 @@ func TestCookie(t *testing.T) {
|
||||
}
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
sess := globalSessions.SessionStart(w, r)
|
||||
sess, err := globalSessions.SessionStart(w, r)
|
||||
if err != nil {
|
||||
t.Fatal("set error,", err)
|
||||
}
|
||||
err = sess.Set("username", "astaxie")
|
||||
if err != nil {
|
||||
t.Fatal("set error,", err)
|
||||
|
@ -26,9 +26,12 @@ func TestMem(t *testing.T) {
|
||||
go globalSessions.GC()
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
sess := globalSessions.SessionStart(w, r)
|
||||
sess, err := globalSessions.SessionStart(w, r)
|
||||
if err != nil {
|
||||
t.Fatal("set error,", err)
|
||||
}
|
||||
defer sess.SessionRelease(w)
|
||||
err := sess.Set("username", "astaxie")
|
||||
err = sess.Set("username", "astaxie")
|
||||
if err != nil {
|
||||
t.Fatal("set error,", err)
|
||||
}
|
||||
|
@ -28,19 +28,13 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
// SessionStore contains all data for one session process with specific id.
|
||||
@ -81,16 +75,15 @@ func Register(name string, provide Provider) {
|
||||
}
|
||||
|
||||
type managerConfig struct {
|
||||
CookieName string `json:"cookieName"`
|
||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||
Gclifetime int64 `json:"gclifetime"`
|
||||
Maxlifetime int64 `json:"maxLifetime"`
|
||||
Secure bool `json:"secure"`
|
||||
SessionIDHashFunc string `json:"sessionIDHashFunc"`
|
||||
SessionIDHashKey string `json:"sessionIDHashKey"`
|
||||
CookieLifeTime int `json:"cookieLifeTime"`
|
||||
ProviderConfig string `json:"providerConfig"`
|
||||
Domain string `json:"domain"`
|
||||
CookieName string `json:"cookieName"`
|
||||
EnableSetCookie bool `json:"enableSetCookie,omitempty"`
|
||||
Gclifetime int64 `json:"gclifetime"`
|
||||
Maxlifetime int64 `json:"maxLifetime"`
|
||||
Secure bool `json:"secure"`
|
||||
CookieLifeTime int `json:"cookieLifeTime"`
|
||||
ProviderConfig string `json:"providerConfig"`
|
||||
Domain string `json:"domain"`
|
||||
SessionIdLength int64 `json:"sessionIdLength"`
|
||||
}
|
||||
|
||||
// Manager contains Provider and its configuration.
|
||||
@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cf.SessionIDHashFunc == "" {
|
||||
cf.SessionIDHashFunc = "sha1"
|
||||
}
|
||||
if cf.SessionIDHashKey == "" {
|
||||
cf.SessionIDHashKey = string(generateRandomKey(16))
|
||||
|
||||
if cf.SessionIdLength == 0 {
|
||||
cf.SessionIdLength = 16
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) {
|
||||
|
||||
// Start session. generate or read the session id from http request.
|
||||
// if session id exists, return SessionStore with this id.
|
||||
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
||||
cookie, err := r.Cookie(manager.config.CookieName)
|
||||
if err != nil || cookie.Value == "" {
|
||||
sid := manager.sessionId(r)
|
||||
session, _ = manager.provider.SessionRead(sid)
|
||||
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) {
|
||||
cookie, errs := r.Cookie(manager.config.CookieName)
|
||||
if errs != nil || cookie.Value == "" {
|
||||
sid, errs := manager.sessionId(r)
|
||||
if errs != nil {
|
||||
return nil, errs
|
||||
}
|
||||
session, err = manager.provider.SessionRead(sid)
|
||||
cookie = &http.Cookie{Name: manager.config.CookieName,
|
||||
Value: url.QueryEscape(sid),
|
||||
Path: "/",
|
||||
@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
|
||||
}
|
||||
r.AddCookie(cookie)
|
||||
} else {
|
||||
sid, _ := url.QueryUnescape(cookie.Value)
|
||||
sid, errs := url.QueryUnescape(cookie.Value)
|
||||
if errs != nil {
|
||||
return nil, errs
|
||||
}
|
||||
if manager.provider.SessionExist(sid) {
|
||||
session, _ = manager.provider.SessionRead(sid)
|
||||
session, err = manager.provider.SessionRead(sid)
|
||||
} else {
|
||||
sid = manager.sessionId(r)
|
||||
session, _ = manager.provider.SessionRead(sid)
|
||||
sid, err = manager.sessionId(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session, err = manager.provider.SessionRead(sid)
|
||||
cookie = &http.Cookie{Name: manager.config.CookieName,
|
||||
Value: url.QueryEscape(sid),
|
||||
Path: "/",
|
||||
@ -219,7 +219,10 @@ func (manager *Manager) GC() {
|
||||
|
||||
// Regenerate a session id for this SessionStore who's id is saving in http request.
|
||||
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
||||
sid := manager.sessionId(r)
|
||||
sid, err := manager.sessionId(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cookie, err := r.Cookie(manager.config.CookieName)
|
||||
if err != nil && cookie.Value == "" {
|
||||
//delete old cookie
|
||||
@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int {
|
||||
return manager.provider.SessionAll()
|
||||
}
|
||||
|
||||
// Set hash function for generating session id.
|
||||
func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
|
||||
manager.config.SessionIDHashFunc = hasfunc
|
||||
manager.config.SessionIDHashKey = hashkey
|
||||
}
|
||||
|
||||
// Set cookie with https.
|
||||
func (manager *Manager) SetSecure(secure bool) {
|
||||
manager.config.Secure = secure
|
||||
}
|
||||
|
||||
// generate session id with rand string, unix nano time, remote addr by hash function.
|
||||
func (manager *Manager) sessionId(r *http.Request) (sid string) {
|
||||
bs := make([]byte, 32)
|
||||
if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil {
|
||||
bs = utils.RandomCreateBytes(32)
|
||||
func (manager *Manager) sessionId(r *http.Request) (string, error) {
|
||||
b := make([]byte, manager.config.SessionIdLength)
|
||||
n, err := rand.Read(b)
|
||||
if n != len(b) || err != nil {
|
||||
return "", fmt.Errorf("Could not successfully read from the system CSPRNG.")
|
||||
}
|
||||
sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs)
|
||||
if manager.config.SessionIDHashFunc == "md5" {
|
||||
h := md5.New()
|
||||
h.Write([]byte(sig))
|
||||
sid = hex.EncodeToString(h.Sum(nil))
|
||||
} else if manager.config.SessionIDHashFunc == "sha1" {
|
||||
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
|
||||
fmt.Fprintf(h, "%s", sig)
|
||||
sid = hex.EncodeToString(h.Sum(nil))
|
||||
} else {
|
||||
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
|
||||
fmt.Fprintf(h, "%s", sig)
|
||||
sid = hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
return
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/astaxie/beego/middleware"
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
@ -31,6 +30,7 @@ func serverStaticRouter(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
requestPath := path.Clean(ctx.Input.Request.URL.Path)
|
||||
i := 0
|
||||
for prefix, staticDir := range StaticDir {
|
||||
if len(prefix) == 0 {
|
||||
continue
|
||||
@ -41,8 +41,13 @@ func serverStaticRouter(ctx *context.Context) {
|
||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
|
||||
return
|
||||
} else {
|
||||
http.NotFound(ctx.ResponseWriter, ctx.Request)
|
||||
return
|
||||
i++
|
||||
if i == len(StaticDir) {
|
||||
http.NotFound(ctx.ResponseWriter, ctx.Request)
|
||||
return
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(requestPath, prefix) {
|
||||
@ -59,9 +64,20 @@ func serverStaticRouter(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
//if the request is dir and DirectoryIndex is false then
|
||||
if finfo.IsDir() && !DirectoryIndex {
|
||||
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden")
|
||||
return
|
||||
if finfo.IsDir() {
|
||||
if !DirectoryIndex {
|
||||
exception("403", ctx)
|
||||
return
|
||||
} else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' {
|
||||
http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302)
|
||||
return
|
||||
}
|
||||
} else if strings.HasSuffix(requestPath, "/index.html") {
|
||||
file := path.Join(staticDir, requestPath)
|
||||
if utils.FileExists(file) {
|
||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request
|
||||
|
@ -42,6 +42,9 @@ func init() {
|
||||
beegoTplFuncMap["dateformat"] = DateFormat
|
||||
beegoTplFuncMap["date"] = Date
|
||||
beegoTplFuncMap["compare"] = Compare
|
||||
beegoTplFuncMap["compare_not"] = CompareNot
|
||||
beegoTplFuncMap["not_nil"] = NotNil
|
||||
beegoTplFuncMap["not_null"] = NotNil
|
||||
beegoTplFuncMap["substr"] = Substr
|
||||
beegoTplFuncMap["html2str"] = Html2str
|
||||
beegoTplFuncMap["str2html"] = Str2html
|
||||
|
@ -139,6 +139,14 @@ func Compare(a, b interface{}) (equal bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func CompareNot(a, b interface{}) (equal bool) {
|
||||
return ! Compare(a, b)
|
||||
}
|
||||
|
||||
func NotNil(a interface{}) (is_nil bool) {
|
||||
return CompareNot(a, nil)
|
||||
}
|
||||
|
||||
func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
|
||||
switch returnType {
|
||||
case "String":
|
||||
@ -246,7 +254,7 @@ func Htmlunquote(src string) string {
|
||||
// /user/John%20Doe
|
||||
//
|
||||
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md
|
||||
func UrlFor(endpoint string, values ...string) string {
|
||||
func UrlFor(endpoint string, values ...interface{}) string {
|
||||
return BeeApp.Handlers.UrlFor(endpoint, values...)
|
||||
}
|
||||
|
||||
@ -302,6 +310,14 @@ func ParseForm(form url.Values, obj interface{}) error {
|
||||
|
||||
switch fieldT.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
|
||||
fieldV.SetBool(true)
|
||||
continue
|
||||
}
|
||||
if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
|
||||
fieldV.SetBool(false)
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -329,11 +345,45 @@ func ParseForm(form url.Values, obj interface{}) error {
|
||||
fieldV.Set(reflect.ValueOf(value))
|
||||
case reflect.String:
|
||||
fieldV.SetString(value)
|
||||
case reflect.Struct:
|
||||
switch fieldT.Type.String() {
|
||||
case "time.Time":
|
||||
format := time.RFC3339
|
||||
if len(tags) > 1 {
|
||||
format = tags[1]
|
||||
}
|
||||
t, err := time.Parse(format, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldV.Set(reflect.ValueOf(t))
|
||||
}
|
||||
case reflect.Slice:
|
||||
if fieldT.Type == sliceOfInts {
|
||||
formVals := form[tag]
|
||||
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
|
||||
for i := 0; i < len(formVals); i++ {
|
||||
val, err := strconv.Atoi(formVals[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldV.Index(i).SetInt(int64(val))
|
||||
}
|
||||
} else if fieldT.Type == sliceOfStrings {
|
||||
formVals := form[tag]
|
||||
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
|
||||
for i := 0; i < len(formVals); i++ {
|
||||
fieldV.Index(i).SetString(formVals[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var sliceOfInts = reflect.TypeOf([]int(nil))
|
||||
var sliceOfStrings = reflect.TypeOf([]string(nil))
|
||||
|
||||
var unKind = map[reflect.Kind]bool{
|
||||
reflect.Uintptr: true,
|
||||
reflect.Complex64: true,
|
||||
@ -368,23 +418,31 @@ func RenderForm(obj interface{}) template.HTML {
|
||||
|
||||
fieldT := objT.Field(i)
|
||||
|
||||
label, name, fType, ignored := parseFormTag(fieldT)
|
||||
label, name, fType, id, class, ignored := parseFormTag(fieldT)
|
||||
if ignored {
|
||||
continue
|
||||
}
|
||||
|
||||
raw = append(raw, renderFormField(label, name, fType, fieldV.Interface()))
|
||||
raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class))
|
||||
}
|
||||
return template.HTML(strings.Join(raw, "</br>"))
|
||||
}
|
||||
|
||||
// renderFormField returns a string containing HTML of a single form field.
|
||||
func renderFormField(label, name, fType string, value interface{}) string {
|
||||
if isValidForInput(fType) {
|
||||
return fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, label, name, fType, value)
|
||||
func renderFormField(label, name, fType string, value interface{}, id string, class string) string {
|
||||
if id != "" {
|
||||
id = " id=\"" + id + "\""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`%v<%v name="%v">%v</%v>`, label, fType, name, value, fType)
|
||||
if class != "" {
|
||||
class = " class=\"" + class + "\""
|
||||
}
|
||||
|
||||
if isValidForInput(fType) {
|
||||
return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v">`, label, id, class, name, fType, value)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`%v<%v%v%v name="%v">%v</%v>`, label, fType, id, class, name, value, fType)
|
||||
}
|
||||
|
||||
// isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
|
||||
@ -400,12 +458,14 @@ func isValidForInput(fType string) bool {
|
||||
|
||||
// parseFormTag takes the stuct-tag of a StructField and parses the `form` value.
|
||||
// returned are the form label, name-property, type and wether the field should be ignored.
|
||||
func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) {
|
||||
func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool) {
|
||||
tags := strings.Split(fieldT.Tag.Get("form"), ",")
|
||||
label = fieldT.Name + ": "
|
||||
name = fieldT.Name
|
||||
fType = "text"
|
||||
ignored = false
|
||||
id = fieldT.Tag.Get("id")
|
||||
class = fieldT.Tag.Get("class")
|
||||
|
||||
switch len(tags) {
|
||||
case 1:
|
||||
|
@ -72,7 +72,7 @@ func TestDate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
func TestCompareRelated(t *testing.T) {
|
||||
if !Compare("abc", "abc") {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
@ -82,6 +82,15 @@ func TestCompare(t *testing.T) {
|
||||
if !Compare("1", 1) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if CompareNot("abc", "abc") {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if !CompareNot("abc", "aBc") {
|
||||
t.Error("should be not equal")
|
||||
}
|
||||
if !NotNil("a string") {
|
||||
t.Error("should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHtmlquote(t *testing.T) {
|
||||
@ -102,12 +111,14 @@ func TestHtmlunquote(t *testing.T) {
|
||||
|
||||
func TestParseForm(t *testing.T) {
|
||||
type user struct {
|
||||
Id int `form:"-"`
|
||||
tag string `form:"tag"`
|
||||
Name interface{} `form:"username"`
|
||||
Age int `form:"age,text"`
|
||||
Email string
|
||||
Intro string `form:",textarea"`
|
||||
Id int `form:"-"`
|
||||
tag string `form:"tag"`
|
||||
Name interface{} `form:"username"`
|
||||
Age int `form:"age,text"`
|
||||
Email string
|
||||
Intro string `form:",textarea"`
|
||||
StrBool bool `form:"strbool"`
|
||||
Date time.Time `form:"date,2006-01-02"`
|
||||
}
|
||||
|
||||
u := user{}
|
||||
@ -119,6 +130,8 @@ func TestParseForm(t *testing.T) {
|
||||
"age": []string{"40"},
|
||||
"Email": []string{"test@gmail.com"},
|
||||
"Intro": []string{"I am an engineer!"},
|
||||
"strbool": []string{"yes"},
|
||||
"date": []string{"2014-11-12"},
|
||||
}
|
||||
if err := ParseForm(form, u); err == nil {
|
||||
t.Fatal("nothing will be changed")
|
||||
@ -144,6 +157,13 @@ func TestParseForm(t *testing.T) {
|
||||
if u.Intro != "I am an engineer!" {
|
||||
t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
|
||||
}
|
||||
if u.StrBool != true {
|
||||
t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
|
||||
}
|
||||
y, m, d := u.Date.Date()
|
||||
if y != 2014 || m.String() != "November" || d != 12 {
|
||||
t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderForm(t *testing.T) {
|
||||
@ -175,12 +195,12 @@ func TestRenderForm(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRenderFormField(t *testing.T) {
|
||||
html := renderFormField("Label: ", "Name", "text", "Value")
|
||||
html := renderFormField("Label: ", "Name", "text", "Value", "", "")
|
||||
if html != `Label: <input name="Name" type="text" value="Value">` {
|
||||
t.Errorf("Wrong html output for input[type=text]: %v ", html)
|
||||
}
|
||||
|
||||
html = renderFormField("Label: ", "Name", "textarea", "Value")
|
||||
html = renderFormField("Label: ", "Name", "textarea", "Value", "", "")
|
||||
if html != `Label: <textarea name="Name">Value</textarea>` {
|
||||
t.Errorf("Wrong html output for textarea: %v ", html)
|
||||
}
|
||||
@ -192,33 +212,34 @@ func TestParseFormTag(t *testing.T) {
|
||||
All int `form:"name,text,年龄:"`
|
||||
NoName int `form:",hidden,年龄:"`
|
||||
OnlyLabel int `form:",,年龄:"`
|
||||
OnlyName int `form:"name"`
|
||||
OnlyName int `form:"name" id:"name" class:"form-name"`
|
||||
Ignored int `form:"-"`
|
||||
}
|
||||
|
||||
objT := reflect.TypeOf(&user{}).Elem()
|
||||
|
||||
label, name, fType, ignored := parseFormTag(objT.Field(0))
|
||||
label, name, fType, id, class, ignored := parseFormTag(objT.Field(0))
|
||||
if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) {
|
||||
t.Errorf("Form Tag with name, label and type was not correctly parsed.")
|
||||
}
|
||||
|
||||
label, name, fType, ignored = parseFormTag(objT.Field(1))
|
||||
label, name, fType, id, class, ignored = parseFormTag(objT.Field(1))
|
||||
if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) {
|
||||
t.Errorf("Form Tag with label and type but without name was not correctly parsed.")
|
||||
}
|
||||
|
||||
label, name, fType, ignored = parseFormTag(objT.Field(2))
|
||||
label, name, fType, id, class, ignored = parseFormTag(objT.Field(2))
|
||||
if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) {
|
||||
t.Errorf("Form Tag containing only label was not correctly parsed.")
|
||||
}
|
||||
|
||||
label, name, fType, ignored = parseFormTag(objT.Field(3))
|
||||
if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) {
|
||||
label, name, fType, id, class, ignored = parseFormTag(objT.Field(3))
|
||||
if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false &&
|
||||
id == "name" && class == "form-name") {
|
||||
t.Errorf("Form Tag containing only name was not correctly parsed.")
|
||||
}
|
||||
|
||||
label, name, fType, ignored = parseFormTag(objT.Field(4))
|
||||
label, name, fType, id, class, ignored = parseFormTag(objT.Field(4))
|
||||
if ignored == false {
|
||||
t.Errorf("Form Tag that should be ignored was not correctly parsed.")
|
||||
}
|
||||
|
@ -111,6 +111,27 @@ func (m *UrlMap) GetMap() map[string]interface{} {
|
||||
return content
|
||||
}
|
||||
|
||||
func (m *UrlMap) GetMapData() []map[string]interface{} {
|
||||
|
||||
resultLists := make([]map[string]interface{}, 0)
|
||||
|
||||
for k, v := range m.urlmap {
|
||||
for kk, vv := range v {
|
||||
result := map[string]interface{}{
|
||||
"request_url": k,
|
||||
"method": kk,
|
||||
"times": vv.RequestNum,
|
||||
"total_time": toS(vv.TotalTime),
|
||||
"max_time": toS(vv.MaxTime),
|
||||
"min_time": toS(vv.MinTime),
|
||||
"avg_time": toS(time.Duration(int64(vv.TotalTime) / vv.RequestNum)),
|
||||
}
|
||||
resultLists = append(resultLists, result)
|
||||
}
|
||||
}
|
||||
return resultLists
|
||||
}
|
||||
|
||||
// global statistics data map
|
||||
var StatisticsMap *UrlMap
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -28,4 +29,12 @@ func TestStatics(t *testing.T) {
|
||||
StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
|
||||
StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
|
||||
t.Log(StatisticsMap.GetMap())
|
||||
|
||||
data := StatisticsMap.GetMapData()
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
|
||||
t.Log(string(b))
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ type TaskFunc func() error
|
||||
|
||||
// task interface
|
||||
type Tasker interface {
|
||||
GetSpec() string
|
||||
GetStatus() string
|
||||
Run() error
|
||||
SetNext(time.Time)
|
||||
@ -102,6 +103,7 @@ type taskerr struct {
|
||||
type Task struct {
|
||||
Taskname string
|
||||
Spec *Schedule
|
||||
SpecStr string
|
||||
DoFunc TaskFunc
|
||||
Prev time.Time
|
||||
Next time.Time
|
||||
@ -116,16 +118,22 @@ func NewTask(tname string, spec string, f TaskFunc) *Task {
|
||||
Taskname: tname,
|
||||
DoFunc: f,
|
||||
ErrLimit: 100,
|
||||
SpecStr: spec,
|
||||
}
|
||||
task.SetCron(spec)
|
||||
return task
|
||||
}
|
||||
|
||||
//get spec string
|
||||
func (s *Task) GetSpec() string {
|
||||
return s.SpecStr
|
||||
}
|
||||
|
||||
// get current task status
|
||||
func (tk *Task) GetStatus() string {
|
||||
var str string
|
||||
for _, v := range tk.Errlist {
|
||||
str += v.t.String() + ":" + v.errinfo + "\n"
|
||||
str += v.t.String() + ":" + v.errinfo + "<br>"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
21
tree.go
21
tree.go
@ -237,12 +237,18 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
|
||||
regexpStr = "/" + regexpStr
|
||||
}
|
||||
} else if reg != "" {
|
||||
for _, w := range params {
|
||||
if w == "." || w == ":" {
|
||||
continue
|
||||
if seg == "*.*" {
|
||||
regexpStr = "/([^.]+).(.+)"
|
||||
} else {
|
||||
for _, w := range params {
|
||||
|
||||
if w == "." || w == ":" {
|
||||
continue
|
||||
}
|
||||
regexpStr = "/([^/]+)" + regexpStr
|
||||
}
|
||||
regexpStr = "/([^/]+)" + regexpStr
|
||||
}
|
||||
|
||||
}
|
||||
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
|
||||
} else {
|
||||
@ -388,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
|
||||
}
|
||||
return true, params
|
||||
}
|
||||
if len(wildcardValues) <= j {
|
||||
return false, nil
|
||||
}
|
||||
params[v] = wildcardValues[j]
|
||||
j += 1
|
||||
}
|
||||
@ -396,6 +405,7 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
|
||||
}
|
||||
return true, params
|
||||
}
|
||||
|
||||
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
|
||||
return false, nil
|
||||
}
|
||||
@ -412,6 +422,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
|
||||
// "/admin/" -> ["admin"]
|
||||
// "/admin/users" -> ["admin", "users"]
|
||||
func splitPath(key string) []string {
|
||||
if key == "" {
|
||||
return []string{}
|
||||
}
|
||||
elements := strings.Split(key, "/")
|
||||
if elements[0] == "" {
|
||||
elements = elements[1:]
|
||||
|
@ -42,6 +42,9 @@ func init() {
|
||||
routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
|
||||
routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
|
||||
routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
|
||||
routers = append(routers, testinfo{"/dl/:width:int/:height:int/*.*",
|
||||
"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
|
||||
map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
|
||||
routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
|
||||
routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
|
||||
routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
|
||||
@ -146,7 +149,11 @@ func TestAddTree2(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSplitPath(t *testing.T) {
|
||||
a := splitPath("/")
|
||||
a := splitPath("")
|
||||
if len(a) != 0 {
|
||||
t.Fatal("/ should retrun []")
|
||||
}
|
||||
a = splitPath("/")
|
||||
if len(a) != 0 {
|
||||
t.Fatal("/ should retrun []")
|
||||
}
|
||||
|
@ -157,19 +157,37 @@ func (e *Email) Bytes() ([]byte, error) {
|
||||
}
|
||||
|
||||
// Add attach file to the send mail
|
||||
func (e *Email) AttachFile(filename string) (a *Attachment, err error) {
|
||||
func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
|
||||
if len(args) < 1 && len(args) > 2 {
|
||||
err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
|
||||
return
|
||||
}
|
||||
filename := args[0]
|
||||
id := ""
|
||||
if len(args) > 1 {
|
||||
id = args[1]
|
||||
}
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ct := mime.TypeByExtension(filepath.Ext(filename))
|
||||
basename := path.Base(filename)
|
||||
return e.Attach(f, basename, ct)
|
||||
return e.Attach(f, basename, ct, id)
|
||||
}
|
||||
|
||||
// Attach is used to attach content from an io.Reader to the email.
|
||||
// Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
|
||||
func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) {
|
||||
func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
|
||||
if len(args) < 1 && len(args) > 2 {
|
||||
err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
|
||||
return
|
||||
}
|
||||
c := args[0] //Content-Type
|
||||
id := ""
|
||||
if len(args) > 1 {
|
||||
id = args[1] //Content-ID
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
if _, err = io.Copy(&buffer, r); err != nil {
|
||||
return
|
||||
@ -186,7 +204,12 @@ func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, e
|
||||
// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
|
||||
at.Header.Set("Content-Type", "application/octet-stream")
|
||||
}
|
||||
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
|
||||
if id != "" {
|
||||
at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename))
|
||||
at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id))
|
||||
} else {
|
||||
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
|
||||
}
|
||||
at.Header.Set("Content-Transfer-Encoding", "base64")
|
||||
e.Attachments = append(e.Attachments, at)
|
||||
return at, nil
|
||||
@ -269,7 +292,7 @@ func qpEscape(dest []byte, c byte) {
|
||||
const nums = "0123456789ABCDEF"
|
||||
dest[0] = '='
|
||||
dest[1] = nums[(c&0xf0)>>4]
|
||||
dest[2] = nums[(c & 0xf)]
|
||||
dest[2] = nums[(c&0xf)]
|
||||
}
|
||||
|
||||
// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
|
||||
|
26
utils/pagination/controller.go
Normal file
26
utils/pagination/controller.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pagination
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/context"
|
||||
)
|
||||
|
||||
// Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
|
||||
func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
|
||||
paginator = NewPaginator(context.Request, per, nums)
|
||||
context.Input.Data["paginator"] = &paginator
|
||||
return
|
||||
}
|
59
utils/pagination/doc.go
Normal file
59
utils/pagination/doc.go
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
The pagination package provides utilities to setup a paginator within the
|
||||
context of a http request.
|
||||
|
||||
Usage
|
||||
|
||||
In your beego.Controller:
|
||||
|
||||
package controllers
|
||||
|
||||
import "github.com/astaxie/beego/utils/pagination"
|
||||
|
||||
type PostsController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (this *PostsController) ListAllPosts() {
|
||||
// sets this.Data["paginator"] with the current offset (from the url query param)
|
||||
postsPerPage := 20
|
||||
paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts())
|
||||
|
||||
// fetch the next 20 posts
|
||||
this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage)
|
||||
}
|
||||
|
||||
|
||||
In your view templates:
|
||||
|
||||
{{if .paginator.HasPages}}
|
||||
<ul class="pagination pagination">
|
||||
{{if .paginator.HasPrev}}
|
||||
<li><a href="{{.paginator.PageLinkFirst}}">{{ i18n .Lang "paginator.first_page"}}</a></li>
|
||||
<li><a href="{{.paginator.PageLinkPrev}}">«</a></li>
|
||||
{{else}}
|
||||
<li class="disabled"><a>{{ i18n .Lang "paginator.first_page"}}</a></li>
|
||||
<li class="disabled"><a>«</a></li>
|
||||
{{end}}
|
||||
{{range $index, $page := .paginator.Pages}}
|
||||
<li{{if $.paginator.IsActive .}} class="active"{{end}}>
|
||||
<a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{if .paginator.HasNext}}
|
||||
<li><a href="{{.paginator.PageLinkNext}}">»</a></li>
|
||||
<li><a href="{{.paginator.PageLinkLast}}">{{ i18n .Lang "paginator.last_page"}}</a></li>
|
||||
{{else}}
|
||||
<li class="disabled"><a>»</a></li>
|
||||
<li class="disabled"><a>{{ i18n .Lang "paginator.last_page"}}</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
|
||||
See also
|
||||
|
||||
http://beego.me/docs/mvc/view/page.md
|
||||
|
||||
*/
|
||||
package pagination
|
189
utils/pagination/paginator.go
Normal file
189
utils/pagination/paginator.go
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pagination
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Paginator within the state of a http request.
|
||||
type Paginator struct {
|
||||
Request *http.Request
|
||||
PerPageNums int
|
||||
MaxPages int
|
||||
|
||||
nums int64
|
||||
pageRange []int
|
||||
pageNums int
|
||||
page int
|
||||
}
|
||||
|
||||
// Returns the total number of pages.
|
||||
func (p *Paginator) PageNums() int {
|
||||
if p.pageNums != 0 {
|
||||
return p.pageNums
|
||||
}
|
||||
pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
|
||||
if p.MaxPages > 0 {
|
||||
pageNums = math.Min(pageNums, float64(p.MaxPages))
|
||||
}
|
||||
p.pageNums = int(pageNums)
|
||||
return p.pageNums
|
||||
}
|
||||
|
||||
// Returns the total number of items (e.g. from doing SQL count).
|
||||
func (p *Paginator) Nums() int64 {
|
||||
return p.nums
|
||||
}
|
||||
|
||||
// Sets the total number of items.
|
||||
func (p *Paginator) SetNums(nums interface{}) {
|
||||
p.nums, _ = ToInt64(nums)
|
||||
}
|
||||
|
||||
// Returns the current page.
|
||||
func (p *Paginator) Page() int {
|
||||
if p.page != 0 {
|
||||
return p.page
|
||||
}
|
||||
if p.Request.Form == nil {
|
||||
p.Request.ParseForm()
|
||||
}
|
||||
p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
|
||||
if p.page > p.PageNums() {
|
||||
p.page = p.PageNums()
|
||||
}
|
||||
if p.page <= 0 {
|
||||
p.page = 1
|
||||
}
|
||||
return p.page
|
||||
}
|
||||
|
||||
// Returns a list of all pages.
|
||||
//
|
||||
// Usage (in a view template):
|
||||
//
|
||||
// {{range $index, $page := .paginator.Pages}}
|
||||
// <li{{if $.paginator.IsActive .}} class="active"{{end}}>
|
||||
// <a href="{{$.paginator.PageLink $page}}">{{$page}}</a>
|
||||
// </li>
|
||||
// {{end}}
|
||||
func (p *Paginator) Pages() []int {
|
||||
if p.pageRange == nil && p.nums > 0 {
|
||||
var pages []int
|
||||
pageNums := p.PageNums()
|
||||
page := p.Page()
|
||||
switch {
|
||||
case page >= pageNums-4 && pageNums > 9:
|
||||
start := pageNums - 9 + 1
|
||||
pages = make([]int, 9)
|
||||
for i, _ := range pages {
|
||||
pages[i] = start + i
|
||||
}
|
||||
case page >= 5 && pageNums > 9:
|
||||
start := page - 5 + 1
|
||||
pages = make([]int, int(math.Min(9, float64(page+4+1))))
|
||||
for i, _ := range pages {
|
||||
pages[i] = start + i
|
||||
}
|
||||
default:
|
||||
pages = make([]int, int(math.Min(9, float64(pageNums))))
|
||||
for i, _ := range pages {
|
||||
pages[i] = i + 1
|
||||
}
|
||||
}
|
||||
p.pageRange = pages
|
||||
}
|
||||
return p.pageRange
|
||||
}
|
||||
|
||||
// Returns URL for a given page index.
|
||||
func (p *Paginator) PageLink(page int) string {
|
||||
link, _ := url.ParseRequestURI(p.Request.URL.String())
|
||||
values := link.Query()
|
||||
if page == 1 {
|
||||
values.Del("p")
|
||||
} else {
|
||||
values.Set("p", strconv.Itoa(page))
|
||||
}
|
||||
link.RawQuery = values.Encode()
|
||||
return link.String()
|
||||
}
|
||||
|
||||
// Returns URL to the previous page.
|
||||
func (p *Paginator) PageLinkPrev() (link string) {
|
||||
if p.HasPrev() {
|
||||
link = p.PageLink(p.Page() - 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns URL to the next page.
|
||||
func (p *Paginator) PageLinkNext() (link string) {
|
||||
if p.HasNext() {
|
||||
link = p.PageLink(p.Page() + 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns URL to the first page.
|
||||
func (p *Paginator) PageLinkFirst() (link string) {
|
||||
return p.PageLink(1)
|
||||
}
|
||||
|
||||
// Returns URL to the last page.
|
||||
func (p *Paginator) PageLinkLast() (link string) {
|
||||
return p.PageLink(p.PageNums())
|
||||
}
|
||||
|
||||
// Returns true if the current page has a predecessor.
|
||||
func (p *Paginator) HasPrev() bool {
|
||||
return p.Page() > 1
|
||||
}
|
||||
|
||||
// Returns true if the current page has a successor.
|
||||
func (p *Paginator) HasNext() bool {
|
||||
return p.Page() < p.PageNums()
|
||||
}
|
||||
|
||||
// Returns true if the given page index points to the current page.
|
||||
func (p *Paginator) IsActive(page int) bool {
|
||||
return p.Page() == page
|
||||
}
|
||||
|
||||
// Returns the current offset.
|
||||
func (p *Paginator) Offset() int {
|
||||
return (p.Page() - 1) * p.PerPageNums
|
||||
}
|
||||
|
||||
// Returns true if there is more than one page.
|
||||
func (p *Paginator) HasPages() bool {
|
||||
return p.PageNums() > 1
|
||||
}
|
||||
|
||||
// Instantiates a paginator struct for the current http request.
|
||||
func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator {
|
||||
p := Paginator{}
|
||||
p.Request = req
|
||||
if per <= 0 {
|
||||
per = 10
|
||||
}
|
||||
p.PerPageNums = per
|
||||
p.SetNums(nums)
|
||||
return &p
|
||||
}
|
34
utils/pagination/utils.go
Normal file
34
utils/pagination/utils.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2014 beego Author. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pagination
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// convert any numeric value to int64
|
||||
func ToInt64(value interface{}) (d int64, err error) {
|
||||
val := reflect.ValueOf(value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64:
|
||||
d = val.Int()
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
d = int64(val.Uint())
|
||||
default:
|
||||
err = fmt.Errorf("ToInt64 need numeric not `%T`", value)
|
||||
}
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user