1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-23 17:10:55 +00:00

Merge branch 'develop' of https://github.com/astaxie/beego into develop-bk

This commit is contained in:
Ming Deng 2020-12-12 21:29:53 +08:00
commit 53688ce32f
27 changed files with 528 additions and 440 deletions

View File

@ -4,7 +4,22 @@
beego is used for rapid development of RESTful APIs, web apps and backend services in Go. beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
###### More info at [beego.me](http://beego.me). ###### More info at [beego.me](http://beego.me).
> If you could not open this website, go to [beedoc](https://github.com/beego/beedoc)
## beego 1.x and 2.x
We recently release beego 2.0.0-beta, and its structure change a lot, so you may get some error
1. If you are working on beego v1.x please try `go get github.com/astaxie/beego@v1.12.3`
2. If you want to try beego 2.0.0, run `go get github.com/astaxie/beego@develop`
We are still working on fix bug and documentation of v2.x. And v2.x's doc will be released with v2.0.0.
## Next version
v1.12.4 will be released on Jan 2021 And v2.0.0 will be released next month.
## Quick Start ## Quick Start
@ -25,7 +40,9 @@ It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific feature
#### Download and install #### Download and install
go get github.com/astaxie/beego go get -u github.com/astaxie/beego@develop
Now we are working on beego v2.0.0, so using `@develop`.
#### Create file `hello.go` #### Create file `hello.go`
```go ```go

View File

@ -212,7 +212,8 @@ func (c *Controller) ServeFormatted(encoding ...bool) {
// Input returns the input data map from POST or PUT request body and query string. // Input returns the input data map from POST or PUT request body and query string.
func (c *Controller) Input() url.Values { func (c *Controller) Input() url.Values {
return (*web.Controller)(c).Input() val, _ := (*web.Controller)(c).Input()
return val
} }
// ParseForm maps input data map to obj struct. // ParseForm maps input data map to obj struct.

View File

@ -141,7 +141,7 @@ func (manager *Manager) GC() {
// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request. // SessionRegenerateID 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) Store { func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) Store {
s := (*session.Manager)(manager).SessionRegenerateID(w, r) s, _ := (*session.Manager)(manager).SessionRegenerateID(w, r)
return &NewToOldStoreAdapter{ return &NewToOldStoreAdapter{
delegate: s, delegate: s,
} }

View File

@ -1,249 +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 adapter
import (
"testing"
"github.com/astaxie/beego/adapter/context"
beecontext "github.com/astaxie/beego/server/web/context"
)
type testinfo struct {
url string
requesturl string
params map[string]string
}
var routers []testinfo
func init() {
routers = make([]testinfo, 0)
routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil})
routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}})
routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}})
routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}})
routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}})
routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}})
routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
routers = append(routers, testinfo{"/", "/", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
routers = append(routers, testinfo{"/*", "/http://customer/123/", map[string]string{":splat": "http://customer/123/"}})
routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}})
routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}})
routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}})
routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}})
routers = append(routers, testinfo{"/thumbnail/:size/uploads/*",
"/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}})
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{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}})
routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}})
}
func TestTreeRouters(t *testing.T) {
for _, r := range routers {
tr := NewTree()
tr.AddRouter(r.url, "astaxie")
ctx := context.NewContext()
obj := tr.Match(r.requesturl, ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal(r.url+" can't get obj, Expect ", r.requesturl)
}
if r.params != nil {
for k, v := range r.params {
if vv := ctx.Input.Param(k); vv != v {
t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
} else if vv == "" && v != "" {
t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k)
}
}
}
}
}
func TestStaticPath(t *testing.T) {
tr := NewTree()
tr.AddRouter("/topic/:id", "wildcard")
tr.AddRouter("/topic", "static")
ctx := context.NewContext()
obj := tr.Match("/topic", ctx)
if obj == nil || obj.(string) != "static" {
t.Fatal("/topic is a static route")
}
obj = tr.Match("/topic/1", ctx)
if obj == nil || obj.(string) != "wildcard" {
t.Fatal("/topic/1 is a wildcard route")
}
}
func TestAddTree(t *testing.T) {
tr := NewTree()
tr.AddRouter("/shop/:id/account", "astaxie")
tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
t1 := NewTree()
t1.AddTree("/v1/zl", tr)
ctx := context.NewContext()
obj := t1.Match("/v1/zl/shop/123/account", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl/shop/:id/account can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":id") != "123" {
t.Fatal("get :id param error")
}
ctx.Input.Reset((*beecontext.Context)(ctx))
obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" {
t.Fatal("get :sd :id :page param error")
}
t2 := NewTree()
t2.AddTree("/v1/:shopid", tr)
ctx.Input.Reset((*beecontext.Context)(ctx))
obj = t2.Match("/v1/zl/shop/123/account", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" {
t.Fatal("get :id :shopid param error")
}
ctx.Input.Reset((*beecontext.Context)(ctx))
obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get :shopid param error")
}
if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" {
t.Fatal("get :sd :id :page :shopid param error")
}
}
func TestAddTree2(t *testing.T) {
tr := NewTree()
tr.AddRouter("/shop/:id/account", "astaxie")
tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
t3 := NewTree()
t3.AddTree("/:version(v1|v2)/:prefix", tr)
ctx := context.NewContext()
obj := t3.Match("/v1/zl/shop/123/account", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" {
t.Fatal("get :id :prefix :version param error")
}
}
func TestAddTree3(t *testing.T) {
tr := NewTree()
tr.AddRouter("/create", "astaxie")
tr.AddRouter("/shop/:sd/account", "astaxie")
t3 := NewTree()
t3.AddTree("/table/:num", tr)
ctx := context.NewContext()
obj := t3.Match("/table/123/shop/123/account", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/table/:num/shop/:sd/account can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" {
t.Fatal("get :num :sd param error")
}
ctx.Input.Reset((*beecontext.Context)(ctx))
obj = t3.Match("/table/123/create", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/table/:num/create can't get obj ")
}
}
func TestAddTree4(t *testing.T) {
tr := NewTree()
tr.AddRouter("/create", "astaxie")
tr.AddRouter("/shop/:sd/:account", "astaxie")
t4 := NewTree()
t4.AddTree("/:info:int/:num/:id", tr)
ctx := context.NewContext()
obj := t4.Match("/12/123/456/shop/123/account", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
}
if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error")
}
if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" ||
ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" ||
ctx.Input.Param(":account") != "account" {
t.Fatal("get :info :num :id :sd :account param error")
}
ctx.Input.Reset((*beecontext.Context)(ctx))
obj = t4.Match("/12/123/456/create", ctx)
if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:info:int/:num/:id/create can't get obj ")
}
}
// Test for issue #1595
func TestAddTree5(t *testing.T) {
tr := NewTree()
tr.AddRouter("/v1/shop/:id", "shopdetail")
tr.AddRouter("/v1/shop/", "shophome")
ctx := context.NewContext()
obj := tr.Match("/v1/shop/", ctx)
if obj == nil || obj.(string) != "shophome" {
t.Fatal("url /v1/shop/ need match router /v1/shop/ ")
}
}

