1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-23 18:40:54 +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

@ -6,6 +6,21 @@ It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific feature
###### 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
###### Please see [Documentation](http://beego.me/docs) for more. ###### Please see [Documentation](http://beego.me/docs) for more.
@ -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

@ -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)\)`},

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