mirror of
https://gitee.com/johng/gf
synced 2026-06-09 02:57:43 +08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d37626981 | |||
| d0ed3b979d | |||
| fa256aec9f | |||
| 86834c5a15 | |||
| c2046157d6 | |||
| cdb2cc89c0 | |||
| 4964c09a77 | |||
| ef34b2c9ce | |||
| 9afe242293 | |||
| 136d93d373 | |||
| 3102cec5b8 | |||
| e28eb9da04 | |||
| 754ed86dfb | |||
| 7058e4f2c4 | |||
| 704a5dbd73 | |||
| fbd4ce8c2e | |||
| cb3ce71cdc | |||
| 7f44f2f5e4 | |||
| 66efbe63f0 | |||
| 49a1308875 | |||
| 4332580c01 |
33
.gitee/ISSUE_TEMPLATE.MD
Normal file
33
.gitee/ISSUE_TEMPLATE.MD
Normal file
@ -0,0 +1,33 @@
|
||||
<!-- 为更高效率地交流并解决问题,请按照以下模板提交issue,感谢! -->
|
||||
### 1. 您当前使用的`Go`版本(将终端`go version`指令结果粘贴到下面)?
|
||||
|
||||
<pre>
|
||||
$ go version
|
||||
|
||||
</pre>
|
||||
|
||||
### 2. 您当前使用的`GoFrame`版本(可以查看`go.mod`/`version.go`/`gf.VERSION`)?
|
||||
|
||||
|
||||
|
||||
### 3. 更新到最新的框架版本是否能够解决问题?
|
||||
|
||||
|
||||
|
||||
### 4. 问题描述?
|
||||
|
||||
<!--
|
||||
请您尽可能地提供一份最短的,可复现问题的代码。
|
||||
代码尽可能地完整,最好是可以直接编译运行。
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### 5. 您期望得到的结果?
|
||||
|
||||
|
||||
|
||||
### 6. 您实际得到的结果?
|
||||
|
||||
|
||||
|
||||
33
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
33
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
### 1. What version of `Go` are you using (`go version`)?
|
||||
|
||||
<pre>
|
||||
$ go version
|
||||
|
||||
</pre>
|
||||
|
||||
### 2. What version of `GoFrame` are you using?
|
||||
|
||||
|
||||
|
||||
### 3. Does this issue reproduce with the latest release?
|
||||
|
||||
|
||||
|
||||
### 4. What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a copy of shortest codes for reproducing the error.
|
||||
A complete runnable program is best.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### 5. What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### 6. What did you see instead?
|
||||
|
||||
|
||||
|
||||
@ -81,6 +81,14 @@ func main() {
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 捐赠
|
||||
|
||||
捐赠支持`GF`框架的研发,
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
<a href="https://goframe.org/images/donate.png" target="_blank">
|
||||
<img src="https://goframe.org/images/donate.png" width="300"/>
|
||||
</a>
|
||||
|
||||
# 贡献者(TOP 10)
|
||||
|
||||
|
||||
35
RELEASE.MD
35
RELEASE.MD
@ -1,3 +1,38 @@
|
||||
# `v1.5.8` (2019-02-28)
|
||||
|
||||
## 新特性
|
||||
1. 主库从`gitee`迁移到了`github`( https://github.com/gogf/gf ),`gitee`作为镜像站,用于国内的代码贡献及ISSUE提交,迁移说明详见:https://goframe.org/upgradeto150
|
||||
1. 对常用的`container`数组模块: `garray`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/garray
|
||||
1. 对常用的`container`集合模块: `gset`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gset
|
||||
1. 对常用的`container`MAP模块: `gmap`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gmap
|
||||
1. 对常用的字符串模块: `gstr`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/text/gstr
|
||||
1. 改进`gform`中对`struct`/`*struct`参数的支持,`*Insert/*Save/*Replace/*Update/Where/Data`方法的参数调整为`interface{}`类型,并支持任意类型的: `string/map/slice/struct/*struct`参数传递,具体请参考:https://goframe.org/database/orm/chaining
|
||||
1. 新增/完善若干模块的单元测试用例, 包括:`gvalid`/`gregex`/`garray`/`gset`/`gmap`/`gstr`/`gconv`/`ghttp`/`gdb`;
|
||||
1. 由于`gkafka`模块比较重,且不是框架核心模块,因此将该模块迁移到新的仓库中独立管理,并去掉相关依赖包:https://github.com/gogf/gkafka
|
||||
1. 新增`greuseport`模块,用以实现TCP的`REUSEPORT`特性:https://godoc.org/github.com/gogf/gf/g/net/greuseport
|
||||
|
||||
## 新功能/改进
|
||||
1. 去掉模板引擎内置变量中自动初始化`session`对象带来的内存占用问题;
|
||||
1. `ghttp.Client`改进,增加若干方法,详见:https://goframe.org/net/ghttp/client
|
||||
1. `ghttp`分组路由增加`COMMON`方法,用以注册常用的`HTTP METHOD`(`GET/PUT/POST/DELETE`)路由;
|
||||
1. 更新框架依赖的`golang.org/x/sys`模块;
|
||||
1. 改进`gform`的批量操作(`Batch*`操作)返回结果对象,可以通过该结果对象获得批量操作准确的受影响记录行数;
|
||||
1. 将`gstr`/`gregex`模块从`util`分类迁移到了`text`分类目录下;
|
||||
1. 将`gtest`模块从`util`分类迁移到了`test`分类目录下;
|
||||
1. 完善`glog`方法注释;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复带点的邮件格式,用`gvalid.Check`的"`email`"规则不能匹配成功;
|
||||
1. 修复`gvalid.Check`在`regex`规则下的检查失败问题;
|
||||
1. 修复`gcron`模块定时规则中天和周不允许`?`符号的问题;
|
||||
1. 修复`ghttp.Server`在部分异常情况下仍然返回`200`状态码的问题;
|
||||
1. 修复`gfpool`模块中由于原子操作问题造成的高并发"内存泄露"问题;
|
||||
1. 修复分组路由注册对象/控制时,方法`Index`的路由仅能通过`/xxx/index`访问的问题;
|
||||
1. 修复模板引擎使用中,当不存在`config.toml`(即使没使用)配置文件时的报错问题;
|
||||
1. 其他一些修复;
|
||||
|
||||
|
||||
|
||||
# `v1.4.6` (2019-01-24)
|
||||
|
||||
## 新特性
|
||||
|
||||
2
TODO.MD
2
TODO.MD
@ -52,7 +52,7 @@
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -37,8 +37,8 @@ type DB interface {
|
||||
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
|
||||
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
|
||||
doPrepare(link dbLink, query string) (*sql.Stmt, error)
|
||||
doInsert(link dbLink, table string, data Map, option int) (result sql.Result, err error)
|
||||
doBatchInsert(link dbLink, table string, list List, batch int, option int) (result sql.Result, err error)
|
||||
doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
doDelete(link dbLink, table string, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
|
||||
@ -61,14 +61,14 @@ type DB interface {
|
||||
Begin() (*TX, error)
|
||||
|
||||
// 数据表插入/更新/保存操作
|
||||
Insert(table string, data Map) (sql.Result, error)
|
||||
Replace(table string, data Map) (sql.Result, error)
|
||||
Save(table string, data Map) (sql.Result, error)
|
||||
Insert(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
Replace(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
Save(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
|
||||
// 数据表插入/更新/保存操作(批量)
|
||||
BatchInsert(table string, list List, batch int) (sql.Result, error)
|
||||
BatchReplace(table string, list List, batch int) (sql.Result, error)
|
||||
BatchSave(table string, list List, batch int) (sql.Result, error)
|
||||
BatchInsert(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
BatchReplace(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
BatchSave(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
|
||||
// 数据修改/删除
|
||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||
@ -149,8 +149,11 @@ const (
|
||||
OPTION_REPLACE = 1
|
||||
OPTION_SAVE = 2
|
||||
OPTION_IGNORE = 3
|
||||
// 默认批量操作的数量值(Batch*操作)
|
||||
gDEFAULT_BATCH_NUM = 10
|
||||
// 默认的连接池连接存活时间(秒)
|
||||
gDEFAULT_CONN_MAX_LIFE_TIME = 30
|
||||
|
||||
)
|
||||
|
||||
// 使用默认/指定分组配置进行连接,数据库集群配置项:default
|
||||
|
||||
@ -14,8 +14,8 @@ import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
@ -234,33 +234,60 @@ func (bs *dbBase) Begin() (*TX, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
func (bs *dbBase) Insert(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_INSERT)
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Insert(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (bs *dbBase) Replace(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_REPLACE)
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Replace(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (bs *dbBase) Save(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_SAVE)
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Save(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// insert、replace, save, ignore操作
|
||||
// 0: insert: 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做
|
||||
func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (result sql.Result, err error) {
|
||||
var fields []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
charl, charr := bs.db.getChars()
|
||||
for k, v := range data {
|
||||
fields = append(fields, charl + k + charr)
|
||||
// 支持insert、replace, save, ignore操作。
|
||||
// 0: insert: 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回;
|
||||
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条;
|
||||
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据;
|
||||
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做;
|
||||
//
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error) {
|
||||
var fields []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
var dataMap Map
|
||||
// 使用反射判断data数据类型,如果为slice类型,那么自动转为批量操作
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
return bs.db.doBatchInsert(link, table, data, option, batch...)
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
dataMap = Map(gconv.Map(data))
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
charL, charR := bs.db.getChars()
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL + k + charR)
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
@ -268,11 +295,11 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for k, _ := range data {
|
||||
for k, _ := range dataMap {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charl, k, charr,
|
||||
charl, k, charr,
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
)
|
||||
}
|
||||
@ -290,28 +317,55 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入
|
||||
func (bs *dbBase) BatchInsert(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_INSERT)
|
||||
func (bs *dbBase) BatchInsert(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (bs *dbBase) BatchReplace(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_REPLACE)
|
||||
func (bs *dbBase) BatchReplace(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (bs *dbBase) BatchSave(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_SAVE)
|
||||
func (bs *dbBase) BatchSave(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// 批量写入数据
|
||||
func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int, option int) (result sql.Result, err error) {
|
||||
var keys []string
|
||||
var values []string
|
||||
var bvalues []string
|
||||
var params []interface{}
|
||||
// 批量写入数据, 参数list支持slice类型,例如: []map/[]struct/[]*struct。
|
||||
func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error) {
|
||||
var keys []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
listMap := (List)(nil)
|
||||
switch v := list.(type) {
|
||||
case List:
|
||||
listMap = v
|
||||
case Map:
|
||||
listMap = List{v}
|
||||
default:
|
||||
rv := reflect.ValueOf(list)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// 如果是slice,那么转换为List类型
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = gconv.Map(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
listMap = List{Map(gconv.Map(list))}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
// 判断长度
|
||||
if len(list) < 1 {
|
||||
if len(listMap) < 1 {
|
||||
return result, errors.New("empty data list")
|
||||
}
|
||||
if link == nil {
|
||||
@ -320,14 +374,15 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
}
|
||||
}
|
||||
// 首先获取字段名称及记录长度
|
||||
for k, _ := range list[0] {
|
||||
keys = append(keys, k)
|
||||
values = append(values, "?")
|
||||
holders := []string(nil)
|
||||
for k, _ := range listMap[0] {
|
||||
keys = append(keys, k)
|
||||
holders = append(holders, "?")
|
||||
}
|
||||
batchResult := new(batchSqlResult)
|
||||
charl, charr := bs.db.getChars()
|
||||
keyStr := charl + strings.Join(keys, charl + "," + charr) + charr
|
||||
valueHolderStr := "(" + strings.Join(values, ",") + ")"
|
||||
charL, charR := bs.db.getChars()
|
||||
keyStr := charL + strings.Join(keys, charL + "," + charR) + charR
|
||||
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
|
||||
// 操作判断
|
||||
operation := getInsertOperationByOption(option)
|
||||
updateStr := ""
|
||||
@ -336,22 +391,26 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
for _, k := range keys {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charl, k, charr,
|
||||
charl, k, charr,
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
}
|
||||
// 构造批量写入数据格式(注意map的遍历是无序的)
|
||||
for i := 0; i < len(list); i++ {
|
||||
batchNum := gDEFAULT_BATCH_NUM
|
||||
if len(batch) > 0 {
|
||||
batchNum = batch[0]
|
||||
}
|
||||
for i := 0; i < len(listMap); i++ {
|
||||
for _, k := range keys {
|
||||
params = append(params, list[i][k])
|
||||
params = append(params, listMap[i][k])
|
||||
}
|
||||
bvalues = append(bvalues, valueHolderStr)
|
||||
if len(bvalues) == batch {
|
||||
values = append(values, valueHolderStr)
|
||||
if len(values) == batchNum {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
operation, table, keyStr, strings.Join(values, ","),
|
||||
updateStr),
|
||||
params...)
|
||||
if err != nil {
|
||||
@ -363,14 +422,14 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
batchResult.lastResult = r
|
||||
batchResult.rowsAffected += n
|
||||
}
|
||||
params = params[:0]
|
||||
bvalues = bvalues[:0]
|
||||
params = params[:0]
|
||||
values = values[:0]
|
||||
}
|
||||
}
|
||||
// 处理最后不构成指定批量的数据
|
||||
if len(bvalues) > 0 {
|
||||
if len(values) > 0 {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
operation, table, keyStr, strings.Join(values, ","),
|
||||
updateStr),
|
||||
params...)
|
||||
if err != nil {
|
||||
@ -386,8 +445,8 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
return batchResult, nil
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型。
|
||||
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
link, err := bs.db.Master()
|
||||
if err != nil {
|
||||
@ -396,23 +455,30 @@ func (bs *dbBase) Update(table string, data interface{}, condition interface{},
|
||||
return bs.db.doUpdate(link, table, data, condition, args ...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型类型。
|
||||
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
params := ([]interface{})(nil)
|
||||
updates := ""
|
||||
charl, charr := bs.db.getChars()
|
||||
refValue := reflect.ValueOf(data)
|
||||
if refValue.Kind() == reflect.Map {
|
||||
var fields []string
|
||||
keys := refValue.MapKeys()
|
||||
for _, k := range keys {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charl, k, charr))
|
||||
params = append(params, gconv.String(refValue.MapIndex(k).Interface()))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
} else {
|
||||
updates = gconv.String(data)
|
||||
charL, charR := bs.db.getChars()
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
var fields []string
|
||||
for k, v := range gconv.Map(data) {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR))
|
||||
params = append(params, gconv.String(v))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
for _, v := range args {
|
||||
params = append(params, gconv.String(v))
|
||||
|
||||
@ -24,23 +24,30 @@ import (
|
||||
func formatCondition(where interface{}, args []interface{}) (string, []interface{}) {
|
||||
// 条件字符串处理
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if reflect.ValueOf(where).Kind() == reflect.Map {
|
||||
ks := reflect.ValueOf(where).MapKeys()
|
||||
vs := reflect.ValueOf(where)
|
||||
for _, k := range ks {
|
||||
key := gconv.String(k.Interface())
|
||||
value := gconv.String(vs.MapIndex(k).Interface())
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(where)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
for k, v := range gconv.Map(where) {
|
||||
key := gconv.String(k)
|
||||
value := gconv.String(v)
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
if gstr.IsNumeric(value) || value == "?" {
|
||||
buffer.WriteString(key + "=" + value)
|
||||
} else {
|
||||
buffer.WriteString(key + "='" + value + "'")
|
||||
}
|
||||
}
|
||||
if gstr.IsNumeric(value) || value == "?" {
|
||||
buffer.WriteString(key + "=" + value)
|
||||
} else {
|
||||
buffer.WriteString(key + "='" + value + "'")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buffer.Write(gconv.Bytes(where))
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
if buffer.Len() == 0 {
|
||||
buffer.WriteString("1=1")
|
||||
@ -57,6 +64,7 @@ func formatCondition(where interface{}, args []interface{}) (string, []interface
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// Where条件参数支持slice类型
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
@ -109,13 +117,13 @@ func formatError(err error, query string, args ...interface{}) error {
|
||||
|
||||
// 根据insert选项获得操作名称
|
||||
func getInsertOperationByOption(option int) string {
|
||||
oper := "INSERT"
|
||||
operator := "INSERT"
|
||||
switch option {
|
||||
case OPTION_REPLACE:
|
||||
oper = "REPLACE"
|
||||
operator = "REPLACE"
|
||||
case OPTION_SAVE:
|
||||
case OPTION_IGNORE:
|
||||
oper = "INSERT IGNORE"
|
||||
operator = "INSERT IGNORE"
|
||||
}
|
||||
return oper
|
||||
return operator
|
||||
}
|
||||
|
||||
@ -200,13 +200,14 @@ func (md *Model) Cache(time int, name ... string) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,操作数据记录项,可以是string/Map, 也可以是:key,value,key,value,...
|
||||
func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
// 链式操作,操作数据项,参数data类型支持 string/map/slice/struct/*struct ,
|
||||
// 也可以是:key,value,key,value,...。
|
||||
func (md *Model) Data(data ...interface{}) *Model {
|
||||
model := md.Clone()
|
||||
if len(data) > 1 {
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
m[gconv.String(data[i])] = data[i+1]
|
||||
m[gconv.String(data[i])] = data[i + 1]
|
||||
}
|
||||
model.data = m
|
||||
} else {
|
||||
@ -223,6 +224,7 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// 如果是slice,那么转换为List类型
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
list := make(List, rv.Len())
|
||||
@ -230,8 +232,9 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
list[i] = gconv.Map(rv.Index(i).Interface())
|
||||
}
|
||||
model.data = list
|
||||
case reflect.Map:
|
||||
model.data = gconv.Map(data[0])
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
model.data = Map(gconv.Map(data[0]))
|
||||
default:
|
||||
model.data = data[0]
|
||||
}
|
||||
@ -240,7 +243,9 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Insert/BatchInsert
|
||||
// 链式操作, CURD - Insert/BatchInsert。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Insert() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -279,7 +284,9 @@ func (md *Model) Insert() (result sql.Result, err error) {
|
||||
return nil, errors.New("inserting into table with invalid data type")
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Replace/BatchReplace
|
||||
// 链式操作, CURD - Replace/BatchReplace。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Replace() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -318,7 +325,9 @@ func (md *Model) Replace() (result sql.Result, err error) {
|
||||
return nil, errors.New("replacing into table with invalid data type")
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Save/BatchSave
|
||||
// 链式操作, CURD - Save/BatchSave。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Save() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -330,7 +339,7 @@ func (md *Model) Save() (result sql.Result, err error) {
|
||||
}
|
||||
// 批量操作
|
||||
if list, ok := md.data.(List); ok {
|
||||
batch := 10
|
||||
batch := gDEFAULT_BATCH_NUM
|
||||
if md.batch > 0 {
|
||||
batch = md.batch
|
||||
}
|
||||
|
||||
@ -85,8 +85,8 @@ func (bs *dbBase) getTableFields(table string) (fields map[string]string, err er
|
||||
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
|
||||
v := bs.cache.GetOrSetFunc("table_fields_" + table, func() interface{} {
|
||||
result := (Result)(nil)
|
||||
charl, charr := bs.db.getChars()
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charl, table, charr))
|
||||
charL, charR := bs.db.getChars()
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -100,33 +100,33 @@ func (tx *TX) GetCount(query string, args ...interface{}) (int, error) {
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
func (tx *TX) Insert(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT)
|
||||
func (tx *TX) Insert(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (tx *TX) Replace(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE)
|
||||
func (tx *TX) Replace(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (tx *TX) Save(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE)
|
||||
func (tx *TX) Save(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入
|
||||
func (tx *TX) BatchInsert(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_INSERT)
|
||||
func (tx *TX) BatchInsert(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (tx *TX) BatchReplace(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_REPLACE)
|
||||
func (tx *TX) BatchReplace(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (tx *TX) BatchSave(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_SAVE)
|
||||
func (tx *TX) BatchSave(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
|
||||
@ -56,6 +56,91 @@ func TestDbBase_Insert(t *testing.T) {
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
// normal map
|
||||
result, err := db.Insert("user", map[interface{}]interface{} {
|
||||
"id" : "2",
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
// struct
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
result, err = db.Insert("user", User{
|
||||
Id : 3,
|
||||
Passport : "t3",
|
||||
Password : "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname : "T3",
|
||||
CreateTime : gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue("select `passport` from `user` where id=?", 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t3")
|
||||
|
||||
// *struct
|
||||
result, err = db.Insert("user", &User{
|
||||
Id : 4,
|
||||
Passport : "t4",
|
||||
Password : "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname : "T4",
|
||||
CreateTime : gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err = db.GetValue("select `passport` from `user` where id=?", 4)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t4")
|
||||
|
||||
// batch with Insert
|
||||
if r, err := db.Insert("user", []interface{} {
|
||||
map[interface{}]interface{} {
|
||||
"id" : 200,
|
||||
"passport" : "t200",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T200",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{} {
|
||||
"id" : 300,
|
||||
"passport" : "t300",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T300",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
// clear unnecessary data
|
||||
result, err = db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 5)
|
||||
}
|
||||
|
||||
func TestDbBase_BatchInsert(t *testing.T) {
|
||||
@ -80,6 +165,36 @@ func TestDbBase_BatchInsert(t *testing.T) {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
result, err := db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
// []interface{}
|
||||
if r, err := db.BatchInsert("user", []interface{} {
|
||||
map[interface{}]interface{} {
|
||||
"id" : 2,
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{} {
|
||||
"id" : 3,
|
||||
"passport" : "t3",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T3",
|
||||
"create_time" : gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Save(t *testing.T) {
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 基本测试
|
||||
func TestModel_Insert(t *testing.T) {
|
||||
result, err := db.Table("user").Filter().Data(g.Map{
|
||||
"id" : 1,
|
||||
@ -23,6 +24,69 @@ func TestModel_Insert(t *testing.T) {
|
||||
}
|
||||
n, _ := result.LastInsertId()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
result, err = db.Table("user").Filter().Data(map[interface{}]interface{} {
|
||||
"id" : "2",
|
||||
"uid" : "2",
|
||||
"passport" : "t2",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T2",
|
||||
"create_time" : gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Uid int `gconv:"uid"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
result, err = db.Table("user").Filter().Data(User{
|
||||
Id : 3,
|
||||
Uid : 3,
|
||||
Passport : "t3",
|
||||
Password : "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname : "T3",
|
||||
CreateTime : gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.Table("user").Fields("passport").Where("id=3").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t3")
|
||||
|
||||
result, err = db.Table("user").Filter().Data(&User{
|
||||
Id : 4,
|
||||
Uid : 4,
|
||||
Passport : "t4",
|
||||
Password : "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname : "T4",
|
||||
CreateTime : gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err = db.Table("user").Fields("passport").Where("id=4").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t4")
|
||||
|
||||
result, err = db.Table("user").Where("id>?", 1).Delete()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 3)
|
||||
}
|
||||
|
||||
func TestModel_Batch(t *testing.T) {
|
||||
@ -191,6 +255,7 @@ func TestModel_GroupBy(t *testing.T) {
|
||||
gtest.Assert(result[0]["nickname"].String(), "T111")
|
||||
}
|
||||
|
||||
// slice
|
||||
func TestModel_Where1(t *testing.T) {
|
||||
result, err := db.Table("user").Where("id IN(?)", g.Slice{1,3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
@ -201,6 +266,7 @@ func TestModel_Where1(t *testing.T) {
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
}
|
||||
|
||||
// slice
|
||||
func TestModel_Where2(t *testing.T) {
|
||||
result, err := db.Table("user").Where("nickname=? AND id IN(?)", "T3", g.Slice{1,3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
11
g/g_func.go
11
g/g_func.go
@ -23,27 +23,36 @@ const (
|
||||
LOG_LEVEL_CRIT = glog.LEVEL_CRIT
|
||||
)
|
||||
|
||||
// NewVar creates a *Var.
|
||||
//
|
||||
// 动态变量
|
||||
func NewVar(i interface{}, unsafe...bool) *Var {
|
||||
return gvar.New(i, unsafe...)
|
||||
}
|
||||
|
||||
// Wait blocks until all the web servers shutdown.
|
||||
//
|
||||
// 阻塞等待HTTPServer执行完成(同一进程多HTTPServer情况下)
|
||||
func Wait() {
|
||||
ghttp.Wait()
|
||||
}
|
||||
|
||||
// Dump dumps a variable to stdout with more manually readable.
|
||||
//
|
||||
// 打印变量
|
||||
func Dump(i...interface{}) {
|
||||
gutil.Dump(i...)
|
||||
}
|
||||
|
||||
// Throw throws a exception, which can be caught by Catch function.
|
||||
// It always be used in TryCatch function.
|
||||
//
|
||||
// 抛出一个异常
|
||||
func Throw(exception interface{}) {
|
||||
gutil.Throw(exception)
|
||||
}
|
||||
|
||||
// try...catch...
|
||||
// TryCatch does the try...catch... logic.
|
||||
func TryCatch(try func(), catch ... func(exception interface{})) {
|
||||
gutil.TryCatch(try, catch...)
|
||||
}
|
||||
@ -10,16 +10,22 @@ import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
// Disable/Enabled debug of logging globally.
|
||||
//
|
||||
// 是否显示调试信息
|
||||
func SetDebug(debug bool) {
|
||||
glog.SetDebug(debug)
|
||||
}
|
||||
|
||||
// Set the logging level globally.
|
||||
//
|
||||
// 设置日志的显示等级
|
||||
func SetLogLevel(level int) {
|
||||
glog.SetLevel(level)
|
||||
}
|
||||
|
||||
// Get the global logging level.
|
||||
//
|
||||
// 获取设置的日志显示等级
|
||||
func GetLogLevel() int {
|
||||
return glog.GetLevel()
|
||||
|
||||
@ -17,42 +17,58 @@ import (
|
||||
"github.com/gogf/gf/g/os/gcfg"
|
||||
)
|
||||
|
||||
// Get an instance of http server with specified name.
|
||||
//
|
||||
// HTTPServer单例对象
|
||||
func Server(name...interface{}) *ghttp.Server {
|
||||
return ghttp.GetServer(name...)
|
||||
}
|
||||
|
||||
// Get an instance of tcp server with specified name.
|
||||
//
|
||||
// TCPServer单例对象
|
||||
func TCPServer(name...interface{}) *gtcp.Server {
|
||||
return gtcp.GetServer(name...)
|
||||
}
|
||||
|
||||
// Get an instance of udp server with specified name.
|
||||
//
|
||||
// UDPServer单例对象
|
||||
func UDPServer(name...interface{}) *gudp.Server {
|
||||
return gudp.GetServer(name...)
|
||||
}
|
||||
|
||||
// Get an instance of template engine object with specified name.
|
||||
//
|
||||
// 核心对象:View
|
||||
func View(name...string) *gview.View {
|
||||
return gins.View(name...)
|
||||
}
|
||||
|
||||
// Config配置管理对象
|
||||
// Get an instance of config object with specified default config file name.
|
||||
//
|
||||
// Config配置管理对象,
|
||||
// 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录
|
||||
func Config(file...string) *gcfg.Config {
|
||||
return gins.Config(file...)
|
||||
}
|
||||
|
||||
// Get an instance of database ORM object with specified configuration group name.
|
||||
//
|
||||
// 数据库操作对象,使用了连接池
|
||||
func Database(name...string) gdb.DB {
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
||||
// Alias of Database.
|
||||
//
|
||||
// (别名)Database
|
||||
func DB(name...string) gdb.DB {
|
||||
return gins.Database(name...)
|
||||
}
|
||||
|
||||
// Get an instance of redis client with specified configuration group name.
|
||||
//
|
||||
// Redis操作对象,使用了连接池
|
||||
func Redis(name...string) *gredis.Redis {
|
||||
return gins.Redis(name...)
|
||||
|
||||
@ -24,11 +24,13 @@ import (
|
||||
|
||||
// http客户端
|
||||
type Client struct {
|
||||
http.Client // 底层http client对象
|
||||
header map[string]string // HEADER信息Map
|
||||
prefix string // 设置请求的URL前缀
|
||||
authUser string // HTTP基本权限设置:名称
|
||||
authPass string // HTTP基本权限设置:密码
|
||||
http.Client // 底层http client对象
|
||||
header map[string]string // HEADER信息Map
|
||||
cookies map[string]string // 自定义COOKIE
|
||||
prefix string // 设置请求的URL前缀
|
||||
authUser string // HTTP基本权限设置:名称
|
||||
authPass string // HTTP基本权限设置:密码
|
||||
browserMode bool // 是否模拟浏览器模式(自动保存提交COOKIE)
|
||||
}
|
||||
|
||||
// http客户端对象指针
|
||||
@ -39,10 +41,16 @@ func NewClient() (*Client) {
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
},
|
||||
header : make(map[string]string),
|
||||
header : make(map[string]string),
|
||||
cookies : make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// 是否模拟浏览器模式(自动保存提交COOKIE)
|
||||
func (c *Client) SetBrowserMode(enabled bool) {
|
||||
c.browserMode = enabled
|
||||
}
|
||||
|
||||
// 设置HTTP Header
|
||||
func (c *Client) SetHeader(key, value string) {
|
||||
c.header[key] = value
|
||||
@ -58,6 +66,18 @@ func (c *Client) SetHeaderRaw(header string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置COOKIE
|
||||
func (c *Client) SetCookie(key, value string) {
|
||||
c.cookies[key] = value
|
||||
}
|
||||
|
||||
// 使用Map设置COOKIE
|
||||
func (c *Client) SetCookieMap(cookieMap map[string]string) {
|
||||
for k, v := range cookieMap {
|
||||
c.cookies[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 设置请求的URL前缀
|
||||
func (c *Client) SetPrefix(prefix string) {
|
||||
c.prefix = prefix
|
||||
@ -143,6 +163,19 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
// COOKIE
|
||||
if len(c.cookies) > 0 {
|
||||
headerCookie := ""
|
||||
for k, v := range c.cookies {
|
||||
if len(headerCookie) > 0 {
|
||||
headerCookie += ";"
|
||||
}
|
||||
headerCookie += k + "=" + v
|
||||
}
|
||||
if len(headerCookie) > 0 {
|
||||
req.Header.Set("Cookie", headerCookie)
|
||||
}
|
||||
}
|
||||
// HTTP账号密码
|
||||
if len(c.authUser) > 0 {
|
||||
req.SetBasicAuth(c.authUser, c.authPass)
|
||||
@ -152,8 +185,10 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &ClientResponse{}
|
||||
r.Response = *resp
|
||||
r := &ClientResponse{
|
||||
cookies : make(map[string]string),
|
||||
}
|
||||
r.Response = resp
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@ -254,13 +289,40 @@ func (c *Client) DoRequest(method, url string, data...string) (*ClientResponse,
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
// COOKIE
|
||||
if len(c.cookies) > 0 {
|
||||
headerCookie := ""
|
||||
for k, v := range c.cookies {
|
||||
if len(headerCookie) > 0 {
|
||||
headerCookie += ";"
|
||||
}
|
||||
headerCookie += k + "=" + v
|
||||
}
|
||||
if len(headerCookie) > 0 {
|
||||
req.Header.Set("Cookie", headerCookie)
|
||||
}
|
||||
}
|
||||
// 执行请求
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &ClientResponse{}
|
||||
r.Response = *resp
|
||||
r := &ClientResponse{
|
||||
cookies : make(map[string]string),
|
||||
}
|
||||
r.Response = resp
|
||||
// 浏览器模式
|
||||
if c.browserMode {
|
||||
now := time.Now()
|
||||
for _, v := range r.Cookies() {
|
||||
if v.Expires.UnixNano() < now.UnixNano() {
|
||||
delete(c.cookies, v.Name)
|
||||
} else {
|
||||
c.cookies[v.Name] = v.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
//fmt.Println(url, c.cookies)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
||||
@ -10,11 +10,27 @@ package ghttp
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 客户端请求结果对象
|
||||
type ClientResponse struct {
|
||||
http.Response
|
||||
*http.Response
|
||||
cookies map[string]string
|
||||
}
|
||||
|
||||
// 获得返回的指定COOKIE值
|
||||
func (r *ClientResponse) GetCookie(key string) string {
|
||||
if r.cookies == nil {
|
||||
now := time.Now()
|
||||
for _, v := range r.Cookies() {
|
||||
if v.Expires.UnixNano() < now.UnixNano() {
|
||||
continue
|
||||
}
|
||||
r.cookies[v.Name] = v.Value
|
||||
}
|
||||
}
|
||||
return r.cookies[key]
|
||||
}
|
||||
|
||||
// 获取返回的数据(二进制).
|
||||
|
||||
@ -79,7 +79,7 @@ func (r *Request) GetVar(key string, def ... interface{}) gvar.VarRead {
|
||||
return r.GetRequestVar(key, def...)
|
||||
}
|
||||
|
||||
// 获取原始请求输入字符串
|
||||
// 获取原始请求输入二进制。
|
||||
func (r *Request) GetRaw() []byte {
|
||||
if r.rawContent == nil {
|
||||
r.rawContent, _ = ioutil.ReadAll(r.Body)
|
||||
@ -87,6 +87,14 @@ func (r *Request) GetRaw() []byte {
|
||||
return r.rawContent
|
||||
}
|
||||
|
||||
// 获取原始请求输入字符串。
|
||||
func (r *Request) GetRawString() string {
|
||||
if r.rawContent == nil {
|
||||
r.rawContent, _ = ioutil.ReadAll(r.Body)
|
||||
}
|
||||
return string(r.rawContent)
|
||||
}
|
||||
|
||||
// 获取原始json请求输入字符串,并解析为json对象
|
||||
func (r *Request) GetJson() *gjson.Json {
|
||||
data := r.GetRaw()
|
||||
|
||||
@ -16,9 +16,10 @@ func (r *Request) initGet() {
|
||||
if !r.parsedGet {
|
||||
r.queryVars = r.URL.Query()
|
||||
if strings.EqualFold(r.Method, "GET") {
|
||||
if raw := r.GetRaw(); len(raw) > 0 {
|
||||
for _, item := range strings.Split(string(raw), "&") {
|
||||
array := strings.Split(item, "=")
|
||||
if raw := r.GetRawString(); len(raw) > 0 {
|
||||
var array []string
|
||||
for _, item := range strings.Split(raw, "&") {
|
||||
array = strings.Split(item, "=")
|
||||
r.queryVars[array[0]] = append(r.queryVars[array[0]], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,6 @@ func (r *Response) buildInVars(params map[string]interface{}) map[string]interfa
|
||||
if params == nil {
|
||||
params = make(map[string]interface{})
|
||||
}
|
||||
|
||||
c := gins.Config()
|
||||
if c.GetFilePath() != "" {
|
||||
params["Config"] = c.GetMap("")
|
||||
|
||||
@ -42,6 +42,7 @@ func GetCookie(r *Request) *Cookie {
|
||||
}
|
||||
return &Cookie {
|
||||
request : r,
|
||||
server : r.Server,
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +53,6 @@ func (c *Cookie) init() {
|
||||
c.path = c.request.Server.GetCookiePath()
|
||||
c.domain = c.request.Server.GetCookieDomain()
|
||||
c.maxage = c.request.Server.GetCookieMaxAge()
|
||||
c.server = c.request.Server
|
||||
c.response = c.request.Response
|
||||
// 如果没有设置COOKIE有效域名,那么设置HOST为默认有效域名
|
||||
if c.domain == "" {
|
||||
@ -115,6 +115,11 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge int, httpOnly
|
||||
}
|
||||
}
|
||||
|
||||
// 获得客户端提交的SessionId
|
||||
func (c *Cookie) GetSessionId() string {
|
||||
return c.Get(c.server.GetSessionIdName())
|
||||
}
|
||||
|
||||
// 设置SessionId
|
||||
func (c *Cookie) SetSessionId(id string) {
|
||||
c.Set(c.server.GetSessionIdName(), id)
|
||||
@ -133,9 +138,14 @@ func (c *Cookie) Get(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 删除COOKIE,使用默认的domain&path
|
||||
func (c *Cookie) Remove(key string) {
|
||||
c.SetCookie(key, "", c.domain, c.path, -86400)
|
||||
}
|
||||
|
||||
// 标记该cookie在对应的域名和路径失效
|
||||
// 删除cookie的重点是需要通知浏览器客户端cookie已过期
|
||||
func (c *Cookie) Remove(key, domain, path string) {
|
||||
func (c *Cookie) RemoveCookie(key, domain, path string) {
|
||||
c.SetCookie(key, "", domain, path, -86400)
|
||||
}
|
||||
|
||||
|
||||
@ -64,15 +64,15 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
faddr : nil,
|
||||
}
|
||||
// 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI
|
||||
// 例如: pattern为/user, 那么会同时注册/user及/user/index,
|
||||
// 这里处理新增/user路由绑定
|
||||
if strings.EqualFold(mname, "Index") {
|
||||
p := key
|
||||
if strings.EqualFold(p[len(p) - 6:], "/index") {
|
||||
p = p[0 : len(p) - 6]
|
||||
if len(p) == 0 {
|
||||
p = "/"
|
||||
}
|
||||
p := gstr.PosR(key, "/index")
|
||||
k := key[0 : p] + key[p + 6 : ]
|
||||
if len(k) == 0 {
|
||||
k = "/"
|
||||
}
|
||||
m[p] = &handlerItem {
|
||||
m[k] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
rtype : gROUTE_REGISTER_CONTROLLER,
|
||||
ctype : v.Elem().Type(),
|
||||
|
||||
@ -74,14 +74,12 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
}
|
||||
// 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI
|
||||
if strings.EqualFold(mname, "Index") {
|
||||
p := key
|
||||
if strings.EqualFold(p[len(p) - 6:], "/index") {
|
||||
p = p[0 : len(p) - 6]
|
||||
if len(p) == 0 {
|
||||
p = "/"
|
||||
}
|
||||
p := gstr.PosR(key, "/index")
|
||||
k := key[0 : p] + key[p + 6 : ]
|
||||
if len(k) == 0 {
|
||||
k = "/"
|
||||
}
|
||||
m[p] = &handlerItem {
|
||||
m[k] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
rtype : gROUTE_REGISTER_OBJECT,
|
||||
ctype : nil,
|
||||
|
||||
@ -52,7 +52,7 @@ func (s *Session) init() {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取SessionId
|
||||
// 获取/创建SessionId
|
||||
func (s *Session) Id() string {
|
||||
s.init()
|
||||
return s.id
|
||||
@ -60,8 +60,11 @@ func (s *Session) Id() string {
|
||||
|
||||
// 获取当前session所有数据
|
||||
func (s *Session) Data() map[string]interface{} {
|
||||
s.init()
|
||||
return s.data.Map()
|
||||
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
|
||||
s.init()
|
||||
return s.data.Map()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置session
|
||||
@ -84,70 +87,158 @@ func (s *Session) BatchSet(m map[string]interface{}) {
|
||||
|
||||
// 判断键名是否存在
|
||||
func (s *Session) Contains (key string) bool {
|
||||
s.init()
|
||||
return s.data.Contains(key)
|
||||
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
|
||||
s.init()
|
||||
return s.data.Contains(key)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取SESSION
|
||||
func (s *Session) Get (key string) interface{} {
|
||||
s.init()
|
||||
return s.data.Get(key)
|
||||
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
|
||||
s.init()
|
||||
return s.data.Get(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取SESSION,建议都用该方法获取参数
|
||||
func (s *Session) GetVar(key string) gvar.VarRead {
|
||||
s.init()
|
||||
return gvar.NewRead(s.data.Get(key), true)
|
||||
}
|
||||
|
||||
|
||||
func (s *Session) GetString (key string) string { return gconv.String(s.Get(key)) }
|
||||
func (s *Session) GetBool(key string) bool { return gconv.Bool(s.Get(key)) }
|
||||
|
||||
func (s *Session) GetInt(key string) int { return gconv.Int(s.Get(key)) }
|
||||
func (s *Session) GetInt8(key string) int8 { return gconv.Int8(s.Get(key)) }
|
||||
func (s *Session) GetInt16(key string) int16 { return gconv.Int16(s.Get(key)) }
|
||||
func (s *Session) GetInt32(key string) int32 { return gconv.Int32(s.Get(key)) }
|
||||
func (s *Session) GetInt64(key string) int64 { return gconv.Int64(s.Get(key)) }
|
||||
|
||||
func (s *Session) GetUint(key string) uint { return gconv.Uint(s.Get(key)) }
|
||||
func (s *Session) GetUint8(key string) uint8 { return gconv.Uint8(s.Get(key)) }
|
||||
func (s *Session) GetUint16(key string) uint16 { return gconv.Uint16(s.Get(key)) }
|
||||
func (s *Session) GetUint32(key string) uint32 { return gconv.Uint32(s.Get(key)) }
|
||||
func (s *Session) GetUint64(key string) uint64 { return gconv.Uint64(s.Get(key)) }
|
||||
|
||||
func (s *Session) GetFloat32 (key string) float32 { return gconv.Float32(s.Get(key)) }
|
||||
func (s *Session) GetFloat64 (key string) float64 { return gconv.Float64(s.Get(key)) }
|
||||
|
||||
func (s *Session) GetBytes(key string) []byte { return gconv.Bytes(s.Get(key)) }
|
||||
func (s *Session) GetInts(key string) []int { return gconv.Ints(s.Get(key)) }
|
||||
func (s *Session) GetFloats(key string) []float64 { return gconv.Floats(s.Get(key)) }
|
||||
func (s *Session) GetStrings(key string) []string { return gconv.Strings(s.Get(key)) }
|
||||
func (s *Session) GetInterfaces(key string) []interface{} { return gconv.Interfaces(s.Get(key)) }
|
||||
|
||||
func (s *Session) GetTime(key string, format...string) time.Time { return gconv.Time(s.Get(key), format...) }
|
||||
func (s *Session) GetTimeDuration(key string) time.Duration { return gconv.TimeDuration(s.Get(key)) }
|
||||
|
||||
// 将变量转换为对象,注意 objPointer 参数必须为struct指针
|
||||
func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error {
|
||||
return gconv.Struct(s.Get(key), objPointer, attrMapping...)
|
||||
return gvar.NewRead(s.Get(key), true)
|
||||
}
|
||||
|
||||
// 删除session
|
||||
func (s *Session) Remove(key string) {
|
||||
s.init()
|
||||
s.data.Remove(key)
|
||||
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
|
||||
s.init()
|
||||
s.data.Remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空session
|
||||
func (s *Session) Clear() {
|
||||
s.init()
|
||||
s.data.Clear()
|
||||
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
|
||||
s.init()
|
||||
s.data.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除)
|
||||
func (s *Session) UpdateExpire() {
|
||||
if len(s.id) > 0 {
|
||||
if len(s.id) > 0 && s.data.Size() > 0 {
|
||||
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetString(key string) string {
|
||||
return gconv.String(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetBool(key string) bool {
|
||||
return gconv.Bool(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInt(key string) int {
|
||||
return gconv.Int(s.Get(key)) }
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInt8(key string) int8 {
|
||||
return gconv.Int8(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInt16(key string) int16 {
|
||||
return gconv.Int16(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInt32(key string) int32 {
|
||||
return gconv.Int32(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInt64(key string) int64 {
|
||||
return gconv.Int64(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetUint(key string) uint {
|
||||
return gconv.Uint(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetUint8(key string) uint8 {
|
||||
return gconv.Uint8(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetUint16(key string) uint16 {
|
||||
return gconv.Uint16(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetUint32(key string) uint32 {
|
||||
return gconv.Uint32(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetUint64(key string) uint64 {
|
||||
return gconv.Uint64(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetFloat32(key string) float32 {
|
||||
return gconv.Float32(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetFloat64(key string) float64 {
|
||||
return gconv.Float64(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetBytes(key string) []byte {
|
||||
return gconv.Bytes(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInts(key string) []int {
|
||||
return gconv.Ints(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetFloats(key string) []float64 {
|
||||
return gconv.Floats(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetStrings(key string) []string {
|
||||
return gconv.Strings(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetInterfaces(key string) []interface{} {
|
||||
return gconv.Interfaces(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetTime(key string, format...string) time.Time {
|
||||
return gconv.Time(s.Get(key), format...)
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
func (s *Session) GetTimeDuration(key string) time.Duration {
|
||||
return gconv.TimeDuration(s.Get(key))
|
||||
}
|
||||
|
||||
// Deprecated, use GetVar instead.
|
||||
// (已废弃, 请使用GetVar) 将变量转换为对象,注意 objPointer 参数必须为struct指针
|
||||
func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error {
|
||||
return gconv.Struct(s.Get(key), objPointer, attrMapping...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
62
g/net/ghttp/ghttp_unit_cookie_test.go
Normal file
62
g/net/ghttp/ghttp_unit_cookie_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// COOKIE测试
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Cookie(t *testing.T) {
|
||||
s := g.Server(gtime.Nanosecond())
|
||||
s.BindHandler("/set", func(r *ghttp.Request){
|
||||
r.Cookie.Set(r.Get("k"), r.Get("v"))
|
||||
})
|
||||
s.BindHandler("/get", func(r *ghttp.Request){
|
||||
//fmt.Println(r.Cookie.Map())
|
||||
r.Response.Write(r.Cookie.Get(r.Get("k")))
|
||||
})
|
||||
s.BindHandler("/remove", func(r *ghttp.Request){
|
||||
r.Cookie.Remove(r.Get("k"))
|
||||
})
|
||||
s.SetPort(8500)
|
||||
s.SetDumpRouteMap(false)
|
||||
go s.Run()
|
||||
defer func() {
|
||||
s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
}()
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix("http://127.0.0.1:8500")
|
||||
r1, e1 := client.Get("/set?k=key1&v=100")
|
||||
if r1 != nil {
|
||||
defer r1.Close()
|
||||
}
|
||||
gtest.Assert(e1, nil)
|
||||
gtest.Assert(r1.ReadAllString(), "")
|
||||
|
||||
gtest.Assert(client.GetContent("/set?k=key2&v=200"), "")
|
||||
|
||||
gtest.Assert(client.GetContent("/get?k=key1"), "100")
|
||||
gtest.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
gtest.Assert(client.GetContent("/get?k=key3"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key1"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key3"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key4"), "")
|
||||
gtest.Assert(client.GetContent("/get?k=key1"), "")
|
||||
gtest.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
})
|
||||
}
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Params(t *testing.T) {
|
||||
func Test_Params_Basic(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
@ -20,6 +20,10 @@ import (
|
||||
// 执行对象
|
||||
type Object struct {}
|
||||
|
||||
func (o *Object) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Object Index")
|
||||
}
|
||||
|
||||
func (o *Object) Show(r *ghttp.Request) {
|
||||
r.Response.Write("Object Show")
|
||||
}
|
||||
@ -33,6 +37,10 @@ type Controller struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) Index() {
|
||||
c.Response.Write("Controller Index")
|
||||
}
|
||||
|
||||
func (c *Controller) Show() {
|
||||
c.Response.Write("Controller Show")
|
||||
}
|
||||
@ -72,11 +80,17 @@ func Test_Router_Group1(t *testing.T) {
|
||||
|
||||
gtest.Assert(client.GetContent ("/api/handler"), "Handler")
|
||||
|
||||
gtest.Assert(client.GetContent ("/api/ctl"), "Controller Index")
|
||||
gtest.Assert(client.GetContent ("/api/ctl/"), "Controller Index")
|
||||
gtest.Assert(client.GetContent ("/api/ctl/index"), "Controller Index")
|
||||
gtest.Assert(client.GetContent ("/api/ctl/my-show"), "Controller Show")
|
||||
gtest.Assert(client.GetContent ("/api/ctl/post"), "Controller REST Post")
|
||||
gtest.Assert(client.GetContent ("/api/ctl/show"), "Controller Show")
|
||||
gtest.Assert(client.PostContent("/api/ctl/rest"), "Controller REST Post")
|
||||
|
||||
gtest.Assert(client.GetContent ("/api/obj"), "Object Index")
|
||||
gtest.Assert(client.GetContent ("/api/obj/"), "Object Index")
|
||||
gtest.Assert(client.GetContent ("/api/obj/index"), "Object Index")
|
||||
gtest.Assert(client.GetContent ("/api/obj/delete"), "Object REST Delete")
|
||||
gtest.Assert(client.GetContent ("/api/obj/my-show"), "Object Show")
|
||||
gtest.Assert(client.GetContent ("/api/obj/show"), "Object Show")
|
||||
66
g/net/ghttp/ghttp_unit_session_test.go
Normal file
66
g/net/ghttp/ghttp_unit_session_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// SESSION测试
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Session(t *testing.T) {
|
||||
s := g.Server(gtime.Nanosecond())
|
||||
s.BindHandler("/set", func(r *ghttp.Request){
|
||||
r.Session.Set(r.Get("k"), r.Get("v"))
|
||||
})
|
||||
s.BindHandler("/get", func(r *ghttp.Request){
|
||||
r.Response.Write(r.Session.Get(r.Get("k")))
|
||||
})
|
||||
s.BindHandler("/remove", func(r *ghttp.Request){
|
||||
r.Session.Remove(r.Get("k"))
|
||||
})
|
||||
s.BindHandler("/clear", func(r *ghttp.Request){
|
||||
r.Session.Clear()
|
||||
})
|
||||
s.SetPort(8600)
|
||||
s.SetDumpRouteMap(false)
|
||||
go s.Run()
|
||||
defer func() {
|
||||
s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
}()
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix("http://127.0.0.1:8600")
|
||||
r1, e1 := client.Get("/set?k=key1&v=100")
|
||||
if r1 != nil {
|
||||
defer r1.Close()
|
||||
}
|
||||
gtest.Assert(e1, nil)
|
||||
gtest.Assert(r1.ReadAllString(), "")
|
||||
|
||||
gtest.Assert(client.GetContent("/set?k=key2&v=200"), "")
|
||||
|
||||
gtest.Assert(client.GetContent("/get?k=key1"), "100")
|
||||
gtest.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
gtest.Assert(client.GetContent("/get?k=key3"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key1"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key3"), "")
|
||||
gtest.Assert(client.GetContent("/remove?k=key4"), "")
|
||||
gtest.Assert(client.GetContent("/get?k=key1"), "")
|
||||
gtest.Assert(client.GetContent("/get?k=key2"), "200")
|
||||
gtest.Assert(client.GetContent("/clear"), "")
|
||||
gtest.Assert(client.GetContent("/get?k=key2"), "")
|
||||
})
|
||||
}
|
||||
@ -63,13 +63,8 @@ func (c *Config) filePath(file...string) (path string) {
|
||||
}
|
||||
c.paths.RLockFunc(func(array []string) {
|
||||
for _, v := range array {
|
||||
//fmt.Println("search:", v, name)
|
||||
if path, _ = gspath.Search(v, name); path != "" {
|
||||
break
|
||||
} else {
|
||||
//if strings.EqualFold(v, "/Users/john/Temp/config") {
|
||||
// gutil.Dump(gspath.Get(v).AllPaths())
|
||||
//}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -94,6 +89,10 @@ func (c *Config) SetPath(path string) error {
|
||||
glog.Error(fmt.Sprintf(`[gcfg] SetPath failed: %s`, err.Error()))
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
if c.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
c.jsons.Clear()
|
||||
c.paths.Clear()
|
||||
c.paths.Append(realPath)
|
||||
@ -116,14 +115,29 @@ func (c *Config) AddPath(path string) error {
|
||||
glog.Error(fmt.Sprintf(`[gcfg] AddPath failed: %s`, err.Error()))
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
if c.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
c.paths.Append(realPath)
|
||||
glog.Debug("[gcfg] AddPath:", realPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取指定文件的绝对路径,默认获取默认的配置文件路径
|
||||
func (c *Config) GetFilePath(file...string) string {
|
||||
return c.filePath(file...)
|
||||
// 获取指定文件的绝对路径,默认获取默认的配置文件路径,当指定的配置文件不存在时,返回空字符串,并且不会报错。
|
||||
func (c *Config) GetFilePath(file...string) (path string) {
|
||||
name := c.name.Val()
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
}
|
||||
c.paths.RLockFunc(func(array []string) {
|
||||
for _, v := range array {
|
||||
if path, _ = gspath.Search(v, name); path != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 设置配置管理对象的默认文件名称
|
||||
|
||||
@ -130,7 +130,9 @@ func (p *Pool) File() (*File, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !p.inited.Val() || p.inited.Set(true) == false {
|
||||
// !p.inited.Val() 使用原子读取操作判断,保证该操作判断的效率;
|
||||
// p.inited.Set(true) == false 使用原子写入操作,保证该操作的原子性;
|
||||
if !p.inited.Val() && p.inited.Set(true) == false {
|
||||
gfsnotify.Add(f.path, func(event *gfsnotify.Event) {
|
||||
// 如果文件被删除或者重命名,立即重建指针池
|
||||
if event.IsRemove() || event.IsRename() {
|
||||
|
||||
@ -115,6 +115,10 @@ func (view *View) SetPath(path string) error {
|
||||
glog.Error(fmt.Sprintf(`[gview] SetPath failed: %s`, err.Error()))
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
if view.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
view.paths.Clear()
|
||||
view.paths.Append(realPath)
|
||||
glog.Debug("[gview] SetPath:", realPath)
|
||||
@ -132,6 +136,10 @@ func (view *View) AddPath(path string) error {
|
||||
glog.Error(fmt.Sprintf(`[gview] AddPath failed: %s`, err.Error()))
|
||||
return err
|
||||
}
|
||||
// 重复判断
|
||||
if view.paths.Search(realPath) != -1 {
|
||||
return nil
|
||||
}
|
||||
view.paths.Append(realPath)
|
||||
glog.Debug("[gview] AddPath:", realPath)
|
||||
return nil
|
||||
|
||||
@ -9,6 +9,7 @@ package gstr
|
||||
import "strings"
|
||||
|
||||
// Find the position of the first occurrence of a substring in a string.
|
||||
// It returns -1, if none found.
|
||||
//
|
||||
// 返回 needle 在 haystack 中首次出现的数字位置,找不到返回-1。
|
||||
func Pos(haystack, needle string, startOffset...int) int {
|
||||
@ -32,6 +33,7 @@ func Pos(haystack, needle string, startOffset...int) int {
|
||||
}
|
||||
|
||||
// Find the position of the first occurrence of a case-insensitive substring in a string.
|
||||
// It returns -1, if none found.
|
||||
//
|
||||
// 返回在字符串 haystack 中 needle 首次出现的数字位置(不区分大小写),找不到返回-1。
|
||||
func PosI(haystack, needle string, startOffset...int) int {
|
||||
@ -56,6 +58,7 @@ func PosI(haystack, needle string, startOffset...int) int {
|
||||
}
|
||||
|
||||
// Find the position of the last occurrence of a substring in a string.
|
||||
// It returns -1, if none found.
|
||||
//
|
||||
// 查找指定字符串在目标字符串中最后一次出现的位置,找不到返回-1。
|
||||
func PosR(haystack, needle string, startOffset...int) int {
|
||||
@ -81,6 +84,7 @@ func PosR(haystack, needle string, startOffset...int) int {
|
||||
}
|
||||
|
||||
// Find the position of the last occurrence of a case-insensitive substring in a string.
|
||||
// It returns -1, if none found.
|
||||
//
|
||||
// 以不区分大小写的方式查找指定字符串在目标字符串中最后一次出现的位置,找不到返回-1。
|
||||
func PosRI(haystack, needle string, startOffset...int) int {
|
||||
|
||||
@ -99,10 +99,13 @@ func Map(i interface{}, noTagCheck...bool) map[string]interface{} {
|
||||
rt := rv.Type()
|
||||
name := ""
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
// 检查json tag
|
||||
// 检查tag, 支持gconv, json标签, 优先使用gconv
|
||||
if len(noTagCheck) == 0 || !noTagCheck[0] {
|
||||
if name = rt.Field(i).Tag.Get("json"); name == "" {
|
||||
name = rt.Field(i).Name
|
||||
tag := rt.Field(i).Tag
|
||||
if name = tag.Get("gconv"); name == "" {
|
||||
if name = tag.Get("json"); name == "" {
|
||||
name = rt.Field(i).Name
|
||||
}
|
||||
}
|
||||
}
|
||||
m[name] = rv.Field(i).Interface()
|
||||
|
||||
23
geg/net/ghttp/server/template/layout/main.go
Normal file
23
geg/net/ghttp/server/template/layout/main.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/main1", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"mainTpl" : "main/main1.html",
|
||||
})
|
||||
})
|
||||
s.BindHandler("/main2", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"mainTpl" : "main/main2.html",
|
||||
})
|
||||
})
|
||||
g.View().SetPath("template")
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<h1>FOOTER</h1>
|
||||
@ -0,0 +1 @@
|
||||
<h1>HEADER</h1>
|
||||
@ -0,0 +1,3 @@
|
||||
{{include "header.html" .}}
|
||||
{{include .mainTpl .}}
|
||||
{{include "footer.html" .}}
|
||||
@ -0,0 +1 @@
|
||||
<h1>MAIN1</h1>
|
||||
@ -0,0 +1 @@
|
||||
<h1>MAIN2</h1>
|
||||
@ -1,5 +1,5 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.5.4"
|
||||
const VERSION = "v1.5.8"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user