View File

@ -72,21 +72,9 @@ func TestCache(t *testing.T) {
t.Error("set Error", err) t.Error("set Error", err)
} }
if err = bm.Incr(context.Background(), "astaxie"); err != nil { // test different integer type for incr & decr
t.Error("Incr Error", err) testMultiIncrDecr(t, bm, timeoutDuration)
}
if v, _ := bm.Get(context.Background(), "astaxie"); v.(int) != 2 {
t.Error("get err")
}
if err = bm.Decr(context.Background(), "astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v, _ := bm.Get(context.Background(), "astaxie"); v.(int) != 1 {
t.Error("get err")
}
bm.Delete(context.Background(), "astaxie") bm.Delete(context.Background(), "astaxie")
if res, _ := bm.IsExist(context.Background(), "astaxie"); res { if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
t.Error("delete err") t.Error("delete err")
@ -120,6 +108,20 @@ func TestCache(t *testing.T) {
if vv[1].(string) != "author1" { if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR") t.Error("GetMulti ERROR")
} }
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0] != nil {
t.Error("GetMulti ERROR")
}
if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR")
}
if err != nil && err.Error() != "key [astaxie0] error: the key isn't exist" {
t.Error("GetMulti ERROR")
}
} }
func TestFileCache(t *testing.T) { func TestFileCache(t *testing.T) {
@ -139,21 +141,9 @@ func TestFileCache(t *testing.T) {
t.Error("get err") t.Error("get err")
} }
if err = bm.Incr(context.Background(), "astaxie"); err != nil { // test different integer type for incr & decr
t.Error("Incr Error", err) testMultiIncrDecr(t, bm, timeoutDuration)
}
if v, _ := bm.Get(context.Background(), "astaxie"); v.(int) != 2 {
t.Error("get err")
}
if err = bm.Decr(context.Background(), "astaxie"); err != nil {
t.Error("Decr Error", err)
}
if v, _ := bm.Get(context.Background(), "astaxie"); v.(int) != 1 {
t.Error("get err")
}
bm.Delete(context.Background(), "astaxie") bm.Delete(context.Background(), "astaxie")
if res, _ := bm.IsExist(context.Background(), "astaxie"); res { if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
t.Error("delete err") t.Error("delete err")
@ -189,5 +179,57 @@ func TestFileCache(t *testing.T) {
t.Error("GetMulti ERROR") t.Error("GetMulti ERROR")
} }
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0] != nil {
t.Error("GetMulti ERROR")
}
if vv[1].(string) != "author1" {
t.Error("GetMulti ERROR")
}
if err == nil {
t.Error("GetMulti ERROR")
}
os.RemoveAll("cache") os.RemoveAll("cache")
} }
func testMultiIncrDecr(t *testing.T, c Cache, timeout time.Duration) {
testIncrDecr(t, c, 1, 2, timeout)
testIncrDecr(t, c, int32(1), int32(2), timeout)
testIncrDecr(t, c, int64(1), int64(2), timeout)
testIncrDecr(t, c, uint(1), uint(2), timeout)
testIncrDecr(t, c, uint32(1), uint32(2), timeout)
testIncrDecr(t, c, uint64(1), uint64(2), timeout)
}
func testIncrDecr(t *testing.T, c Cache, beforeIncr interface{}, afterIncr interface{}, timeout time.Duration) {
var err error
ctx := context.Background()
key := "incDecKey"
if err = c.Put(ctx, key, beforeIncr, timeout); err != nil {
t.Error("Get Error", err)
}
if err = c.Incr(ctx, key); err != nil {
t.Error("Incr Error", err)
}
if v, _ := c.Get(ctx, key); v != afterIncr {
t.Error("Get Error")
}
if err = c.Decr(ctx, key); err != nil {
t.Error("Decr Error", err)
}
if v, _ := c.Get(ctx, key); v != beforeIncr {
t.Error("Get Error")
}
if err := c.Delete(ctx, key); err != nil {
t.Error("Delete Error")
}
}

99
client/cache/file.go vendored
View File

