1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 13:21:01 +00:00

52 Commits

Author SHA1 Message Date
093f976365 Merge pull request #4355 from flycash/develop
rename key world 'governor' to 'admin'
2020-12-13 19:36:23 +08:00
198b9cce5f rename key world 'governor' to 'admin' 2020-12-13 19:11:07 +08:00
1452a75de4 Merge pull request #4354 from flycash/develop
update flow request image
2020-12-13 18:27:50 +08:00
728296a7ae update flow request image 2020-12-13 18:26:53 +08:00
e2fc8b93f2 Merge pull request #4353 from flycash/develop
update architecture and flow .png
2020-12-13 17:22:19 +08:00
86620210f8 update architecture and flow .png 2020-12-13 17:01:08 +08:00
64d7d36794 Merge pull request #4352 from flycash/develop
update read me: add architecture ssection
2020-12-13 15:45:45 +08:00
88bd9f594a update read me: add architecture ssection 2020-12-13 15:43:21 +08:00
2e1c50253c Merge pull request #4351 from flycash/develop
update readme
2020-12-13 15:35:10 +08:00
a5842e9de2 update readme 2020-12-13 15:30:07 +08:00
40f7a56bf5 Merge pull request #4348 from flycash/develop-bk
update doc
2020-12-12 23:47:48 +08:00
aec5f4b3a5 remove flag of XSRF cookie 2020-12-12 23:26:03 +08:00
9eda707297 update doc 2020-12-12 22:30:44 +08:00
b92edb6587 Merge pull request #4347 from flycash/develop-bk
Resolve conflict between master and develop
2020-12-12 22:00:31 +08:00
53688ce32f Merge branch 'develop' of https://github.com/astaxie/beego into develop-bk 2020-12-12 21:29:53 +08:00
debd68cbe4 Revert "Merge pull request #4325 from flycash/revert1"
This reverts commit fad897346f, reversing
changes made to e284b0ddae.
2020-12-12 21:28:58 +08:00
b6d6571e99 Merge pull request #4332 from AllenX2018/fix-issue-4331
fix issue #4331
2020-12-04 09:22:39 +08:00
db785479ab fix issue #4331
1. Modify Incr&Decr for file cache
2. Add different type of integer testcases for Incr&Decr
2020-12-01 17:18:30 +08:00
54ab211f00 Merge pull request #4329 from jianzhiyao/fix-3865
fix dot parse in route & delete useless testing file
2020-12-01 11:52:53 +08:00
c034d3767a rollback 2020-11-30 20:22:52 +08:00
99a47e7644 fix reg_express 2020-11-30 19:27:59 +08:00
59ca0d063f fix dot parsing result in route & delete useless testing file 2020-11-29 23:39:58 +08:00
aa05b66ca1 Merge pull request #4328 from flycash/develop
expose more error code in web module
2020-11-29 23:05:20 +08:00
8d7f48ea75 expose more error code in web module 2020-11-29 22:33:03 +08:00
fd48c62873 Merge pull request #4316 from AllenX2018/fix-issue-4311
fix issue #4311
2020-11-29 22:01:25 +08:00
7ee5c8e553 Merge pull request #4327 from flycash/develop
Update readme
2020-11-27 00:02:56 +08:00
4afa9d2d25 Update readme 2020-11-26 23:49:37 +08:00
fad897346f Merge pull request #4325 from flycash/revert1
Revert "Merge pull request #4254 from astaxie/develop-2.0"
2020-11-26 20:18:35 +08:00
650fde66aa Revert "Merge pull request #4254 from astaxie/develop-2.0"
This reverts commit e284b0ddae, reversing
changes made to 8ef8fd2606.
2020-11-26 17:48:29 +08:00
359ad7a889 Merge pull request #4323 from flycash/fix4298
Update doc & update default conf path
2020-11-25 20:48:53 +08:00
00ed1c3733 change the globalInstance to read conf/app.conf 2020-11-25 20:16:41 +08:00
0958174bc8 update doc 2020-11-25 20:14:18 +08:00
c6282e7b27 remove ineffectual assignment to err 2020-11-18 09:56:56 +08:00
6225f0c1e9 fix issue 4311 2020-11-17 20:53:33 +08:00
05d8e293f7 Merge pull request #4313 from AllenX2018/fix-issue-4312
fix issue #4312
2020-11-14 16:04:29 +08:00
663e5d728c fix issue #4312 2020-11-13 16:47:22 +08:00
26bc9e7264 Merge pull request #4310 from gswcodeshow/develop
添加Condition单元测试 #4298
2020-11-13 09:31:18 +08:00
0a852912b4 调整CI警告 2020-11-13 07:25:21 +08:00
647e21b0c4 添加Condition单元测试 2020-11-13 00:13:35 +08:00
a00b54d8f6 Merge pull request #4304 from gswcodeshow/develop
Fix 4298
2020-11-12 21:41:10 +08:00
8f16098508 Merge pull request #4306 from AllenX2018/fix-issue-4305
fix issue #4305: add write lock for map adminTaskList iteration and modify
2020-11-11 20:52:29 +08:00
aad80ba4fa fix issue#4305: add write lock for map adminTaskList iteration and modify 2020-11-11 17:14:05 +08:00
8f3fd317da Fix 4298
Condition的clone方法,params存在共享内存,导致isCond死循环。
2020-11-11 16:12:57 +08:00
c5c03815f3 Merge pull request #4301 from flycash/fix4298
Fix 4298
2020-11-11 08:55:08 +08:00
0b3bcbd3ec Fix 4298 2020-11-10 23:30:24 +08:00
49d763dc6c Merge pull request #4296 from flycash/fix4138
Fix 4138
2020-11-10 21:58:28 +08:00
aa2c0ca108 Merge pull request #4299 from AllenX2018/fix-4282
fix issue 4282
2020-11-10 21:57:52 +08:00
b3474b20b9 fix issue 4282 2020-11-10 15:45:34 +08:00
2a6fadb9ae Fix 4138 2020-11-09 22:46:26 +08:00
e284b0ddae Merge pull request #4254 from astaxie/develop-2.0
Prepare Release v2.0.0-beta
2020-11-07 21:20:18 +08:00
8ef8fd2606 Merge pull request #4036 from astaxie/develop
V1.12.2
2020-06-30 23:25:29 +08:00
0cd80525e7 Merge branch 'develop' 2020-02-07 16:25:41 +08:00
40 changed files with 592 additions and 696 deletions