@ -26,8 +26,8 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -144,17 +144,22 @@ func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
// GetMulti gets values from file cache. // GetMulti gets values from file cache.
// if nonexistent or expired return an empty string. // if nonexistent or expired return an empty string.
func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
var rc []interface{} rc := make([]interface{}, len(keys))
for _, key := range keys { keysErr := make([]string, 0)
val, err := fc.Get(context.Background(), key)
if err != nil {
rc = append(rc, err)
} else {
rc = append(rc, val)
}
for i, ki := range keys {
val, err := fc.Get(context.Background(), ki)
if err != nil {
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, err.Error()))
continue
}
rc[i] = val
} }
return rc, nil
if len(keysErr) == 0 {
return rc, nil
}
return rc, errors.New(strings.Join(keysErr, "; "))
} }
// Put value into file cache. // Put value into file cache.
@ -189,28 +194,70 @@ func (fc *FileCache) Delete(ctx context.Context, key string) error {
// Incr increases cached int value. // Incr increases cached int value.
// fc value is saved forever unless deleted. // fc value is saved forever unless deleted.
func (fc *FileCache) Incr(ctx context.Context, key string) error { func (fc *FileCache) Incr(ctx context.Context, key string) error {
data, _ := fc.Get(context.Background(), key) data, err := fc.Get(context.Background(), key)
var incr int if err != nil {
if reflect.TypeOf(data).Name() != "int" { return err
incr = 0
} else {
incr = data.(int) + 1
} }
fc.Put(context.Background(), key, incr, time.Duration(fc.EmbedExpiry))
return nil var res interface{}
switch val := data.(type) {
case int:
res = val + 1
case int32:
res = val + 1
case int64:
res = val + 1
case uint:
res = val + 1
case uint32:
res = val + 1
case uint64:
res = val + 1
default:
return errors.Errorf("data is not (u)int (u)int32 (u)int64")
}
return fc.Put(context.Background(), key, res, time.Duration(fc.EmbedExpiry))
} }
// Decr decreases cached int value. // Decr decreases cached int value.
func (fc *FileCache) Decr(ctx context.Context, key string) error { func (fc *FileCache) Decr(ctx context.Context, key string) error {
data, _ := fc.Get(context.Background(), key) data, err := fc.Get(context.Background(), key)
var decr int if err != nil {
if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { return err
decr = 0
} else {
decr = data.(int) - 1
} }
fc.Put(context.Background(), key, decr, time.Duration(fc.EmbedExpiry))
return nil var res interface{}
switch val := data.(type) {
case int:
res = val - 1
case int32:
res = val - 1
case int64:
res = val - 1
case uint:
if val > 0 {
res = val - 1
} else {
return errors.New("data val is less than 0")
}
case uint32:
if val > 0 {
res = val - 1
} else {
return errors.New("data val is less than 0")
}
case uint64:
if val > 0 {
res = val - 1
} else {
return errors.New("data val is less than 0")
}
default:
return errors.Errorf("data is not (u)int (u)int32 (u)int64")
}
return fc.Put(context.Background(), key, res, time.Duration(fc.EmbedExpiry))
} }
// IsExist checks if value exists. // IsExist checks if value exists.

View File

@ -33,6 +33,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"strings" "strings"
"time" "time"
@ -68,19 +69,31 @@ func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
// GetMulti gets a value from a key in memcache. // GetMulti gets a value from a key in memcache.
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
var rv []interface{} rv := make([]interface{}, len(keys))
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return rv, err return rv, err
} }
} }
mv, err := rc.conn.GetMulti(keys) mv, err := rc.conn.GetMulti(keys)
if err == nil { if err != nil {
for _, v := range mv { return rv, err
rv = append(rv, v.Value)
}
} }
return rv, err
keysErr := make([]string, 0)
for i, ki := range keys {
if _, ok := mv[ki]; !ok {
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "the key isn't exist"))
continue
}
rv[i] = mv[ki].Value
}
if len(keysErr) == 0 {
return rv, nil
}
return rv, fmt.Errorf(strings.Join(keysErr, "; "))
} }
// Put puts a value into memcache. // Put puts a value into memcache.

View File

@ -28,7 +28,6 @@ import (
) )
func TestMemcacheCache(t *testing.T) { func TestMemcacheCache(t *testing.T) {
addr := os.Getenv("MEMCACHE_ADDR") addr := os.Getenv("MEMCACHE_ADDR")
if addr == "" { if addr == "" {
addr = "127.0.0.1:11211" addr = "127.0.0.1:11211"
@ -114,6 +113,20 @@ func TestMemcacheCache(t *testing.T) {
t.Error("GetMulti ERROR") t.Error("GetMulti ERROR")
} }
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
if len(vv) != 2 {
t.Error("GetMulti ERROR")
}
if vv[0] != nil {
t.Error("GetMulti ERROR")
}
if string(vv[1].([]byte)) != "author1" {
t.Error("GetMulti ERROR")
}
if err != nil && err.Error() == "key [astaxie0] error: key isn't exist" {
t.Error("GetMulti ERROR")
}
// test clear all // test clear all
if err = bm.ClearAll(context.Background()); err != nil { if err = bm.ClearAll(context.Background()); err != nil {
t.Error("clear all err") t.Error("clear all err")

View File

@ -18,6 +18,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"strings"
"sync" "sync"
"time" "time"
) )
@ -68,22 +70,28 @@ func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error)
} }
return itm.val, nil return itm.val, nil
} }
return nil, nil return nil, errors.New("the key isn't exist")
} }
// GetMulti gets caches from memory. // GetMulti gets caches from memory.
// If non-existent or expired, return nil. // If non-existent or expired, return nil.
func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
var rc []interface{} rc := make([]interface{}, len(keys))
for _, name := range keys { keysErr := make([]string, 0)
val, err := bc.Get(context.Background(), name)
for i, ki := range keys {
val, err := bc.Get(context.Background(), ki)
if err != nil { if err != nil {
rc = append(rc, err) keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, err.Error()))
} else { continue
rc = append(rc, val)
} }
rc[i] = val
} }
return rc, nil
if len(keysErr) == 0 {
return rc, nil
}
return rc, errors.New(strings.Join(keysErr, "; "))
} }
// Put puts cache into memory. // Put puts cache into memory.

View File

@ -113,6 +113,14 @@ func TestRedisCache(t *testing.T) {
t.Error("GetMulti ERROR") t.Error("GetMulti ERROR")
} }
vv, _ = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
if vv[0] != nil {
t.Error("GetMulti ERROR")
}
if v, _ := redis.String(vv[1], nil); v != "author1" {
t.Error("GetMulti ERROR")
}
// test clear all // test clear all
if err = bm.ClearAll(context.Background()); err != nil { if err = bm.ClearAll(context.Background()); err != nil {
t.Error("clear all err") t.Error("clear all err")

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -28,36 +29,50 @@ func NewSsdbCache() cache.Cache {
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) { func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return nil, nil return nil, err
} }
} }
value, err := rc.conn.Get(key) value, err := rc.conn.Get(key)
if err == nil { if err == nil {
return value, nil return value, nil
} }
return nil, nil return nil, err
} }
// GetMulti gets one or keys values from ssdb. // GetMulti gets one or keys values from ssdb.
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
size := len(keys) size := len(keys)
var values []interface{} values := make([]interface{}, size)
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return values, err return values, err
} }
} }
res, err := rc.conn.Do("multi_get", keys) res, err := rc.conn.Do("multi_get", keys)
if err != nil {
return values, err
}
resSize := len(res) resSize := len(res)
if err == nil { keyIdx := make(map[string]int)
for i := 1; i < resSize; i += 2 { for i := 1; i < resSize; i += 2 {
values = append(values, res[i+1]) keyIdx[res[i]] = i
}
keysErr := make([]string, 0)
for i, ki := range keys {
if _, ok := keyIdx[ki]; !ok {
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "the key isn't exist"))
continue
} }
return values, nil values[i] = res[keyIdx[ki]+1]
} }
for i := 0; i < size; i++ {
values = append(values, err) if len(keysErr) != 0 {
return values, fmt.Errorf(strings.Join(keysErr, "; "))
} }
return values, nil return values, nil
} }

View File