262
README.md
View File

@ -1,42 +1,57 @@
# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/astaxie/beego) # Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/astaxie/beego)
Beego is used for rapid development of enterprise application in Go, including RESTful APIs, web apps and backend
services.
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
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. embedding.
###### More info at [beego.me](http://beego.me). ![architecture](https://cdn.nlark.com/yuque/0/2020/png/755700/1607857489109-1e267fce-d65f-4c5e-b915-5c475df33c58.png)
Beego is compos of four parts:
1. Base modules: including log module, config module, governor module;
2. Task: is used for running timed tasks or periodic tasks;
3. Client: including ORM module, httplib module, cache module;
4. Server: including web module. We will support gRPC in the future;
## Quick Start ## Quick Start
###### Please see [Documentation](http://beego.me/docs) for more. [Officail website](http://beego.me)
[Example](https://github.com/beego-dev/beego-example)
> If you could not open official website, go to [beedoc](https://github.com/beego/beedoc)
###### [beego-example](https://github.com/beego-dev/beego-example)
### Web Application ### Web Application
![Http Request](https://cdn.nlark.com/yuque/0/2020/png/755700/1607857462507-855ec543-7ce3-402d-a0cb-b2524d5a4b60.png)
#### Create `hello` directory, cd `hello` directory #### Create `hello` directory, cd `hello` directory
mkdir hello mkdir hello
cd hello cd hello
#### Init module #### Init module
go mod init go mod init
#### Download and install #### Download and install
go get github.com/astaxie/beego go get github.com/astaxie/beego@v2.0.0
#### Create file `hello.go` #### Create file `hello.go`
```go ```go
package main package main
import "github.com/astaxie/beego/server/web" import "github.com/astaxie/beego/server/web"
func main(){ func main() {
web.Run() web.Run()
} }
``` ```
#### Build and run #### Build and run
go build hello.go go build hello.go
@ -46,227 +61,36 @@ func main(){
Congratulations! You've just built your first **beego** app. Congratulations! You've just built your first **beego** app.
### Using ORM module
```go
package main
import (
"github.com/astaxie/beego/client/orm"
"github.com/astaxie/beego/core/logs"
_ "github.com/go-sql-driver/mysql"
)
// User -
type User struct {
ID int `orm:"column(id)"`
Name string `orm:"column(name)"`
}
func init() {
// need to register models in init
orm.RegisterModel(new(User))
// need to register db driver
orm.RegisterDriver("mysql", orm.DRMySQL)
// need to register default database
orm.RegisterDataBase("default", "mysql", "beego:test@tcp(192.168.0.105:13306)/orm_test?charset=utf8")
}
func main() {
// automatically build table
orm.RunSyncdb("default", false, true)
// create orm object, and it will use `default` database
o := orm.NewOrm()
// data
user := new(User)
user.Name = "mike"
// insert data
id, err := o.Insert(user)
if err != nil {
logs.Info(err)
}
// ...
}
```
### Using httplib as http client
```go
package main
import (
"github.com/astaxie/beego/client/httplib"
"github.com/astaxie/beego/core/logs"
)
func main() {
// Get, more methods please read docs
req := httplib.Get("http://beego.me/")
str, err := req.String()
if err != nil {
logs.Error(err)
}
logs.Info(str)
}
```
### Using config module
```go
package main
import (
"context"
"github.com/astaxie/beego/core/config"
"github.com/astaxie/beego/core/logs"
)
var (
ConfigFile = "./app.conf"
)
func main() {
cfg, err := config.NewConfig("ini", ConfigFile)
if err != nil {
logs.Critical("An error occurred:", err)
panic(err)
}
res, _ := cfg.String(context.Background(), "name")
logs.Info("load config name is", res)
}
```
### Using logs module
```go
package main
import (
"github.com/astaxie/beego/core/logs"
)
func main() {
err := logs.SetLogger(logs.AdapterFile, `{"filename":"project.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
if err != nil {
panic(err)
}
logs.Info("hello beego")
}
```
### Using timed task
```go
package main
import (
"context"
"time"
"github.com/astaxie/beego/core/logs"
"github.com/astaxie/beego/task"
)
func main() {
// create a task
tk1 := task.NewTask("tk1", "0/3 * * * * *", func(ctx context.Context) error { logs.Info("tk1"); return nil })
// check task
err := tk1.Run(context.Background())
if err != nil {
logs.Error(err)
}
// add task to global todolist
task.AddTask("tk1", tk1)
// start tasks
task.StartTask()
// wait 12 second
time.Sleep(12 * time.Second)
defer task.StopTask()
}
```
### Using cache module
```go
package main
import (
"context"
"time"
"github.com/astaxie/beego/client/cache"
// don't forget this
_ "github.com/astaxie/beego/client/cache/redis"
"github.com/astaxie/beego/core/logs"
)
func main() {
// create cache
bm, err := cache.NewCache("redis", `{"key":"default", "conn":":6379", "password":"123456", "dbNum":"0"}`)
if err != nil {
logs.Error(err)
}
// put
isPut := bm.Put(context.Background(), "astaxie", 1, time.Second*10)
logs.Info(isPut)
isPut = bm.Put(context.Background(), "hello", "world", time.Second*10)
logs.Info(isPut)
// get
result, _ := bm.Get(context.Background(),"astaxie")
logs.Info(string(result.([]byte)))
multiResult, _ := bm.GetMulti(context.Background(), []string{"astaxie", "hello"})
for i := range multiResult {
logs.Info(string(multiResult[i].([]byte)))
}
// isExist
isExist, _ := bm.IsExist(context.Background(), "astaxie")
logs.Info(isExist)
// delete
isDelete := bm.Delete(context.Background(), "astaxie")
logs.Info(isDelete)
}
```
## Features ## Features
* RESTful support * RESTful support
* MVC architecture * [MVC architecture](https://github.com/beego/beedoc/tree/master/en-US/mvc)
* Modularity * Modularity
* Auto API documents * [Auto API documents](https://github.com/beego/beedoc/blob/master/en-US/advantage/docs.md)
* Annotation router * [Annotation router](https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/router.md)
* Namespace * [Namespace](https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/router.md#namespace)
* Powerful development tools * [Powerful development tools](https://github.com/beego/bee)
* Full stack for Web & API * Full stack for Web & API
## Documentation ## Modules
* [orm](https://github.com/beego/beedoc/tree/master/en-US/mvc/model)
* [English](http://beego.me/docs/intro/) * [session](https://github.com/beego/beedoc/blob/master/en-US/module/session.md)
* [中文文档](http://beego.me/docs/intro/) * [logs](https://github.com/beego/beedoc/blob/master/en-US/module/logs.md)
* [Русский](http://beego.me/docs/intro/) * [config](https://github.com/beego/beedoc/blob/master/en-US/module/config.md)
* [cache](https://github.com/beego/beedoc/blob/master/en-US/module/cache.md)
* [context](https://github.com/beego/beedoc/blob/master/en-US/module/context.md)
* [governor](https://github.com/beego/beedoc/blob/master/en-US/module/governor.md)
* [httplib](https://github.com/beego/beedoc/blob/master/en-US/module/httplib.md)
* [task](https://github.com/beego/beedoc/blob/master/en-US/module/task.md)
* [i18n](https://github.com/beego/beedoc/blob/master/en-US/module/i18n.md)
## Community ## Community
* [http://beego.me/community](http://beego.me/community) * [http://beego.me/community](http://beego.me/community)
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) * Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited
from [here](https://github.com/beego/beedoc/issues/232)
* QQ Group Group ID:523992905 * QQ Group Group ID:523992905
* [Contribution Guide](https://github.com/beego/beedoc/blob/master/en-US/intro/contributing.md).
## License ## License

View File

@ -17,7 +17,7 @@ package adapter
import ( import (
"time" "time"
_ "github.com/astaxie/beego/core/governor" _ "github.com/astaxie/beego/core/admin"
"github.com/astaxie/beego/server/web" "github.com/astaxie/beego/server/web"
) )

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

@ -31,19 +31,19 @@
package toolbox package toolbox
import ( import (
"github.com/astaxie/beego/core/governor" "github.com/astaxie/beego/core/admin"
) )
// AdminCheckList holds health checker map // AdminCheckList holds health checker map
// Deprecated using governor.AdminCheckList // Deprecated using admin.AdminCheckList
var AdminCheckList map[string]HealthChecker var AdminCheckList map[string]HealthChecker
// HealthChecker health checker interface // HealthChecker health checker interface
type HealthChecker governor.HealthChecker type HealthChecker admin.HealthChecker
// AddHealthCheck add health checker with name string // AddHealthCheck add health checker with name string
func AddHealthCheck(name string, hc HealthChecker) { func AddHealthCheck(name string, hc HealthChecker) {
governor.AddHealthCheck(name, hc) admin.AddHealthCheck(name, hc)
AdminCheckList[name] = hc AdminCheckList[name] = hc
} }

View File

@ -19,7 +19,7 @@ import (
"os" "os"
"time" "time"
"github.com/astaxie/beego/core/governor" "github.com/astaxie/beego/core/admin"
) )
var startTime = time.Now() var startTime = time.Now()
@ -31,20 +31,20 @@ func init() {
// ProcessInput parse input command string // ProcessInput parse input command string
func ProcessInput(input string, w io.Writer) { func ProcessInput(input string, w io.Writer) {
governor.ProcessInput(input, w) admin.ProcessInput(input, w)
} }
// MemProf record memory profile in pprof // MemProf record memory profile in pprof
func MemProf(w io.Writer) { func MemProf(w io.Writer) {
governor.MemProf(w) admin.MemProf(w)
} }
// GetCPUProfile start cpu profile monitor // GetCPUProfile start cpu profile monitor
func GetCPUProfile(w io.Writer) { func GetCPUProfile(w io.Writer) {
governor.GetCPUProfile(w) admin.GetCPUProfile(w)
} }
// PrintGCSummary print gc information to io.Writer // PrintGCSummary print gc information to io.Writer
func PrintGCSummary(w io.Writer) { func PrintGCSummary(w io.Writer) {
governor.PrintGCSummary(w) admin.PrintGCSummary(w)
} }

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

@ -28,5 +28,5 @@ var (
const ( const (
// VERSION represent beego web framework version. // VERSION represent beego web framework version.
VERSION = "2.0.0-alpha" VERSION = "2.0.0"
) )

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

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package governor package admin
import ( import (
"github.com/pkg/errors" "github.com/pkg/errors"

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package governor healthcheck // Package admin healthcheck
// //
// type DatabaseCheck struct { // type DatabaseCheck struct {
// } // }
@ -28,7 +28,7 @@
// AddHealthCheck("database",&DatabaseCheck{}) // AddHealthCheck("database",&DatabaseCheck{})
// //
// more docs: http://beego.me/docs/module/toolbox.md // more docs: http://beego.me/docs/module/toolbox.md
package governor package admin
// AdminCheckList holds health checker map // AdminCheckList holds health checker map
var AdminCheckList map[string]HealthChecker var AdminCheckList map[string]HealthChecker

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package governor package admin
import ( import (
"fmt" "fmt"

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package governor package admin
import ( import (
"os" "os"

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

@ -24,7 +24,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/astaxie/beego/core/governor" "github.com/astaxie/beego/core/admin"
) )
type adminController struct { type adminController struct {
@ -51,7 +51,7 @@ func (a *adminController) ProfIndex() {
data = make(map[interface{}]interface{}) data = make(map[interface{}]interface{})
result bytes.Buffer result bytes.Buffer
) )
governor.ProcessInput(command, &result) admin.ProcessInput(command, &result)
data["Content"] = template.HTMLEscapeString(result.String()) data["Content"] = template.HTMLEscapeString(result.String())
if format == "json" && command == "gc summary" { if format == "json" && command == "gc summary" {
@ -88,7 +88,7 @@ func (a *adminController) TaskStatus() {
req.ParseForm() req.ParseForm()
taskname := req.Form.Get("taskname") taskname := req.Form.Get("taskname")
if taskname != "" { if taskname != "" {
cmd := governor.GetCommand("task", "run") cmd := admin.GetCommand("task", "run")
res := cmd.Execute(taskname) res := cmd.Execute(taskname)
if res.IsSuccess() { if res.IsSuccess() {
@ -103,7 +103,7 @@ func (a *adminController) TaskStatus() {
// List Tasks // List Tasks
content := make(M) content := make(M)
resultList := governor.GetCommand("task", "list").Execute().Content.([][]string) resultList := admin.GetCommand("task", "list").Execute().Content.([][]string)
var fields = []string{ var fields = []string{
"Task Name", "Task Name",
"Task Spec", "Task Spec",
@ -141,7 +141,7 @@ func heathCheck(rw http.ResponseWriter, r *http.Request) {
} }
) )
for name, h := range governor.AdminCheckList { for name, h := range admin.AdminCheckList {
if err := h.Check(); err != nil { if err := h.Check(); err != nil {
result = []string{ result = []string{
"error", "error",

View File

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/astaxie/beego/core/governor" "github.com/astaxie/beego/core/admin"
) )
type SampleDatabaseCheck struct { type SampleDatabaseCheck struct {
@ -126,8 +126,8 @@ func TestWriteJSON(t *testing.T) {
func TestHealthCheckHandlerDefault(t *testing.T) { func TestHealthCheckHandlerDefault(t *testing.T) {
endpointPath := "/healthcheck" endpointPath := "/healthcheck"
governor.AddHealthCheck("database", &SampleDatabaseCheck{}) admin.AddHealthCheck("database", &SampleDatabaseCheck{})
governor.AddHealthCheck("cache", &SampleCacheCheck{}) admin.AddHealthCheck("cache", &SampleCacheCheck{})
req, err := http.NewRequest("GET", endpointPath, nil) req, err := http.NewRequest("GET", endpointPath, nil)
if err != nil { if err != nil {
@ -187,8 +187,8 @@ func TestBuildHealthCheckResponseList(t *testing.T) {
func TestHealthCheckHandlerReturnsJSON(t *testing.T) { func TestHealthCheckHandlerReturnsJSON(t *testing.T) {
governor.AddHealthCheck("database", &SampleDatabaseCheck{}) admin.AddHealthCheck("database", &SampleDatabaseCheck{})
governor.AddHealthCheck("cache", &SampleCacheCheck{}) admin.AddHealthCheck("cache", &SampleCacheCheck{})
req, err := http.NewRequest("GET", "/healthcheck?json=true", nil) req, err := http.NewRequest("GET", "/healthcheck?json=true", nil)
if err != nil { if err != nil {

View File

@ -21,7 +21,7 @@ var indexTpl = `
For detail usage please check our document: For detail usage please check our document:
</p> </p>
<p> <p>
<a target="_blank" href="http://beego.me/docs/module/governor.md">Toolbox</a> <a target="_blank" href="http://beego.me/docs/module/admin.md">Toolbox</a>
</p> </p>
<p> <p>
<a target="_blank" href="http://beego.me/docs/advantage/monitor.md">Live Monitor</a> <a target="_blank" href="http://beego.me/docs/advantage/monitor.md">Live Monitor</a>

View File

@ -149,7 +149,8 @@ func (ctx *Context) XSRFToken(key string, expire int64) string {
token, ok := ctx.GetSecureCookie(key, "_xsrf") token, ok := ctx.GetSecureCookie(key, "_xsrf")
if !ok { if !ok {
token = string(utils.RandomCreateBytes(32)) token = string(utils.RandomCreateBytes(32))
ctx.SetSecureCookie(key, "_xsrf", token, expire, "", "", true, true) // TODO make it configurable
ctx.SetSecureCookie(key, "_xsrf", token, expire, "", "")
} }
ctx._xsrfToken = token ctx._xsrfToken = token
} }

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

@ -21,13 +21,13 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/astaxie/beego/core/governor" "github.com/astaxie/beego/core/admin"
) )
type listTaskCommand struct { type listTaskCommand struct {
} }
func (l *listTaskCommand) Execute(params ...interface{}) *governor.Result { func (l *listTaskCommand) Execute(params ...interface{}) *admin.Result {
resultList := make([][]string, 0, len(globalTaskManager.adminTaskList)) resultList := make([][]string, 0, len(globalTaskManager.adminTaskList))
for tname, tk := range globalTaskManager.adminTaskList { for tname, tk := range globalTaskManager.adminTaskList {
result := []string{ result := []string{
@ -39,7 +39,7 @@ func (l *listTaskCommand) Execute(params ...interface{}) *governor.Result {
resultList = append(resultList, result) resultList = append(resultList, result)
} }
return &governor.Result{ return &admin.Result{
Status: 200, Status: 200,
Content: resultList, Content: resultList,
} }
@ -48,9 +48,9 @@ func (l *listTaskCommand) Execute(params ...interface{}) *governor.Result {
type runTaskCommand struct { type runTaskCommand struct {
} }
func (r *runTaskCommand) Execute(params ...interface{}) *governor.Result { func (r *runTaskCommand) Execute(params ...interface{}) *admin.Result {
if len(params) == 0 { if len(params) == 0 {
return &governor.Result{ return &admin.Result{
Status: 400, Status: 400,
Error: errors.New("task name not passed"), Error: errors.New("task name not passed"),
} }
@ -59,7 +59,7 @@ func (r *runTaskCommand) Execute(params ...interface{}) *governor.Result {
tn, ok := params[0].(string) tn, ok := params[0].(string)
if !ok { if !ok {
return &governor.Result{ return &admin.Result{
Status: 400, Status: 400,
Error: errors.New("parameter is invalid"), Error: errors.New("parameter is invalid"),
} }
@ -68,17 +68,17 @@ func (r *runTaskCommand) Execute(params ...interface{}) *governor.Result {
if t, ok := globalTaskManager.adminTaskList[tn]; ok { if t, ok := globalTaskManager.adminTaskList[tn]; ok {
err := t.Run(context.Background()) err := t.Run(context.Background())
if err != nil { if err != nil {
return &governor.Result{ return &admin.Result{
Status: 500, Status: 500,
Error: err, Error: err,
} }
} }
return &governor.Result{ return &admin.Result{
Status: 200, Status: 200,
Content: t.GetStatus(context.Background()), Content: t.GetStatus(context.Background()),
} }
} else { } else {
return &governor.Result{ return &admin.Result{
Status: 400, Status: 400,
Error: errors.New(fmt.Sprintf("task with name %s not found", tn)), Error: errors.New(fmt.Sprintf("task with name %s not found", tn)),
} }
@ -87,6 +87,6 @@ func (r *runTaskCommand) Execute(params ...interface{}) *governor.Result {
} }
func registerCommands() { func registerCommands() {
governor.RegisterCommand("task", "list", &listTaskCommand{}) admin.RegisterCommand("task", "list", &listTaskCommand{})
governor.RegisterCommand("task", "run", &runTaskCommand{}) admin.RegisterCommand("task", "run", &runTaskCommand{})
} }

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