@ -106,6 +106,20 @@ func TestSsdbcacheCache(t *testing.T) {
t.Error("getmulti error") t.Error("getmulti error")
} }
vv, err = ssdb.GetMulti(context.Background(), []string{"ssdb", "ssdb11"})
if len(vv) != 2 {
t.Error("getmulti error")
}
if vv[0].(string) != "ssdb" {
t.Error("getmulti error")
}
if vv[1] != nil {
t.Error("getmulti error")
}
if err != nil && err.Error() != "key [ssdb11] error: the key isn't exist" {
t.Error("getmulti error")
}
// test clear all done // test clear all done
if err = ssdb.ClearAll(context.Background()); err != nil { if err = ssdb.ClearAll(context.Background()); err != nil {
t.Error("clear all err") t.Error("clear all err")

View File

@ -76,10 +76,13 @@ func (c Condition) AndNot(expr string, args ...interface{}) *Condition {
// AndCond combine a condition to current condition // AndCond combine a condition to current condition
func (c *Condition) AndCond(cond *Condition) *Condition { func (c *Condition) AndCond(cond *Condition) *Condition {
c = c.clone()
if c == cond { if c == cond {
panic(fmt.Errorf("<Condition.AndCond> cannot use self as sub cond")) panic(fmt.Errorf("<Condition.AndCond> cannot use self as sub cond"))
} }
c = c.clone()
if cond != nil { if cond != nil {
c.params = append(c.params, condValue{cond: cond, isCond: true}) c.params = append(c.params, condValue{cond: cond, isCond: true})
} }
@ -149,5 +152,8 @@ func (c *Condition) IsEmpty() bool {
// clone clone a condition // clone clone a condition
func (c Condition) clone() *Condition { func (c Condition) clone() *Condition {
params := make([]condValue, len(c.params))
copy(params, c.params)
c.params = params
return &c return &c
} }

View File

@ -2668,3 +2668,48 @@ func TestPSQueryBuilder(t *testing.T) {
throwFailNow(t, AssertIs(l[0].UserName, "astaxie")) throwFailNow(t, AssertIs(l[0].UserName, "astaxie"))
throwFailNow(t, AssertIs(l[0].Age, 30)) throwFailNow(t, AssertIs(l[0].Age, 30))
} }
func TestCondition(t *testing.T) {
// test Condition whether to include yourself
cond := NewCondition()
cond = cond.AndCond(cond.Or("ID", 1))
cond = cond.AndCond(cond.Or("ID", 2))
cond = cond.AndCond(cond.Or("ID", 3))
cond = cond.AndCond(cond.Or("ID", 4))
cycleFlag := false
var hasCycle func(*Condition)
hasCycle = func(c *Condition) {
if nil == c || cycleFlag {
return
}
condPointMap := make(map[string]bool)
condPointMap[fmt.Sprintf("%p", c)] = true
for _, p := range c.params {
if p.isCond {
adr := fmt.Sprintf("%p", p.cond)
if condPointMap[adr] {
// self as sub cond was cycle
cycleFlag = true
break
}
condPointMap[adr] = true
}
}
if cycleFlag {
return
}
for _, p := range c.params {
if p.isCond {
// check next cond
hasCycle(p.cond)
}
}
return
}
hasCycle(cond)
// cycleFlag was true,meaning use self as sub cond
throwFail(t, AssertIs(!cycleFlag, true))
return
}

View File

@ -49,12 +49,12 @@ func (f *StrTo) Set(v string) {
// Clear string // Clear string
func (f *StrTo) Clear() { func (f *StrTo) Clear() {
*f = StrTo(0x1E) *f = StrTo(rune(0x1E))
} }
// Exist check string exist // Exist check string exist
func (f StrTo) Exist() bool { func (f StrTo) Exist() bool {
return string(f) != string(0x1E) return string(f) != string(rune(0x1E))
} }
// Bool string to bool // Bool string to bool

View File

@ -67,4 +67,4 @@ func TestSnakeStringWithAcronym(t *testing.T) {
t.Error("Unit Test Fail:", v, res, answer[v]) t.Error("Unit Test Fail:", v, res, answer[v])
} }
} }
} }

View File

@ -18,7 +18,6 @@ package config
// for most users, they only need to use those methods // for most users, they only need to use those methods
var globalInstance Configer var globalInstance Configer
// InitGlobalInstance will ini the global instance // InitGlobalInstance will ini the global instance
// If you want to use specific implementation, don't forget to import it. // If you want to use specific implementation, don't forget to import it.
// e.g. _ import "github.com/astaxie/beego/core/config/etcd" // e.g. _ import "github.com/astaxie/beego/core/config/etcd"

View File

@ -519,7 +519,7 @@ func (c *IniConfigContainer) Unmarshaler(prefix string, obj interface{}, opt ...
func init() { func init() {
Register("ini", &IniConfig{}) Register("ini", &IniConfig{})
err := InitGlobalInstance("ini", "config/app.conf") err := InitGlobalInstance("ini", "conf/app.conf")
if err != nil { if err != nil {
logs.Warn("init global config instance failed. If you donot use this, just ignore it. ", err) logs.Warn("init global config instance failed. If you donot use this, just ignore it. ", err)
} }

View File

@ -764,9 +764,7 @@ func formatLog(f interface{}, v ...interface{}) string {
if len(v) == 0 { if len(v) == 0 {
return msg return msg
} }
if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") { if !strings.Contains(msg, "%") {
// format string
} else {
// do not contain format char // do not contain format char
msg += strings.Repeat(" %v", len(v)) msg += strings.Repeat(" %v", len(v))
} }

View File

@ -1,10 +1,10 @@
package logs package logs
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -25,8 +25,8 @@ func newSLACKWriter() Logger {
} }
func (s *SLACKWriter) Format(lm *LogMsg) string { func (s *SLACKWriter) Format(lm *LogMsg) string {
text := fmt.Sprintf("{\"text\": \"%s %s\"}", lm.When.Format("2006-01-02 15:04:05"), lm.OldStyleFormat()) // text := fmt.Sprintf("{\"text\": \"%s\"}", msg)
return text return lm.When.Format("2006-01-02 15:04:05") + " " + lm.OldStyleFormat()
} }
func (s *SLACKWriter) SetFormatter(f LogFormatter) { func (s *SLACKWriter) SetFormatter(f LogFormatter) {
@ -55,10 +55,12 @@ func (s *SLACKWriter) WriteMsg(lm *LogMsg) error {
return nil return nil
} }
msg := s.Format(lm) msg := s.Format(lm)
form := url.Values{} m := make(map[string]string, 1)
form.Add("payload", msg) m["text"] = msg
resp, err := http.PostForm(s.WebhookURL, form) body, _ := json.Marshal(m)
// resp, err := http.PostForm(s.WebhookURL, form)
resp, err := http.Post(s.WebhookURL, "application/json", bytes.NewReader(body))
if err != nil { if err != nil {
return err return err
} }

44
core/logs/slack_test.go Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2020
//
// 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 logs
// func TestSLACKWriter_WriteMsg(t *testing.T) {
// sc := `
// {
// "webhookurl":"",
// "level":7
// }
// `
// l := newSLACKWriter()
// err := l.Init(sc)
// if err != nil {
// Debug(err)
// }
//
// err = l.WriteMsg(&LogMsg{
// Level: 7,
// Msg: `{ "abs"`,
// When: time.Now(),
// FilePath: "main.go",
// LineNumber: 100,
// enableFullFilePath: true,
// enableFuncCallDepth: true,
// })
//
// if err != nil {
// Debug(err)
// }
//
// }

View File

@ -261,15 +261,15 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error {
} }
// ServeFormatted serves YAML, XML or JSON, depending on the value of the Accept header // ServeFormatted serves YAML, XML or JSON, depending on the value of the Accept header
func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) { func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) error {
accept := output.Context.Input.Header("Accept") accept := output.Context.Input.Header("Accept")
switch accept { switch accept {
case ApplicationYAML: case ApplicationYAML:
output.YAML(data) return output.YAML(data)
case ApplicationXML, TextXML: case ApplicationXML, TextXML:
output.XML(data, hasIndent) return output.XML(data, hasIndent)
default: default:
output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0]) return output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0])
} }
} }

View File

@ -16,6 +16,7 @@ package web
import ( import (
"bytes" "bytes"
context2 "context"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
@ -250,13 +251,16 @@ func (c *Controller) Render() error {
// RenderString returns the rendered template string. Do not send out response. // RenderString returns the rendered template string. Do not send out response.
func (c *Controller) RenderString() (string, error) { func (c *Controller) RenderString() (string, error) {
b, e := c.RenderBytes() b, e := c.RenderBytes()
if e != nil {
return "", e
}
return string(b), e return string(b), e
} }
// RenderBytes returns the bytes of rendered template string. Do not send out response. // RenderBytes returns the bytes of rendered template string. Do not send out response.
func (c *Controller) RenderBytes() ([]byte, error) { func (c *Controller) RenderBytes() ([]byte, error) {
buf, err := c.renderTemplate() buf, err := c.renderTemplate()
//if the controller has set layout, then first get the tplName's content set the content to the layout // if the controller has set layout, then first get the tplName's content set the content to the layout
if err == nil && c.Layout != "" { if err == nil && c.Layout != "" {
c.Data["LayoutContent"] = template.HTML(buf.String()) c.Data["LayoutContent"] = template.HTML(buf.String())
@ -276,7 +280,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
} }
buf.Reset() buf.Reset()
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) err = ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
} }
return buf.Bytes(), err return buf.Bytes(), err
} }
@ -373,50 +377,57 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string {
} }
// ServeJSON sends a json response with encoding charset. // ServeJSON sends a json response with encoding charset.
func (c *Controller) ServeJSON(encoding ...bool) { func (c *Controller) ServeJSON(encoding ...bool) error {
var ( var (
hasIndent = BConfig.RunMode != PROD hasIndent = BConfig.RunMode != PROD
hasEncoding = len(encoding) > 0 && encoding[0] hasEncoding = len(encoding) > 0 && encoding[0]
) )
c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) return c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
} }
// ServeJSONP sends a jsonp response. // ServeJSONP sends a jsonp response.
func (c *Controller) ServeJSONP() { func (c *Controller) ServeJSONP() error {
hasIndent := BConfig.RunMode != PROD hasIndent := BConfig.RunMode != PROD
c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent) return c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent)
} }
// ServeXML sends xml response. // ServeXML sends xml response.
func (c *Controller) ServeXML() { func (c *Controller) ServeXML() error {
hasIndent := BConfig.RunMode != PROD hasIndent := BConfig.RunMode != PROD
c.Ctx.Output.XML(c.Data["xml"], hasIndent) return c.Ctx.Output.XML(c.Data["xml"], hasIndent)
} }
// ServeYAML sends yaml response. // ServeYAML sends yaml response.
func (c *Controller) ServeYAML() { func (c *Controller) ServeYAML() error {
c.Ctx.Output.YAML(c.Data["yaml"]) return c.Ctx.Output.YAML(c.Data["yaml"])
} }
// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header // ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header
func (c *Controller) ServeFormatted(encoding ...bool) { func (c *Controller) ServeFormatted(encoding ...bool) error {
hasIndent := BConfig.RunMode != PROD hasIndent := BConfig.RunMode != PROD
hasEncoding := len(encoding) > 0 && encoding[0] hasEncoding := len(encoding) > 0 && encoding[0]
c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding) return c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding)
} }
// Input returns the input data map from POST or PUT request body and query string. // Input returns the input data map from POST or PUT request body and query string.
func (c *Controller) Input() url.Values { func (c *Controller) Input() (url.Values, error) {
if c.Ctx.Request.Form == nil { if c.Ctx.Request.Form == nil {
c.Ctx.Request.ParseForm() err := c.Ctx.Request.ParseForm()
if err != nil {
return nil, err
}
} }
return c.Ctx.Request.Form return c.Ctx.Request.Form, nil
} }
// ParseForm maps input data map to obj struct. // ParseForm maps input data map to obj struct.
func (c *Controller) ParseForm(obj interface{}) error { func (c *Controller) ParseForm(obj interface{}) error {
return ParseForm(c.Input(), obj) form, err := c.Input()
if err != nil {
return err
}
return ParseForm(form, obj)
} }
// GetString returns the input value by key string or the default value while it's present and input is blank // GetString returns the input value by key string or the default value while it's present and input is blank
@ -438,7 +449,7 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string {
defv = def[0] defv = def[0]
} }
if f := c.Input(); f == nil { if f, err := c.Input(); f == nil || err != nil {
return defv return defv
} else if vs := f[key]; len(vs) > 0 { } else if vs := f[key]; len(vs) > 0 {
return vs return vs
@ -618,11 +629,11 @@ func (c *Controller) StartSession() session.Store {
} }
// SetSession puts value into session. // SetSession puts value into session.
func (c *Controller) SetSession(name interface{}, value interface{}) { func (c *Controller) SetSession(name interface{}, value interface{}) error {
if c.CruSession == nil { if c.CruSession == nil {
c.StartSession() c.StartSession()
} }
c.CruSession.Set(nil, name, value) return c.CruSession.Set(context2.Background(), name, value)
} }
// GetSession gets value from session. // GetSession gets value from session.
@ -630,32 +641,38 @@ func (c *Controller) GetSession(name interface{}) interface{} {
if c.CruSession == nil { if c.CruSession == nil {
c.StartSession() c.StartSession()
} }
return c.CruSession.Get(nil, name) return c.CruSession.Get(context2.Background(), name)
} }
// DelSession removes value from session. // DelSession removes value from session.
func (c *Controller) DelSession(name interface{}) { func (c *Controller) DelSession(name interface{}) error {
if c.CruSession == nil { if c.CruSession == nil {
c.StartSession() c.StartSession()
} }
c.CruSession.Delete(nil, name) return c.CruSession.Delete(context2.Background(), name)
} }
// SessionRegenerateID regenerates session id for this session. // SessionRegenerateID regenerates session id for this session.
// the session data have no changes. // the session data have no changes.
func (c *Controller) SessionRegenerateID() { func (c *Controller) SessionRegenerateID() error {
if c.CruSession != nil { if c.CruSession != nil {
c.CruSession.SessionRelease(nil, c.Ctx.ResponseWriter) c.CruSession.SessionRelease(context2.Background(), c.Ctx.ResponseWriter)
} }
c.CruSession = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request) var err error
c.CruSession, err = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request)
c.Ctx.Input.CruSession = c.CruSession c.Ctx.Input.CruSession = c.CruSession
return err
} }
// DestroySession cleans session data and session cookie. // DestroySession cleans session data and session cookie.
func (c *Controller) DestroySession() { func (c *Controller) DestroySession() error {
c.Ctx.Input.CruSession.Flush(nil) err := c.Ctx.Input.CruSession.Flush(nil)
if err != nil {
return err
}
c.Ctx.Input.CruSession = nil c.Ctx.Input.CruSession = nil
GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request) GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
return nil
} }
// IsAjax returns this request is ajax or not. // IsAjax returns this request is ajax or not.

View File

@ -273,7 +273,6 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) {
for _, f := range a.Filters { for _, f := range a.Filters {
p.InsertFilter(f.Pattern, f.Pos, f.Filter, WithReturnOnOutput(f.ReturnOnOutput), WithResetParams(f.ResetParams)) p.InsertFilter(f.Pattern, f.Pos, f.Filter, WithReturnOnOutput(f.ReturnOnOutput), WithResetParams(f.ResetParams))
} }
p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
} }
} }

View File

@ -298,15 +298,21 @@ func (manager *Manager) GC() {
} }
// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request. // SessionRegenerateID 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 Store) { func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) (Store, error) {
sid, err := manager.sessionID() sid, err := manager.sessionID()
if err != nil { if err != nil {
return return nil, err
} }
var session Store
cookie, err := r.Cookie(manager.config.CookieName) cookie, err := r.Cookie(manager.config.CookieName)
if err != nil || cookie.Value == "" { if err != nil || cookie.Value == "" {
//delete old cookie //delete old cookie
session, _ = manager.provider.SessionRead(nil, sid) session, err = manager.provider.SessionRead(nil, sid)
if err != nil {
return nil, err
}
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
@ -315,8 +321,16 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
Domain: manager.config.Domain, Domain: manager.config.Domain,
} }
} else { } else {
oldsid, _ := url.QueryUnescape(cookie.Value) oldsid, err := url.QueryUnescape(cookie.Value)
session, _ = manager.provider.SessionRegenerate(nil, oldsid, sid) if err != nil {
return nil, err
}
session, err = manager.provider.SessionRegenerate(nil, oldsid, sid)
if err != nil {
return nil, err
}
cookie.Value = url.QueryEscape(sid) cookie.Value = url.QueryEscape(sid)
cookie.HttpOnly = true cookie.HttpOnly = true
cookie.Path = "/" cookie.Path = "/"
@ -335,7 +349,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
w.Header().Set(manager.config.SessionNameInHTTPHeader, sid) w.Header().Set(manager.config.SessionNameInHTTPHeader, sid)
} }
return return session, nil
} }
// GetActiveSession Get all active sessions count number. // GetActiveSession Get all active sessions count number.

View File

@ -21,76 +21,109 @@ import (
"github.com/astaxie/beego/server/web/context" "github.com/astaxie/beego/server/web/context"
) )
type testinfo struct { type testInfo struct {
url string pattern string
requesturl string requestUrl string
params map[string]string params map[string]string
shouldMatchOrNot bool
} }
var routers []testinfo var routers []testInfo
func matchTestInfo(pattern, url string, params map[string]string) testInfo {
return testInfo{
pattern: pattern,
requestUrl: url,
params: params,
shouldMatchOrNot: true,
}
}
func notMatchTestInfo(pattern, url string) testInfo {
return testInfo{
pattern: pattern,
requestUrl: url,
params: nil,
shouldMatchOrNot: false,
}
}
func init() { func init() {
routers = make([]testinfo, 0) routers = make([]testInfo, 0)
routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil}) //match example
routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}}) routers = append(routers, matchTestInfo("/topic/?:auth:int", "/topic", nil))
routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}}) routers = append(routers, matchTestInfo("/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}))
routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}}) routers = append(routers, matchTestInfo("/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}))
routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}}) routers = append(routers, matchTestInfo("/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}))
routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}}) routers = append(routers, matchTestInfo("/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}))
routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}))
routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}}) routers = append(routers, matchTestInfo("/:id", "/123", map[string]string{":id": "123"}))
routers = append(routers, testinfo{"/", "/", nil}) routers = append(routers, matchTestInfo("/hello/?:id", "/hello", map[string]string{":id": ""}))
routers = append(routers, testinfo{"/customer/login", "/customer/login", nil}) routers = append(routers, matchTestInfo("/", "/", nil))
routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}}) routers = append(routers, matchTestInfo("/customer/login", "/customer/login", nil))
routers = append(routers, testinfo{"/*", "/http://customer/123/", map[string]string{":splat": "http://customer/123/"}}) routers = append(routers, matchTestInfo("/customer/login", "/customer/login.json", map[string]string{":ext": "json"}))
routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}) routers = append(routers, matchTestInfo("/*", "/http://customer/123/", map[string]string{":splat": "http://customer/123/"}))
routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}) routers = append(routers, matchTestInfo("/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}))
routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}) routers = append(routers, matchTestInfo("/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}))
routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}}) routers = append(routers, matchTestInfo("/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}))
routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}}) routers = append(routers, matchTestInfo("/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}))
routers = append(routers, testinfo{"/thumbnail/:size/uploads/*", routers = append(routers, matchTestInfo("/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}))
"/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", routers = append(routers, matchTestInfo("/thumbnail/:size/uploads/*", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}))
map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}}) routers = append(routers, matchTestInfo("/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}))
routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}}) routers = append(routers, matchTestInfo("/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}))
routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}}) routers = append(routers, matchTestInfo("/:name/test/*.*", "/nice/test/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, matchTestInfo("/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{"/dl/:width:int/:height:int/*.*", routers = append(routers, matchTestInfo("/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}))
"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg", routers = append(routers, matchTestInfo("/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}))
map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}}) routers = append(routers, matchTestInfo("/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}))
routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}))
routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/: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\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}))
routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/shop/:id/account", "/v1/shop/123/account", 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, matchTestInfo("/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}))
routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}}) routers = append(routers, matchTestInfo("/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}))
routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}))
routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}}) routers = append(routers, matchTestInfo("/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}))
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}))
routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}}) routers = append(routers, matchTestInfo("/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}))
routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}}) routers = append(routers, matchTestInfo("/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}))
routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}}) routers = append(routers, matchTestInfo("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}))
routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}}) routers = append(routers, matchTestInfo("/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}))
routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}}) routers = append(routers, matchTestInfo("/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}))
routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}}) //not match example
routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}})
// https://github.com/astaxie/beego/issues/3865
routers = append(routers, notMatchTestInfo("/read_:id:int\\.htm", "/read_222htm"))
routers = append(routers, notMatchTestInfo("/read_:id:int\\.htm", "/read_222_htm"))
routers = append(routers, notMatchTestInfo("/read_:id:int\\.htm", " /read_262shtm"))
} }
func TestTreeRouters(t *testing.T) { func TestTreeRouters(t *testing.T) {
for _, r := range routers { for _, r := range routers {
shouldMatch := r.shouldMatchOrNot
tr := NewTree() tr := NewTree()
tr.AddRouter(r.url, "astaxie") tr.AddRouter(r.pattern, "astaxie")
ctx := context.NewContext() ctx := context.NewContext()
obj := tr.Match(r.requesturl, ctx) obj := tr.Match(r.requestUrl, ctx)
if !shouldMatch {
if obj != nil {
t.Fatal("pattern:", r.pattern, ", should not match", r.requestUrl)
} else {
return
}
}
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal(r.url+" can't get obj, Expect ", r.requesturl) t.Fatal("pattern:", r.pattern+", can't match obj, Expect ", r.requestUrl)
} }
if r.params != nil { if r.params != nil {
for k, v := range r.params { for k, v := range r.params {
if vv := ctx.Input.Param(k); vv != v { if vv := ctx.Input.Param(k); vv != v {
t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv) t.Fatal("The Rule: " + r.pattern + "\nThe RequestURL:" + r.requestUrl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
} else if vv == "" && v != "" { } else if vv == "" && v != "" {
t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k) t.Fatal(r.pattern + " " + r.requestUrl + " get param empty:" + k)
} }
} }
} }
@ -247,7 +280,6 @@ func TestAddTree5(t *testing.T) {
t.Fatal("url /v1/shop/ need match router /v1/shop/ ") t.Fatal("url /v1/shop/ need match router /v1/shop/ ")
} }
} }
func TestSplitPath(t *testing.T) { func TestSplitPath(t *testing.T) {
a := splitPath("") a := splitPath("")
if len(a) != 0 { if len(a) != 0 {
@ -292,6 +324,7 @@ func TestSplitSegment(t *testing.T) {
":id([0-9]+)": {true, []string{":id"}, `([0-9]+)`}, ":id([0-9]+)": {true, []string{":id"}, `([0-9]+)`},
":id([0-9]+)_:name": {true, []string{":id", ":name"}, `([0-9]+)_(.+)`}, ":id([0-9]+)_:name": {true, []string{":id", ":name"}, `([0-9]+)_(.+)`},
":id(.+)_cms.html": {true, []string{":id"}, `(.+)_cms.html`}, ":id(.+)_cms.html": {true, []string{":id"}, `(.+)_cms.html`},
":id(.+)_cms\\.html": {true, []string{":id"}, `(.+)_cms\.html`},
"cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`}, "cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`},
`:app(a|b|c)`: {true, []string{":app"}, `(a|b|c)`}, `:app(a|b|c)`: {true, []string{":app"}, `(a|b|c)`},
`:app\((a|b|c)\)`: {true, []string{":app"}, `(.+)\((a|b|c)\)`}, `:app\((a|b|c)\)`: {true, []string{":app"}, `(.+)\((a|b|c)\)`},
@ -303,4 +336,4 @@ func TestSplitSegment(t *testing.T) {
t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r) t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r)
} }
} }
} }

View File

@ -452,9 +452,11 @@ func (m *taskManager) StartTask() {
func (m *taskManager) run() { func (m *taskManager) run() {
now := time.Now().Local() now := time.Now().Local()
m.taskLock.Lock()
for _, t := range m.adminTaskList { for _, t := range m.adminTaskList {
t.SetNext(nil, now) t.SetNext(nil, now)
} }
m.taskLock.Unlock()
for { for {
// we only use RLock here because NewMapSorter copy the reference, do not change any thing // we only use RLock here because NewMapSorter copy the reference, do not change any thing