mirror of
https://gitee.com/johng/gf
synced 2026-06-10 03:23:59 +08:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9515d7126 | |||
| b56679a97c | |||
| d1b123964a | |||
| 991f7c4958 | |||
| 770619c39e | |||
| 4ad5450b80 | |||
| 49ce7fe885 | |||
| e66f63262b | |||
| 94bd5da68a | |||
| 3eee95caf2 | |||
| 5c638c630a | |||
| a6ec9d7a1c | |||
| 374c70c0e3 | |||
| 40771066d4 | |||
| 22a7ef43ce | |||
| 05f22d1cee | |||
| ebf56a86ab | |||
| 2ba59e8943 | |||
| 83be1de04c | |||
| c8251ed82f | |||
| 2335ea0c4d | |||
| 5d874e9063 | |||
| e352b07055 | |||
| fdea242b50 | |||
| 72ecf2d2af | |||
| 0f854e46d8 | |||
| 4564f38e1a | |||
| 7e06bf6705 | |||
| d780cf64c2 |
@ -15,18 +15,21 @@ env:
|
||||
services:
|
||||
- mysql
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
- local
|
||||
|
||||
before_install:
|
||||
- pwd
|
||||
|
||||
install:
|
||||
- pwd
|
||||
- cat /etc/hosts
|
||||
|
||||
script:
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
@ -4,17 +4,16 @@
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://goframe.org)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
|
||||
<!--
|
||||
[](https://codecov.io/gh/gogf/gf)
|
||||
[](https://www.codetriage.com/gogf/gf)
|
||||
-->
|
||||
GoFrame is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: cache, logging, containers, timer, validator, database orm, etc. Supporting web server integrated with router, cookie, session, logger, configure, template, https, hooks, rewrites and many more features.
|
||||
-->
|
||||
|
||||
`GF(GoFrame)` is a modular, loose-coupled and production-ready application development framework written in Go. Providing a series of core components and dozens of practical modules, such as: cache, logging, array/queue/set/map, timer/timing tasks, file/memory lock, object pool, validator, database ORM, etc. Supporting web server with graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing, rewrite rules and many more features.
|
||||
`GF(GoFrame)` is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites and many more features.
|
||||
|
||||
# Installation
|
||||
```
|
||||
|
||||
@ -4,16 +4,11 @@
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://goframe.org)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
|
||||
<!--
|
||||
[](https://codecov.io/gh/gogf/gf)
|
||||
[](https://www.codetriage.com/gogf/gf)
|
||||
-->
|
||||
|
||||
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
|
||||
并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
|
||||
|
||||
4
TODO.MD
4
TODO.MD
@ -55,6 +55,10 @@
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6,3 +6,15 @@
|
||||
|
||||
package garray
|
||||
|
||||
|
||||
type apiSliceInterface interface {
|
||||
Slice() []interface{}
|
||||
}
|
||||
|
||||
type apiSliceInt interface {
|
||||
Slice() []int
|
||||
}
|
||||
|
||||
type apiSliceString interface {
|
||||
Slice() []string
|
||||
}
|
||||
@ -7,12 +7,12 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IntArray struct {
|
||||
@ -53,6 +53,20 @@ func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
@ -233,13 +247,31 @@ func (a *IntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项。
|
||||
@ -425,17 +457,22 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *IntArray) Merge(array *IntArray) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -538,11 +575,19 @@ func (a *IntArray) SubSlice(offset, size int) []int {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *IntArray) Rand(size int) []int {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *IntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *IntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -588,5 +633,12 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -7,12 +7,13 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Array struct {
|
||||
@ -30,8 +31,6 @@ func New(unsafe...bool) *Array {
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New方法。
|
||||
func NewArray(unsafe...bool) *Array {
|
||||
return NewArraySize(0, 0, unsafe...)
|
||||
}
|
||||
@ -48,6 +47,16 @@ func NewArraySize(size int, cap int, unsafe...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// See NewArrayFrom.
|
||||
func NewFrom(array []interface{}, unsafe...bool) *Array {
|
||||
return NewArrayFrom(array, unsafe...)
|
||||
}
|
||||
|
||||
// See NewArrayFromCopy.
|
||||
func NewFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
return NewArrayFromCopy(array, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
@ -60,6 +69,20 @@ func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
@ -196,13 +219,31 @@ func (a *Array) PushRight(value...interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
@ -408,17 +449,22 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *Array) Merge(array *Array) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -520,11 +566,19 @@ func (a *Array) SubSlice(offset, size int) []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *Array) Rand(size int) []interface{} {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *Array) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *Array) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -570,7 +624,14 @@ func (a *Array) Reverse() *Array {
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Counts all the values of an array.
|
||||
@ -584,4 +645,13 @@ func (a *Array) CountValues() map[interface{}]int {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
//
|
||||
// 将当前数组转换为字符串返回。
|
||||
func (a *Array) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
@ -53,6 +54,20 @@ func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &StringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
@ -233,13 +248,31 @@ func (a *StringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
@ -423,17 +456,22 @@ func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *StringArray) Merge(array *StringArray) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *StringArray) Merge(array interface{}) *StringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -537,11 +575,19 @@ func (a *StringArray) SubSlice(offset, size int) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *StringArray) Rand(size int) []string {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *StringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *StringArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -587,6 +633,13 @@ func (a *StringArray) Reverse() *StringArray {
|
||||
func (a *StringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(a.array, glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
|
||||
@ -7,13 +7,13 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认按照从小到大进行排序
|
||||
@ -67,6 +67,20 @@ func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedIntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
@ -172,13 +186,31 @@ func (a *SortedIntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedIntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
@ -399,18 +431,22 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedIntArray) Merge(array *SortedIntArray) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -462,11 +498,19 @@ func (a *SortedIntArray) SubSlice(offset, size int) []int {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedIntArray) Rand(size int) []int {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedIntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedIntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -488,5 +532,12 @@ func (a *SortedIntArray) Rand(size int) []int {
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -7,13 +7,13 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认按照从小到大进行排序
|
||||
@ -69,6 +69,20 @@ func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
@ -178,13 +192,31 @@ func (a *SortedArray) PopRight() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedArray) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
@ -406,20 +438,22 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedArray) Merge(array *SortedArray) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
@ -471,11 +505,19 @@ func (a *SortedArray) SubSlice(offset, size int) []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedArray) Rand(size int) []interface{} {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedArray) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedArray) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -497,5 +539,12 @@ func (a *SortedArray) Rand(size int) []interface{} {
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
@ -61,6 +62,20 @@ func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray
|
||||
return a
|
||||
}
|
||||
|
||||
// Create an array from a copy of given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice拷贝创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedStringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
@ -166,13 +181,31 @@ func (a *SortedStringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedStringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedStringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
@ -393,18 +426,22 @@ func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
// Merge two arrays. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedStringArray) Merge(array *SortedStringArray) *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Strings(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
@ -456,11 +493,19 @@ func (a *SortedStringArray) SubSlice(offset, size int) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedStringArray) Rand(size int) []string {
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedStringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedStringArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
@ -482,5 +527,12 @@ func (a *SortedStringArray) Rand(size int) []string {
|
||||
func (a *SortedStringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(a.array, glue)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
111
g/container/garray/garray_z_example_test.go
Normal file
111
g/container/garray/garray_z_example_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// 创建普通的数组,默认并发安全(带锁)
|
||||
a := garray.New()
|
||||
|
||||
// 添加数据项
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// 获取当前数组长度
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// 获取当前数据项列表
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 获取指定索引项
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// 查找指定数据项是否存在
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// 在指定索引前插入数据项
|
||||
a.InsertAfter(9, 11)
|
||||
// 在指定索引后插入数据项
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 修改指定索引的数据项
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 搜索数据项,返回搜索到的索引位置
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// 删除指定索引的数据项
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 清空数组
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
|
||||
// 随机返回两个数据项(不删除)
|
||||
fmt.Println(array.Rands(2))
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 9
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_merge() {
|
||||
array1 := garray.NewFrom([]interface{}{1,2})
|
||||
array2 := garray.NewFrom([]interface{}{3,4})
|
||||
slice1 := []interface{}{5,6}
|
||||
slice2 := []int{7,8}
|
||||
slice3 := []string{"9","0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
@ -161,9 +161,18 @@ func TestIntArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), 2)
|
||||
gtest.Assert(len(array1.Rand(10)), 7)
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
gtest.Assert(len(array1.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
gtest.AssertIN(array1.Rand(), a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), a1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -80,6 +80,14 @@ func TestArray_PushAndPop(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []interface{}{0,1,2,3,4,5,6}
|
||||
@ -165,9 +173,9 @@ func TestArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), 2)
|
||||
gtest.Assert(len(array1.Rand(10)), 7)
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
gtest.Assert(len(array1.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -162,9 +162,17 @@ func TestStringArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), "2")
|
||||
gtest.Assert(len(array1.Rand(10)), "7")
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
gtest.Assert(len(array1.Rands(2)), "2")
|
||||
gtest.Assert(len(array1.Rands(10)), "7")
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), a1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -15,9 +15,8 @@
|
||||
package gqueue
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 1、这是一个先进先出的队列(chan <-- list);
|
||||
@ -28,9 +27,8 @@ import (
|
||||
//
|
||||
// 4、由于功能主体是chan,那么操作仍然像chan那样具有阻塞效果;
|
||||
type Queue struct {
|
||||
mu sync.Mutex // 底层链表写锁
|
||||
limit int // 队列限制大小
|
||||
list *list.List // 底层数据链表
|
||||
list *glist.List // 底层数据链表
|
||||
events chan struct{} // 写入事件通知
|
||||
closed chan struct{} // 队列关闭通知
|
||||
C chan interface{} // 队列数据读取
|
||||
@ -50,7 +48,7 @@ func New(limit...int) *Queue {
|
||||
q.limit = limit[0]
|
||||
q.C = make(chan interface{}, limit[0])
|
||||
} else {
|
||||
q.list = list.New()
|
||||
q.list = glist.New()
|
||||
q.events = make(chan struct{}, math.MaxInt32)
|
||||
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
|
||||
go q.startAsyncLoop()
|
||||
@ -68,7 +66,6 @@ func (q *Queue) startAsyncLoop() {
|
||||
for {
|
||||
if length := q.list.Len(); length > 0 {
|
||||
array := make([]interface{}, length)
|
||||
q.mu.Lock()
|
||||
for i := 0; i < length; i++ {
|
||||
if e := q.list.Front(); e != nil {
|
||||
array[i] = q.list.Remove(e)
|
||||
@ -76,7 +73,6 @@ func (q *Queue) startAsyncLoop() {
|
||||
break
|
||||
}
|
||||
}
|
||||
q.mu.Unlock()
|
||||
for _, v := range array {
|
||||
q.C <- v
|
||||
}
|
||||
@ -93,9 +89,7 @@ func (q *Queue) Push(v interface{}) {
|
||||
if q.limit > 0 {
|
||||
q.C <- v
|
||||
} else {
|
||||
q.mu.Lock()
|
||||
q.list.PushBack(v)
|
||||
q.mu.Unlock()
|
||||
q.events <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,9 @@ type DB interface {
|
||||
GetOne(query string, args ...interface{}) (Record, error)
|
||||
GetValue(query string, args ...interface{}) (Value, error)
|
||||
GetCount(query string, args ...interface{}) (int, error)
|
||||
GetStruct(obj interface{}, query string, args ...interface{}) error
|
||||
GetStruct(objPointer interface{}, query string, args ...interface{}) error
|
||||
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
|
||||
GetScan(objPointer interface{}, query string, args ...interface{}) error
|
||||
|
||||
// 创建底层数据库master/slave链接对象
|
||||
Master() (*sql.DB, error)
|
||||
|
||||
@ -165,13 +165,44 @@ func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询结果记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(obj interface{}, query string, args ...interface{}) error {
|
||||
// 数据库查询,查询单条记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(objPointer interface{}, query string, args ...interface{}) error {
|
||||
one, err := bs.GetOne(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(obj)
|
||||
return one.ToStruct(objPointer)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (bs *dbBase) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
all, err := bs.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return bs.db.GetStructs(objPointer, query, args ...)
|
||||
case reflect.Struct:
|
||||
return bs.db.GetStruct(objPointer, query, args ...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
@ -38,8 +39,21 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
buffer.WriteString(k + "=?")
|
||||
newArgs = append(newArgs, v)
|
||||
// 支持slice键值/属性,这个时候作为IN查询
|
||||
switch reflect.ValueOf(v).Kind() {
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
buffer.WriteString(k + " IN(?)")
|
||||
default:
|
||||
if gstr.Pos(k, "<") == -1 && gstr.Pos(k, ">") == -1 && gstr.Pos(k, "=") == -1 {
|
||||
buffer.WriteString(k + "=?")
|
||||
} else {
|
||||
buffer.WriteString(k + "?")
|
||||
}
|
||||
}
|
||||
// 当给定的Where参数为map/struct时,args参数必定为空,
|
||||
// 考虑到后续还会对args做处理,特别是判断slice类型,这里直接给args赋值。
|
||||
args = append(args, v)
|
||||
}
|
||||
newWhere = buffer.String()
|
||||
default:
|
||||
@ -66,6 +80,7 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// counter用于匹配该参数的位置(与index对应)
|
||||
counter := 0
|
||||
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
|
||||
counter++
|
||||
|
||||
@ -35,6 +35,7 @@ type Model struct {
|
||||
cacheEnabled bool // 当前SQL操作是否开启查询缓存功能
|
||||
cacheTime int // 查询缓存时间
|
||||
cacheName string // 查询缓存名称
|
||||
alterable bool // 当前模型是否运行可修改模式(默认情况下链式操作不会修改当前模型,而是创建新的模型返回)
|
||||
}
|
||||
|
||||
// 链式操作,数据表字段,可支持多个表,以半角逗号连接
|
||||
@ -79,47 +80,69 @@ func (md *Model) Clone() *Model {
|
||||
return newModel
|
||||
}
|
||||
|
||||
// 标识当前对象可被修改。
|
||||
// 1. 默认情况下,模型对象的对象属性无法被修改,
|
||||
// 每一次链式操作都是克隆一个新的模型对象,这样所有的操作都不会污染模型对象。
|
||||
// 但是链式操作如果需要分开执行,那么需要将新的克隆对象赋值给旧的模型对象继续操作。
|
||||
// 2. 当标识模型对象为可修改,那么在当前模型对象的所有链式操作均会影响下一次的链式操作,
|
||||
// 即使是链式操作分开执行。
|
||||
// 3. 大部分ORM框架默认模型对象是可修改的,但是GF框架的ORM提供给开发者更灵活,更安全的链式操作选项。
|
||||
func (md *Model) Alterable() *Model {
|
||||
md.alterable = true
|
||||
return md
|
||||
}
|
||||
|
||||
// 返回操作的模型对象,可能是当前对象,也可能是新的克隆对象,根据alterable决定。
|
||||
func (md *Model) getModel() *Model {
|
||||
if md.alterable {
|
||||
return md
|
||||
} else {
|
||||
return md.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
// 链式操作,左联表
|
||||
func (md *Model) LeftJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,右联表
|
||||
func (md *Model) RightJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,内联表
|
||||
func (md *Model) InnerJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,查询字段
|
||||
func (md *Model) Fields(fields string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.fields = fields
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,过滤字段
|
||||
func (md *Model) Filter() (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.filter = true
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,condition,支持string & gdb.Map
|
||||
// 链式操作,condition,支持string & gdb.Map.
|
||||
// 注意,多个Where调用时,会相互覆盖,只有最后一个Where语句生效。
|
||||
func (md *Model) Where(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where = newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
model.whereArgs = newArgs
|
||||
// 支持 Where("uid", 1)这种格式
|
||||
if len(args) == 1 && strings.Index(model.where , "?") < 0 {
|
||||
model.where += "=?"
|
||||
@ -129,7 +152,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,添加AND条件到Where中
|
||||
func (md *Model) And(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where += " AND " + newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
@ -138,7 +161,7 @@ func (md *Model) And(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,添加OR条件到Where中
|
||||
func (md *Model) Or(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where += " OR " + newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
@ -147,21 +170,21 @@ func (md *Model) Or(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,group by
|
||||
func (md *Model) GroupBy(groupBy string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.groupBy = groupBy
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,order by
|
||||
func (md *Model) OrderBy(orderBy string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.orderBy = orderBy
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,limit
|
||||
func (md *Model) Limit(start int, limit int) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.start = start
|
||||
model.limit = limit
|
||||
return model
|
||||
@ -170,7 +193,7 @@ func (md *Model) Limit(start int, limit int) (*Model) {
|
||||
// 链式操作,翻页
|
||||
// @author ymrjqyy
|
||||
func (md *Model) ForPage(page, limit int) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.start = (page - 1) * limit
|
||||
model.limit = limit
|
||||
return model
|
||||
@ -178,7 +201,7 @@ func (md *Model) ForPage(page, limit int) (*Model) {
|
||||
|
||||
// 设置批处理的大小
|
||||
func (md *Model) Batch(batch int) *Model {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.batch = batch
|
||||
return model
|
||||
}
|
||||
@ -188,7 +211,7 @@ func (md *Model) Batch(batch int) *Model {
|
||||
// name表示自定义的缓存名称,便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,清理缓存时可以给定清理的缓存名称进行精准清理。
|
||||
func (md *Model) Cache(time int, name ... string) *Model {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.cacheTime = time
|
||||
if len(name) > 0 {
|
||||
model.cacheName = name[0]
|
||||
@ -203,7 +226,7 @@ func (md *Model) Cache(time int, name ... string) *Model {
|
||||
// 链式操作,操作数据项,参数data类型支持 string/map/slice/struct/*struct ,
|
||||
// 也可以是:key,value,key,value,...。
|
||||
func (md *Model) Data(data ...interface{}) *Model {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
if len(data) > 1 {
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
@ -438,13 +461,44 @@ func (md *Model) Value() (Value, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 链式操作,查询单条记录,并自动转换为struct对象
|
||||
func (md *Model) Struct(obj interface{}) error {
|
||||
// 链式操作,查询单条记录,并自动转换为struct对象, 参数必须为对象的指针,不能为空指针。
|
||||
func (md *Model) Struct(objPointer interface{}) error {
|
||||
one, err := md.One()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(obj)
|
||||
return one.ToStruct(objPointer)
|
||||
}
|
||||
|
||||
// 链式操作,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (md *Model) Structs(objPointerSlice interface{}) error {
|
||||
r, err := md.All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (md *Model) Scan(objPointer interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return md.Structs(objPointer)
|
||||
case reflect.Struct:
|
||||
return md.Struct(objPointer)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 链式操作,查询数量,fields可以为空,也可以自定义查询字段,
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
2.不支持save/replace方法
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
|
||||
@ -8,8 +8,10 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 数据库事务对象
|
||||
@ -75,6 +77,37 @@ func (tx *TX) GetStruct(obj interface{}, query string, args ...interface{}) erro
|
||||
return one.ToStruct(obj)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (tx *TX) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
all, err := tx.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (tx *TX) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return tx.db.GetStructs(objPointer, query, args ...)
|
||||
case reflect.Struct:
|
||||
return tx.db.GetStruct(objPointer, query, args ...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
func (tx *TX) GetValue(query string, args ...interface{}) (Value, error) {
|
||||
one, err := tx.GetOne(query, args ...)
|
||||
|
||||
@ -33,10 +33,6 @@ func (r Record) ToMap() Map {
|
||||
}
|
||||
|
||||
// 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针
|
||||
func (r Record) ToStruct(obj interface{}) error {
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range r {
|
||||
m[k] = v.Val()
|
||||
}
|
||||
return gconv.Struct(m, obj)
|
||||
func (r Record) ToStruct(objPointer interface{}) error {
|
||||
return gconv.Struct(r.ToMap(), objPointer)
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 将结果集转换为JSON字符串
|
||||
@ -96,3 +98,30 @@ func (r Result) ToUintRecord(key string) map[uint]Record {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// 将结果列表转换为指定对象的slice。
|
||||
func (r Result) ToStructs(objPointerSlice interface{}) error {
|
||||
l := len(r)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
t := reflect.TypeOf(objPointerSlice)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind())
|
||||
}
|
||||
a := reflect.MakeSlice(t.Elem(), l, l)
|
||||
itemType := a.Index(0).Type()
|
||||
for i := 0; i < l; i++ {
|
||||
if itemType.Kind() == reflect.Ptr {
|
||||
e := reflect.New(itemType.Elem()).Elem()
|
||||
r[i].ToStruct(e)
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(itemType).Elem()
|
||||
r[i].ToStruct(e)
|
||||
a.Index(i).Set(e)
|
||||
}
|
||||
}
|
||||
reflect.ValueOf(objPointerSlice).Elem().Set(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -266,19 +266,159 @@ func TestDbBase_GetCount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDbBase_GetStruct(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_GetStructs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_GetScan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Delete(t *testing.T) {
|
||||
|
||||
@ -121,7 +121,7 @@ func TestModel_Replace(t *testing.T) {
|
||||
"passport" : "t11",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T11",
|
||||
"create_time" : gtime.Now().String(),
|
||||
"create_time" : "2018-10-10 00:01:10",
|
||||
}).Replace()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
@ -136,7 +136,7 @@ func TestModel_Save(t *testing.T) {
|
||||
"passport" : "t111",
|
||||
"password" : "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname" : "T111",
|
||||
"create_time" : gtime.Now().String(),
|
||||
"create_time" : "2018-10-10 00:01:10",
|
||||
}).Save()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
@ -168,13 +168,30 @@ func TestModel_Clone(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
gtest.Assert(record["id"].Int(), 3)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(count, 2)
|
||||
gtest.Assert(record["id"].Int(), 3)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
}
|
||||
|
||||
func TestModel_Alterable(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table("user").Alterable().Where("id IN(?)", g.Slice{1,3})
|
||||
count, err := md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
md.And("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_All(t *testing.T) {
|
||||
result, err := db.Table("user").All()
|
||||
if err != nil {
|
||||
@ -222,19 +239,164 @@ func TestModel_Select(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModel_Struct(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Struct(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Struct(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Struct(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Structs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table("user").OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table("user").OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Scan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Scan(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Scan(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table("user").OrderBy("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table("user").OrderBy("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_OrderBy(t *testing.T) {
|
||||
@ -255,8 +417,8 @@ func TestModel_GroupBy(t *testing.T) {
|
||||
gtest.Assert(result[0]["nickname"].String(), "T111")
|
||||
}
|
||||
|
||||
// where string
|
||||
func TestModel_WhereString(t *testing.T) {
|
||||
func TestModel_Where(t *testing.T) {
|
||||
// string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id=? and nickname=?", 3, "T3").One()
|
||||
if err != nil {
|
||||
@ -264,10 +426,7 @@ func TestModel_WhereString(t *testing.T) {
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
// where map
|
||||
func TestModel_WhereMap(t *testing.T) {
|
||||
// map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{"id" : 3, "nickname" : "T3"}).One()
|
||||
if err != nil {
|
||||
@ -275,10 +434,15 @@ func TestModel_WhereMap(t *testing.T) {
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
// where struct
|
||||
func TestModel_WhereStruct(t *testing.T) {
|
||||
// map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{"id>" : 1, "id<" : 3}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
// struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
@ -296,27 +460,53 @@ func TestModel_WhereStruct(t *testing.T) {
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
// where slice
|
||||
func TestModel_WhereSlice1(t *testing.T) {
|
||||
result, err := db.Table("user").Where("id IN(?)", g.Slice{1,3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
}
|
||||
|
||||
// where slice
|
||||
func TestModel_WhereSlice2(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 {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
// slice single
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id IN(?)", g.Slice{1, 3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
})
|
||||
// slice + string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("nickname=? AND id IN(?)", "T3", g.Slice{1,3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{
|
||||
"id" : g.Slice{1,3},
|
||||
"nickname" : "T3",
|
||||
}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Ids []int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
}
|
||||
result, err := db.Table("user").Where(User{
|
||||
Ids : []int{1, 3},
|
||||
Nickname : "T3",
|
||||
}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Delete(t *testing.T) {
|
||||
|
||||
@ -267,6 +267,29 @@ func TestTX_Save(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Update(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if result, err := db.Update("user", "create_time='2010-10-10 00:00:01'", "id=3"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value, err := db.Table("user").Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetAll(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
@ -331,26 +354,215 @@ func TestTX_GetCount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTX_GetStruct(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, "SELECT * FROM user WHERE id=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.NickName, "T11")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetStructs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetScan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_Delete(t *testing.T) {
|
||||
|
||||
31
g/g.go
31
g/g.go
@ -3,34 +3,51 @@
|
||||
// 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.
|
||||
// 常用数据类型以及对象封装
|
||||
|
||||
package g
|
||||
|
||||
import "github.com/gogf/gf/g/container/gvar"
|
||||
|
||||
// 框架动态变量,可以用该类型替代interface{}类型
|
||||
type Var = gvar.Var
|
||||
// Universal variable type, like generics.
|
||||
//
|
||||
// 动态变量类型,可以用该类型替代interface{}类型
|
||||
type Var = gvar.Var
|
||||
|
||||
// Frequently-used map type alias.
|
||||
//
|
||||
// 常用map数据结构(使用别名)
|
||||
type Map = map[string]interface{}
|
||||
type MapAnyAny = map[interface{}]interface{}
|
||||
type MapAnyStr = map[interface{}]string
|
||||
type MapAnyInt = map[interface{}]int
|
||||
type MapStrAny = map[string]interface{}
|
||||
type MapStrStr = map[string]string
|
||||
type MapStrInt = map[string]int
|
||||
type MapIntAny = map[int]interface{}
|
||||
type MapIntStr = map[int]string
|
||||
type MapIntInt = map[int]int
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
//
|
||||
// 常用list数据结构(使用别名)
|
||||
type List = []Map
|
||||
type ListAnyStr = []map[interface{}]string
|
||||
type ListAnyInt = []map[interface{}]int
|
||||
type ListStrAny = []map[string]interface{}
|
||||
type ListStrStr = []map[string]string
|
||||
type ListStrInt = []map[string]int
|
||||
type ListIntAny = []map[int]interface{}
|
||||
type ListIntStr = []map[int]string
|
||||
type ListIntInt = []map[int]int
|
||||
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
//
|
||||
// 常用slice数据结构(使用别名)
|
||||
type Slice = []interface{}
|
||||
type SliceAny = []interface{}
|
||||
type SliceStr = []string
|
||||
type SliceInt = []int
|
||||
type Array = Slice
|
||||
type ArrayStr = SliceStr
|
||||
type ArrayInt = SliceInt
|
||||
type Array = []interface{}
|
||||
type ArrayAny = []interface{}
|
||||
type ArrayStr = []string
|
||||
type ArrayInt = []int
|
||||
|
||||
@ -45,13 +45,10 @@ func (r *Response) Write(content ... interface{}) {
|
||||
return
|
||||
}
|
||||
for _, v := range content {
|
||||
switch v.(type) {
|
||||
case []byte:
|
||||
// 如果是二进制数据,那么返回二进制数据
|
||||
r.buffer.Write(gconv.Bytes(v))
|
||||
|
||||
switch value := v.(type) {
|
||||
case []byte: r.buffer.Write(value)
|
||||
case string: r.buffer.WriteString(value)
|
||||
default:
|
||||
// 否则一律按照可显示的字符串进行转换
|
||||
r.buffer.WriteString(gconv.String(v))
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,10 +13,10 @@ import (
|
||||
)
|
||||
|
||||
// 展示模板,可以给定模板参数,及临时的自定义模板函数
|
||||
func (r *Response) WriteTpl(tpl string, params map[string]interface{}, funcmap...map[string]interface{}) error {
|
||||
func (r *Response) WriteTpl(tpl string, params map[string]interface{}, funcMap...map[string]interface{}) error {
|
||||
fmap := make(gview.FuncMap)
|
||||
if len(funcmap) > 0 {
|
||||
fmap = funcmap[0]
|
||||
if len(funcMap) > 0 {
|
||||
fmap = funcMap[0]
|
||||
}
|
||||
if b, err := r.ParseTpl(tpl, params, fmap); err != nil {
|
||||
r.Write("Tpl Parsing Error: " + err.Error())
|
||||
@ -28,10 +28,10 @@ func (r *Response) WriteTpl(tpl string, params map[string]interface{}, funcmap..
|
||||
}
|
||||
|
||||
// 展示模板内容,可以给定模板参数,及临时的自定义模板函数
|
||||
func (r *Response) WriteTplContent(content string, params map[string]interface{}, funcmap...map[string]interface{}) error {
|
||||
func (r *Response) WriteTplContent(content string, params map[string]interface{}, funcMap...map[string]interface{}) error {
|
||||
fmap := make(gview.FuncMap)
|
||||
if len(funcmap) > 0 {
|
||||
fmap = funcmap[0]
|
||||
if len(funcMap) > 0 {
|
||||
fmap = funcMap[0]
|
||||
}
|
||||
if b, err := r.ParseTplContent(content, params, fmap); err != nil {
|
||||
r.Write("Tpl Parsing Error: " + err.Error())
|
||||
@ -43,19 +43,19 @@ func (r *Response) WriteTplContent(content string, params map[string]interface{}
|
||||
}
|
||||
|
||||
// 解析模板文件,并返回模板内容
|
||||
func (r *Response) ParseTpl(tpl string, params gview.Params, funcmap...map[string]interface{}) ([]byte, error) {
|
||||
func (r *Response) ParseTpl(tpl string, params gview.Params, funcMap...map[string]interface{}) ([]byte, error) {
|
||||
fmap := make(gview.FuncMap)
|
||||
if len(funcmap) > 0 {
|
||||
fmap = funcmap[0]
|
||||
if len(funcMap) > 0 {
|
||||
fmap = funcMap[0]
|
||||
}
|
||||
return gins.View().Parse(tpl, r.buildInVars(params), r.buildInFuncs(fmap))
|
||||
}
|
||||
|
||||
// 解析并返回模板内容
|
||||
func (r *Response) ParseTplContent(content string, params gview.Params, funcmap...map[string]interface{}) ([]byte, error) {
|
||||
func (r *Response) ParseTplContent(content string, params gview.Params, funcMap...map[string]interface{}) ([]byte, error) {
|
||||
fmap := make(gview.FuncMap)
|
||||
if len(funcmap) > 0 {
|
||||
fmap = funcmap[0]
|
||||
if len(funcMap) > 0 {
|
||||
fmap = funcMap[0]
|
||||
}
|
||||
return gins.View().ParseContent(content, r.buildInVars(params), r.buildInFuncs(fmap))
|
||||
}
|
||||
@ -77,14 +77,14 @@ func (r *Response) buildInVars(params map[string]interface{}) map[string]interfa
|
||||
}
|
||||
|
||||
// 内置函数
|
||||
func (r *Response) buildInFuncs(funcmap map[string]interface{}) map[string]interface{} {
|
||||
if funcmap == nil {
|
||||
funcmap = make(map[string]interface{})
|
||||
func (r *Response) buildInFuncs(funcMap map[string]interface{}) map[string]interface{} {
|
||||
if funcMap == nil {
|
||||
funcMap = make(map[string]interface{})
|
||||
}
|
||||
funcmap["get"] = r.funcGet
|
||||
funcmap["post"] = r.funcPost
|
||||
funcmap["request"] = r.funcRequest
|
||||
return funcmap
|
||||
funcMap["get"] = r.funcGet
|
||||
funcMap["post"] = r.funcPost
|
||||
funcMap["request"] = r.funcRequest
|
||||
return funcMap
|
||||
}
|
||||
|
||||
// 模板内置函数: get
|
||||
|
||||
@ -9,7 +9,6 @@ package ghttp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
)
|
||||
|
||||
// 域名管理器对象
|
||||
@ -18,121 +17,80 @@ type Domain struct {
|
||||
m map[string]bool // 多域名
|
||||
}
|
||||
|
||||
// 域名对象表,用以存储和检索域名(支持多域名)与域名对象之间的关联关系
|
||||
var domainMap = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 生成一个域名对象
|
||||
// 生成一个域名对象, 参数 domains 支持给定多个域名。
|
||||
func (s *Server) Domain(domains string) *Domain {
|
||||
if r := domainMap.Get(domains); r != nil {
|
||||
return r.(*Domain)
|
||||
}
|
||||
d := &Domain{
|
||||
s : s,
|
||||
m : make(map[string]bool),
|
||||
}
|
||||
result := strings.Split(domains, ",")
|
||||
for _, v := range result {
|
||||
for _, v := range strings.Split(domains, ",") {
|
||||
d.m[strings.TrimSpace(v)] = true
|
||||
}
|
||||
domainMap.Set(domains, d)
|
||||
return d
|
||||
}
|
||||
|
||||
// 注意该方法是直接绑定方法的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
|
||||
func (d *Domain) BindHandler(pattern string, handler HandlerFunc) error {
|
||||
func (d *Domain) BindHandler(pattern string, handler HandlerFunc) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindHandler(pattern + "@" + domain, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindHandler(pattern + "@" + domain, handler)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行对象方法
|
||||
func (d *Domain) BindObject(pattern string, obj interface{}, methods...string) error {
|
||||
if len(methods) > 0 {
|
||||
return d.BindObjectMethod(pattern, obj, strings.Join(methods, ","))
|
||||
}
|
||||
func (d *Domain) BindObject(pattern string, obj interface{}, methods...string) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindObject(pattern + "@" + domain, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindObject(pattern + "@" + domain, obj, methods...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 执行对象方法注册,methods参数不区分大小写
|
||||
func (d *Domain) BindObjectMethod(pattern string, obj interface{}, method string) error {
|
||||
func (d *Domain) BindObjectMethod(pattern string, obj interface{}, method string) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindObjectMethod(pattern + "@" + domain, obj, method); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindObjectMethod(pattern + "@" + domain, obj, method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RESTful执行对象注册
|
||||
func (d *Domain) BindObjectRest(pattern string, obj interface{}) error {
|
||||
func (d *Domain) BindObjectRest(pattern string, obj interface{}) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindObjectRest(pattern + "@" + domain, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindObjectRest(pattern + "@" + domain, obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 控制器注册
|
||||
func (d *Domain) BindController(pattern string, c Controller, methods...string) error {
|
||||
if len(methods) > 0 {
|
||||
return d.BindControllerMethod(pattern, c, strings.Join(methods, ","))
|
||||
}
|
||||
func (d *Domain) BindController(pattern string, c Controller, methods...string) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindController(pattern + "@" + domain, c); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindController(pattern + "@" + domain, c, methods...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 控制器方法注册,methods参数区分大小写
|
||||
func (d *Domain) BindControllerMethod(pattern string, c Controller, method string) error {
|
||||
func (d *Domain) BindControllerMethod(pattern string, c Controller, method string) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindControllerMethod(pattern + "@" + domain, c, method); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindControllerMethod(pattern + "@" + domain, c, method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RESTful控制器注册
|
||||
func (d *Domain) BindControllerRest(pattern string, c Controller) error {
|
||||
func (d *Domain) BindControllerRest(pattern string, c Controller) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindControllerRest(pattern + "@" + domain, c); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindControllerRest(pattern + "@" + domain, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 绑定指定的hook回调函数, hook参数的值由ghttp server设定,参数不区分大小写
|
||||
// 目前hook支持:Init/Shut
|
||||
func (d *Domain)BindHookHandler(pattern string, hook string, handler HandlerFunc) error {
|
||||
func (d *Domain)BindHookHandler(pattern string, hook string, handler HandlerFunc) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindHookHandler(pattern + "@" + domain, hook, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindHookHandler(pattern + "@" + domain, hook, handler)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 通过map批量绑定回调函数
|
||||
func (d *Domain)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error {
|
||||
func (d *Domain)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) {
|
||||
for domain, _ := range d.m {
|
||||
if err := d.s.BindHookHandlerByMap(pattern + "@" + domain, hookmap); err != nil {
|
||||
return err
|
||||
}
|
||||
d.s.BindHookHandlerByMap(pattern + "@" + domain, hookmap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 绑定指定的状态码回调函数
|
||||
|
||||
@ -60,10 +60,11 @@ func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string {
|
||||
|
||||
// 路由注册处理方法。
|
||||
// 如果带有hook参数,表示是回调注册方法; 否则为普通路由执行方法。
|
||||
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) (resultErr error) {
|
||||
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) {
|
||||
// Web Server正常运行时无法动态注册路由方法
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
return errors.New("cannot bind handler while server running")
|
||||
glog.Error("cannot bind handler while server running")
|
||||
return
|
||||
}
|
||||
var hookName string
|
||||
if len(hook) > 0 {
|
||||
@ -71,29 +72,22 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
|
||||
}
|
||||
domain, method, uri, err := s.parsePattern(pattern)
|
||||
if err != nil {
|
||||
return errors.New("invalid pattern")
|
||||
glog.Error("invalid pattern:", pattern)
|
||||
return
|
||||
}
|
||||
if len(uri) == 0 || uri[0] != '/' {
|
||||
glog.Error("invalid pattern:", pattern)
|
||||
return
|
||||
}
|
||||
// 注册地址记录及重复注册判断
|
||||
regkey := s.handlerKey(hookName, method, uri, domain)
|
||||
caller := s.getHandlerRegisterCallerLine(handler)
|
||||
if len(hook) == 0 {
|
||||
if item, ok := s.routesMap[regkey]; ok {
|
||||
s := fmt.Sprintf(`duplicated route registry "%s", already registered in %s`, pattern, item[0].file)
|
||||
glog.Errorfln(s)
|
||||
return errors.New(s)
|
||||
glog.Errorfln(`duplicated route registry "%s", already registered in %s`, pattern, item[0].file)
|
||||
return
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if resultErr == nil {
|
||||
if _, ok := s.routesMap[regkey]; !ok {
|
||||
s.routesMap[regkey] = make([]registeredRouteItem, 0)
|
||||
}
|
||||
s.routesMap[regkey] = append(s.routesMap[regkey], registeredRouteItem {
|
||||
file : caller,
|
||||
handler : handler,
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
// 路由对象
|
||||
handler.router = &Router {
|
||||
@ -193,9 +187,15 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
|
||||
l.PushBack(handler)
|
||||
}
|
||||
}
|
||||
//gutil.Dump(s.serveTree)
|
||||
//gutil.Dump(s.hooksTree)
|
||||
return nil
|
||||
// gutil.Dump(s.serveTree)
|
||||
// gutil.Dump(s.hooksTree)
|
||||
if _, ok := s.routesMap[regkey]; !ok {
|
||||
s.routesMap[regkey] = make([]registeredRouteItem, 0)
|
||||
}
|
||||
s.routesMap[regkey] = append(s.routesMap[regkey], registeredRouteItem {
|
||||
file : caller,
|
||||
handler : handler,
|
||||
})
|
||||
}
|
||||
|
||||
// 对比两个handlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序。
|
||||
|
||||
@ -18,8 +18,8 @@ import (
|
||||
)
|
||||
|
||||
// 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写
|
||||
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error {
|
||||
return s.setHandler(pattern, &handlerItem {
|
||||
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) {
|
||||
s.setHandler(pattern, &handlerItem {
|
||||
name : runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
@ -28,13 +28,10 @@ func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc
|
||||
}
|
||||
|
||||
// 通过map批量绑定回调函数
|
||||
func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error {
|
||||
func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) {
|
||||
for k, v := range hookmap {
|
||||
if err := s.BindHookHandler(pattern, k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
s.BindHookHandler(pattern, k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 事件回调处理,内部使用了缓存处理.
|
||||
|
||||
@ -8,20 +8,19 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strings"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 绑定控制器,控制器需要实现gmvc.Controller接口
|
||||
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
|
||||
// 第三个参数methods用以指定需要注册的方法,支持多个方法名称,多个方法以英文“,”号分隔,区分大小写
|
||||
func (s *Server)BindController(pattern string, c Controller, methods...string) error {
|
||||
// 绑定控制器,控制器需要实现 gmvc.Controller 接口,
|
||||
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话,
|
||||
// 第三个参数methods用以指定需要注册的方法,支持多个方法名称,多个方法以英文“,”号分隔,区分大小写.
|
||||
func (s *Server)BindController(pattern string, c Controller, methods...string) {
|
||||
methodMap := (map[string]bool)(nil)
|
||||
if len(methods) > 0 {
|
||||
methodMap = make(map[string]bool)
|
||||
@ -45,11 +44,7 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
continue
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
if methodMap != nil {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
}
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
@ -71,8 +66,8 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) {
|
||||
p := gstr.PosR(key, "/index")
|
||||
k := key[0 : p] + key[p + 6 : ]
|
||||
if len(k) == 0 {
|
||||
k = "/"
|
||||
if len(k) == 0 || k[0] == '@' {
|
||||
k = "/" + k
|
||||
}
|
||||
m[k] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
@ -83,11 +78,11 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
|
||||
// 绑定路由到指定的方法执行
|
||||
func (s *Server)BindControllerMethod(pattern string, c Controller, method string) error {
|
||||
// 绑定路由到指定的方法执行, 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。
|
||||
func (s *Server)BindControllerMethod(pattern string, c Controller, method string) {
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
t := v.Type()
|
||||
@ -95,12 +90,12 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
|
||||
mname := strings.TrimSpace(method)
|
||||
fval := v.MethodByName(mname)
|
||||
if !fval.IsValid() {
|
||||
return errors.New("invalid method name:" + mname)
|
||||
glog.Error("invalid method name:" + mname)
|
||||
return
|
||||
}
|
||||
if _, ok := fval.Interface().(func()); !ok {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, fval.Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, fval.Type().String())
|
||||
return
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
@ -116,14 +111,14 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
|
||||
fname : mname,
|
||||
faddr : nil,
|
||||
}
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
|
||||
// 绑定控制器(RESTFul),控制器需要实现gmvc.Controller接口
|
||||
// 方法会识别HTTP方法,并做REST绑定处理,例如:Post方法会绑定到HTTP POST的方法请求处理,Delete方法会绑定到HTTP DELETE的方法请求处理
|
||||
// 因此只会绑定HTTP Method对应的方法,其他方法不会自动注册绑定
|
||||
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
|
||||
func (s *Server)BindControllerRest(pattern string, c Controller) error {
|
||||
func (s *Server)BindControllerRest(pattern string, c Controller) {
|
||||
// 遍历控制器,获取方法列表,并构造成uri
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
@ -138,9 +133,8 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error {
|
||||
continue
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
return
|
||||
}
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
@ -156,5 +150,5 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error {
|
||||
faddr : nil,
|
||||
}
|
||||
}
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
|
||||
@ -8,17 +8,17 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"bytes"
|
||||
"runtime"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
|
||||
func (s *Server) BindHandler(pattern string, handler HandlerFunc) error {
|
||||
return s.bindHandlerItem(pattern, &handlerItem {
|
||||
func (s *Server) BindHandler(pattern string, handler HandlerFunc) {
|
||||
s.bindHandlerItem(pattern, &handlerItem {
|
||||
name : runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
|
||||
rtype : gROUTE_REGISTER_HANDLER,
|
||||
ctype : nil,
|
||||
@ -30,21 +30,19 @@ func (s *Server) BindHandler(pattern string, handler HandlerFunc) error {
|
||||
// 绑定URI到操作函数/方法
|
||||
// pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn
|
||||
// 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行
|
||||
func (s *Server) bindHandlerItem(pattern string, item *handlerItem) error {
|
||||
func (s *Server) bindHandlerItem(pattern string, item *handlerItem) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
return errors.New("server handlers cannot be changed while running")
|
||||
glog.Error("server handlers cannot be changed while running")
|
||||
return
|
||||
}
|
||||
return s.setHandler(pattern, item)
|
||||
s.setHandler(pattern, item)
|
||||
}
|
||||
|
||||
// 通过映射数组绑定URI到操作函数/方法
|
||||
func (s *Server) bindHandlerByMap(m handlerMap) error {
|
||||
func (s *Server) bindHandlerByMap(m handlerMap) {
|
||||
for p, h := range m {
|
||||
if err := s.bindHandlerItem(p, h); err != nil {
|
||||
return err
|
||||
}
|
||||
s.bindHandlerItem(p, h)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将内置的名称按照设定的规则合并到pattern中,内置名称按照{.xxx}规则命名。
|
||||
|
||||
@ -8,19 +8,18 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strings"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面
|
||||
// 第三个参数methods用以指定需要注册的方法,支持多个方法名称,多个方法以英文“,”号分隔,区分大小写
|
||||
func (s *Server)BindObject(pattern string, obj interface{}, methods...string) error {
|
||||
func (s *Server)BindObject(pattern string, obj interface{}, methods...string) {
|
||||
methodMap := (map[string]bool)(nil)
|
||||
if len(methods) > 0 {
|
||||
methodMap = make(map[string]bool)
|
||||
@ -52,11 +51,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
if methodMap != nil {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
}
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
@ -78,8 +73,8 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) {
|
||||
p := gstr.PosR(key, "/index")
|
||||
k := key[0 : p] + key[p + 6 : ]
|
||||
if len(k) == 0 {
|
||||
k = "/"
|
||||
if len(k) == 0 || k[0] == '@' {
|
||||
k = "/" + k
|
||||
}
|
||||
m[k] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
@ -92,12 +87,12 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面
|
||||
// 第三个参数methods支持多个方法注册,多个方法以英文“,”号分隔,区分大小写
|
||||
func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string) error {
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面,
|
||||
// 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。
|
||||
func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string) {
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(obj)
|
||||
t := v.Type()
|
||||
@ -105,13 +100,13 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
mname := strings.TrimSpace(method)
|
||||
fval := v.MethodByName(mname)
|
||||
if !fval.IsValid() {
|
||||
return errors.New("invalid method name:" + mname)
|
||||
glog.Error("invalid method name:" + mname)
|
||||
return
|
||||
}
|
||||
faddr, ok := fval.Interface().(func(*Request))
|
||||
if !ok {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request)" is required`, fval.Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, fval.Type().String())
|
||||
return
|
||||
}
|
||||
finit := (func(*Request))(nil)
|
||||
fshut := (func(*Request))(nil)
|
||||
@ -138,12 +133,12 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
fshut : fshut,
|
||||
}
|
||||
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面
|
||||
// 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义
|
||||
func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面,
|
||||
// 需要注意对象方法的定义必须按照 ghttp.HandlerFunc 来定义
|
||||
func (s *Server)BindObjectRest(pattern string, obj interface{}) {
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(obj)
|
||||
t := v.Type()
|
||||
@ -165,9 +160,8 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
glog.Error(s)
|
||||
return errors.New(s)
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
@ -185,5 +179,5 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
|
||||
fshut : fshut,
|
||||
}
|
||||
}
|
||||
return s.bindHandlerByMap(m)
|
||||
s.bindHandlerByMap(m)
|
||||
}
|
||||
@ -17,7 +17,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := 8000; i <= 8100; i++ {
|
||||
for i := 8000; i <= 9000; i++ {
|
||||
ports.Append(i)
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +38,11 @@ func (c *Controller) Show() {
|
||||
c.Response.Write("Controller Show")
|
||||
}
|
||||
|
||||
func (c *Controller) Info() {
|
||||
c.Response.Write("Controller Info")
|
||||
}
|
||||
|
||||
// 控制器注册测试
|
||||
func Test_Router_Controller(t *testing.T) {
|
||||
func Test_Router_Controller1(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindController("/", new(Controller))
|
||||
@ -71,3 +73,58 @@ func Test_Router_Controller(t *testing.T) {
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Controller2(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindController("/controller", new(Controller), "Show, Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "1Controller Show2")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "1Controller Info2")
|
||||
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_ControllerMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindControllerMethod("/controller-info", new(Controller), "Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller-info"), "1Controller Info2")
|
||||
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
338
g/net/ghttp/ghttp_unit_router_domain_basic_test.go
Normal file
338
g/net/ghttp/ghttp_unit_router_domain_basic_test.go
Normal file
@ -0,0 +1,338 @@
|
||||
// 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.
|
||||
|
||||
// 基本路由功能以及优先级测试
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 基本路由功能测试
|
||||
func Test_Router_DomainBasic(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindHandler("/:name", func(r *ghttp.Request){
|
||||
r.Response.Write("/:name")
|
||||
})
|
||||
d.BindHandler("/:name/update", func(r *ghttp.Request){
|
||||
r.Response.Write(r.Get("name"))
|
||||
})
|
||||
d.BindHandler("/:name/:action", func(r *ghttp.Request){
|
||||
r.Response.Write(r.Get("action"))
|
||||
})
|
||||
d.BindHandler("/:name/*any", func(r *ghttp.Request){
|
||||
r.Response.Write(r.Get("any"))
|
||||
})
|
||||
d.BindHandler("/user/list/{field}.html", func(r *ghttp.Request){
|
||||
r.Response.Write(r.Get("field"))
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
gtest.Assert(client.GetContent("/john"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/john/update"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/john/edit"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/user/list/100.html"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
gtest.Assert(client.GetContent("/john"), "")
|
||||
gtest.Assert(client.GetContent("/john/update"), "john")
|
||||
gtest.Assert(client.GetContent("/john/edit"), "edit")
|
||||
gtest.Assert(client.GetContent("/user/list/100.html"), "100")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
gtest.Assert(client.GetContent("/john"), "")
|
||||
gtest.Assert(client.GetContent("/john/update"), "john")
|
||||
gtest.Assert(client.GetContent("/john/edit"), "edit")
|
||||
gtest.Assert(client.GetContent("/user/list/100.html"), "100")
|
||||
})
|
||||
}
|
||||
|
||||
// 测试HTTP Method注册.
|
||||
func Test_Router_DomainMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindHandler("GET:/get", func(r *ghttp.Request){
|
||||
|
||||
})
|
||||
d.BindHandler("POST:/post", func(r *ghttp.Request){
|
||||
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
resp1, err := client.Get("/get")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 404)
|
||||
|
||||
resp2, err := client.Post("/get")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 404)
|
||||
|
||||
resp3, err := client.Get("/post")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 404)
|
||||
|
||||
resp4, err := client.Post("/post")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 404)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
resp1, err := client.Get("/get")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 200)
|
||||
|
||||
resp2, err := client.Post("/get")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 404)
|
||||
|
||||
resp3, err := client.Get("/post")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 404)
|
||||
|
||||
resp4, err := client.Post("/post")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 200)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
resp1, err := client.Get("/get")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 200)
|
||||
|
||||
resp2, err := client.Post("/get")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 404)
|
||||
|
||||
resp3, err := client.Get("/post")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 404)
|
||||
|
||||
resp4, err := client.Post("/post")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 200)
|
||||
})
|
||||
}
|
||||
|
||||
// 测试状态返回.
|
||||
func Test_Router_DomainStatus(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindHandler("/200", func(r *ghttp.Request){
|
||||
r.Response.WriteStatus(200)
|
||||
})
|
||||
d.BindHandler("/300", func(r *ghttp.Request){
|
||||
r.Response.WriteStatus(300)
|
||||
})
|
||||
d.BindHandler("/400", func(r *ghttp.Request){
|
||||
r.Response.WriteStatus(400)
|
||||
})
|
||||
d.BindHandler("/500", func(r *ghttp.Request){
|
||||
r.Response.WriteStatus(500)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
resp1, err := client.Get("/200")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 404)
|
||||
|
||||
resp2, err := client.Get("/300")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 404)
|
||||
|
||||
resp3, err := client.Get("/400")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 404)
|
||||
|
||||
resp4, err := client.Get("/500")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 404)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
resp1, err := client.Get("/200")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 200)
|
||||
|
||||
resp2, err := client.Get("/300")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 300)
|
||||
|
||||
resp3, err := client.Get("/400")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 400)
|
||||
|
||||
resp4, err := client.Get("/500")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 500)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
resp1, err := client.Get("/200")
|
||||
defer resp1.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.StatusCode, 200)
|
||||
|
||||
resp2, err := client.Get("/300")
|
||||
defer resp2.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp2.StatusCode, 300)
|
||||
|
||||
resp3, err := client.Get("/400")
|
||||
defer resp3.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp3.StatusCode, 400)
|
||||
|
||||
resp4, err := client.Get("/500")
|
||||
defer resp4.Close()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp4.StatusCode, 500)
|
||||
})
|
||||
}
|
||||
|
||||
// 自定义状态码处理.
|
||||
func Test_Router_DomainCustomStatusHandler(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindHandler("/", func(r *ghttp.Request){
|
||||
r.Response.Write("hello")
|
||||
})
|
||||
d.BindStatusHandler(404, func(r *ghttp.Request){
|
||||
r.Response.Write("404 page")
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/ThisDoesNotExist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "hello")
|
||||
gtest.Assert(client.GetContent("/ThisDoesNotExist"), "404 page")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "hello")
|
||||
gtest.Assert(client.GetContent("/ThisDoesNotExist"), "404 page")
|
||||
})
|
||||
}
|
||||
|
||||
// 测试不存在的路由.
|
||||
func Test_Router_Domain404(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindHandler("/", func(r *ghttp.Request){
|
||||
r.Response.Write("hello")
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "hello")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "hello")
|
||||
})
|
||||
}
|
||||
127
g/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go
Normal file
127
g/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go
Normal file
@ -0,0 +1,127 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/frame/gmvc"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DomainControllerRest struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Init(r *ghttp.Request) {
|
||||
c.Controller.Init(r)
|
||||
c.Response.Write("1")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Shut() {
|
||||
c.Response.Write("2")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Get() {
|
||||
c.Response.Write("Controller Get")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Put() {
|
||||
c.Response.Write("Controller Put")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Post() {
|
||||
c.Response.Write("Controller Post")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Delete() {
|
||||
c.Response.Write("Controller Delete")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Patch() {
|
||||
c.Response.Write("Controller Patch")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Options() {
|
||||
c.Response.Write("Controller Options")
|
||||
}
|
||||
|
||||
func (c *DomainControllerRest) Head() {
|
||||
c.Response.Header().Set("head-ok", "1")
|
||||
}
|
||||
|
||||
// 控制器注册测试
|
||||
func Test_Router_DomainControllerRest(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindControllerRest("/", new(DomainControllerRest))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.PutContent("/"), "Not Found")
|
||||
gtest.Assert(client.PostContent("/"), "Not Found")
|
||||
gtest.Assert(client.DeleteContent("/"), "Not Found")
|
||||
gtest.Assert(client.PatchContent("/"), "Not Found")
|
||||
gtest.Assert(client.OptionsContent("/"), "Not Found")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Controller Get2")
|
||||
gtest.Assert(client.PutContent("/"), "1Controller Put2")
|
||||
gtest.Assert(client.PostContent("/"), "1Controller Post2")
|
||||
gtest.Assert(client.DeleteContent("/"), "1Controller Delete2")
|
||||
gtest.Assert(client.PatchContent("/"), "1Controller Patch2")
|
||||
gtest.Assert(client.OptionsContent("/"), "1Controller Options2")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "1")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Controller Get2")
|
||||
gtest.Assert(client.PutContent("/"), "1Controller Put2")
|
||||
gtest.Assert(client.PostContent("/"), "1Controller Post2")
|
||||
gtest.Assert(client.DeleteContent("/"), "1Controller Delete2")
|
||||
gtest.Assert(client.PatchContent("/"), "1Controller Patch2")
|
||||
gtest.Assert(client.OptionsContent("/"), "1Controller Options2")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "1")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
202
g/net/ghttp/ghttp_unit_router_domain_controller_test.go
Normal file
202
g/net/ghttp/ghttp_unit_router_domain_controller_test.go
Normal file
@ -0,0 +1,202 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/frame/gmvc"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DomainController struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *DomainController) Init(r *ghttp.Request) {
|
||||
c.Controller.Init(r)
|
||||
c.Response.Write("1")
|
||||
}
|
||||
|
||||
func (c *DomainController) Shut() {
|
||||
c.Response.Write("2")
|
||||
}
|
||||
|
||||
func (c *DomainController) Index() {
|
||||
c.Response.Write("Controller Index")
|
||||
}
|
||||
|
||||
func (c *DomainController) Show() {
|
||||
c.Response.Write("Controller Show")
|
||||
}
|
||||
|
||||
func (c *DomainController) Info() {
|
||||
c.Response.Write("Controller Info")
|
||||
}
|
||||
|
||||
func Test_Router_DomainController1(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindController("/", new(DomainController))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Controller Index2")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "1Controller Index2")
|
||||
gtest.Assert(client.GetContent("/show"), "1Controller Show2")
|
||||
gtest.Assert(client.GetContent("/info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Controller Index2")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "1Controller Index2")
|
||||
gtest.Assert(client.GetContent("/show"), "1Controller Show2")
|
||||
gtest.Assert(client.GetContent("/info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_DomainController2(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindController("/controller", new(DomainController), "Show, Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "1Controller Show2")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "1Controller Show2")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_DomainControllerMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindControllerMethod("/controller-info", new(DomainController), "Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller-info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller-info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/controller-info"), "1Controller Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
122
g/net/ghttp/ghttp_unit_router_domain_object_rest_test.go
Normal file
122
g/net/ghttp/ghttp_unit_router_domain_object_rest_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DomainObjectRest struct {}
|
||||
|
||||
func (o *DomainObjectRest) Init(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Shut(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Get(r *ghttp.Request) {
|
||||
r.Response.Write("Object Get")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Put(r *ghttp.Request) {
|
||||
r.Response.Write("Object Put")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Post(r *ghttp.Request) {
|
||||
r.Response.Write("Object Post")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Delete(r *ghttp.Request) {
|
||||
r.Response.Write("Object Delete")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Patch(r *ghttp.Request) {
|
||||
r.Response.Write("Object Patch")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Options(r *ghttp.Request) {
|
||||
r.Response.Write("Object Options")
|
||||
}
|
||||
|
||||
func (o *DomainObjectRest) Head(r *ghttp.Request) {
|
||||
r.Response.Header().Set("head-ok", "1")
|
||||
}
|
||||
|
||||
func Test_Router_DomainObjectRest(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.BindObjectRest("/", new(DomainObjectRest))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.PutContent("/"), "Not Found")
|
||||
gtest.Assert(client.PostContent("/"), "Not Found")
|
||||
gtest.Assert(client.DeleteContent("/"), "Not Found")
|
||||
gtest.Assert(client.PatchContent("/"), "Not Found")
|
||||
gtest.Assert(client.OptionsContent("/"), "Not Found")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Object Get2")
|
||||
gtest.Assert(client.PutContent("/"), "1Object Put2")
|
||||
gtest.Assert(client.PostContent("/"), "1Object Post2")
|
||||
gtest.Assert(client.DeleteContent("/"), "1Object Delete2")
|
||||
gtest.Assert(client.PatchContent("/"), "1Object Patch2")
|
||||
gtest.Assert(client.OptionsContent("/"), "1Object Options2")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "1")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Object Get2")
|
||||
gtest.Assert(client.PutContent("/"), "1Object Put2")
|
||||
gtest.Assert(client.PostContent("/"), "1Object Post2")
|
||||
gtest.Assert(client.DeleteContent("/"), "1Object Delete2")
|
||||
gtest.Assert(client.PatchContent("/"), "1Object Patch2")
|
||||
gtest.Assert(client.OptionsContent("/"), "1Object Options2")
|
||||
resp1, err := client.Head("/")
|
||||
if err == nil {
|
||||
defer resp1.Close()
|
||||
}
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(resp1.Header.Get("head-ok"), "1")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
196
g/net/ghttp/ghttp_unit_router_domain_object_test.go
Normal file
196
g/net/ghttp/ghttp_unit_router_domain_object_test.go
Normal file
@ -0,0 +1,196 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DomainObject struct {}
|
||||
|
||||
func (o *DomainObject) Init(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
}
|
||||
|
||||
func (o *DomainObject) Shut(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
}
|
||||
|
||||
func (o *DomainObject) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Object Index")
|
||||
}
|
||||
|
||||
func (o *DomainObject) Show(r *ghttp.Request) {
|
||||
r.Response.Write("Object Show")
|
||||
}
|
||||
|
||||
func (o *DomainObject) Info(r *ghttp.Request) {
|
||||
r.Response.Write("Object Info")
|
||||
}
|
||||
|
||||
func Test_Router_DomainObject1(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindObject("/", new(DomainObject))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Object Index2")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "1Object Index2")
|
||||
gtest.Assert(client.GetContent("/show"), "1Object Show2")
|
||||
gtest.Assert(client.GetContent("/info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "1Object Index2")
|
||||
gtest.Assert(client.GetContent("/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/index"), "1Object Index2")
|
||||
gtest.Assert(client.GetContent("/show"), "1Object Show2")
|
||||
gtest.Assert(client.GetContent("/info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func Test_Router_DomainObject2(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindObject("/object", new(DomainObject), "Show, Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "1Object Show2")
|
||||
gtest.Assert(client.GetContent("/object/info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "1Object Show2")
|
||||
gtest.Assert(client.GetContent("/object/info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_DomainObjectMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Domain("localhost, local").BindObjectMethod("/object-info", new(DomainObject), "Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object-info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://localhost:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object-info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object-info"), "1Object Info2")
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
126
g/net/ghttp/ghttp_unit_router_exit_test.go
Normal file
126
g/net/ghttp/ghttp_unit_router_exit_test.go
Normal file
@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Router_Exit(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
|
||||
"BeforeServe" : func(r *ghttp.Request){ r.Response.Write("1") },
|
||||
"AfterServe" : func(r *ghttp.Request){ r.Response.Write("2") },
|
||||
"BeforeOutput" : func(r *ghttp.Request){ r.Response.Write("3") },
|
||||
"AfterOutput" : func(r *ghttp.Request){ r.Response.Write("4") },
|
||||
"BeforeClose" : func(r *ghttp.Request){ r.Response.Write("5") },
|
||||
"AfterClose" : func(r *ghttp.Request){ r.Response.Write("6") },
|
||||
})
|
||||
s.BindHandler("/test/test", func(r *ghttp.Request) {
|
||||
r.Response.Write("test-start")
|
||||
r.Exit()
|
||||
r.Response.Write("test-end")
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "123")
|
||||
gtest.Assert(client.GetContent("/test/test"), "1test-start23")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_ExitHook(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/priority/show", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("3")
|
||||
r.ExitHook()
|
||||
},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/priority/show"), "3show")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_ExitAll(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/priority/show", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("3")
|
||||
r.ExitAll()
|
||||
},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/priority/show"), "3")
|
||||
})
|
||||
}
|
||||
87
g/net/ghttp/ghttp_unit_router_hook_test.go
Normal file
87
g/net/ghttp/ghttp_unit_router_hook_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Router_Hook_Basic(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
|
||||
"BeforeServe" : func(r *ghttp.Request){ r.Response.Write("1") },
|
||||
"AfterServe" : func(r *ghttp.Request){ r.Response.Write("2") },
|
||||
"BeforeOutput" : func(r *ghttp.Request){ r.Response.Write("3") },
|
||||
"AfterOutput" : func(r *ghttp.Request){ r.Response.Write("4") },
|
||||
"BeforeClose" : func(r *ghttp.Request){ r.Response.Write("5") },
|
||||
"AfterClose" : func(r *ghttp.Request){ r.Response.Write("6") },
|
||||
})
|
||||
s.BindHandler("/test/test", func(r *ghttp.Request) {
|
||||
r.Response.Write("test")
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "123")
|
||||
gtest.Assert(client.GetContent("/test/test"), "1test23")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Hook_Priority(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/priority/show", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("3")
|
||||
},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/priority/show"), "312show")
|
||||
gtest.Assert(client.GetContent("/priority/any/any"), "2")
|
||||
gtest.Assert(client.GetContent("/priority/name"), "12")
|
||||
})
|
||||
}
|
||||
|
||||
112
g/net/ghttp/ghttp_unit_router_names_test.go
Normal file
112
g/net/ghttp/ghttp_unit_router_names_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
// 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.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NamesObject struct {}
|
||||
|
||||
func (o *NamesObject) ShowName(r *ghttp.Request) {
|
||||
r.Response.Write("Object Show Name")
|
||||
}
|
||||
|
||||
func Test_NameToUri_FullName(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetNameToUriType(ghttp.NAME_TO_URI_TYPE_FULLNAME)
|
||||
s.BindObject("/{.struct}/{.method}", new(NamesObject))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/NamesObject"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/NamesObject/ShowName"), "Object Show Name")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func Test_NameToUri_AllLower(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetNameToUriType(ghttp.NAME_TO_URI_TYPE_ALLLOWER)
|
||||
s.BindObject("/{.struct}/{.method}", new(NamesObject))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/NamesObject"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/namesobject/showname"), "Object Show Name")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NameToUri_Camel(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetNameToUriType(ghttp.NAME_TO_URI_TYPE_CAMEL)
|
||||
s.BindObject("/{.struct}/{.method}", new(NamesObject))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/NamesObject"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/namesObject/showName"), "Object Show Name")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NameToUri_Default(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetNameToUriType(ghttp.NAME_TO_URI_TYPE_DEFAULT)
|
||||
s.BindObject("/{.struct}/{.method}", new(NamesObject))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/NamesObject"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/names-object/show-name"), "Object Show Name")
|
||||
})
|
||||
}
|
||||
|
||||
@ -33,8 +33,11 @@ func (o *Object) Show(r *ghttp.Request) {
|
||||
r.Response.Write("Object Show")
|
||||
}
|
||||
|
||||
// 执行对象注册
|
||||
func Test_Router_Object(t *testing.T) {
|
||||
func (o *Object) Info(r *ghttp.Request) {
|
||||
r.Response.Write("Object Info")
|
||||
}
|
||||
|
||||
func Test_Router_Object1(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindObject("/", new(Object))
|
||||
@ -65,3 +68,59 @@ func Test_Router_Object(t *testing.T) {
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func Test_Router_Object2(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindObject("/object", new(Object), "Show, Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "1Object Show2")
|
||||
gtest.Assert(client.GetContent("/object/info"), "1Object Info2")
|
||||
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_ObjectMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindObjectMethod("/object-info", new(Object), "Info")
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/init"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/shut"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/index"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/show"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object/info"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/object-info"), "1Object Info2")
|
||||
|
||||
gtest.Assert(client.GetContent("/none-exist"), "Not Found")
|
||||
})
|
||||
}
|
||||
232
g/net/ghttp/ghttp_unit_static_test.go
Normal file
232
g/net/ghttp/ghttp_unit_static_test.go
Normal file
@ -0,0 +1,232 @@
|
||||
// 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.
|
||||
|
||||
// 静态文件服务测试
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Static_Folder_Forbidden(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
defer gfile.Remove(path)
|
||||
gfile.PutContents(path + "/test.html", "test")
|
||||
s.SetServerRoot(path)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/index.html"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_IndexFolder(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
defer gfile.Remove(path)
|
||||
gfile.PutContents(path + "/test.html", "test")
|
||||
s.SetIndexFolder(true)
|
||||
s.SetServerRoot(path)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.AssertNE(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/index.html"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_IndexFiles1(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
defer gfile.Remove(path)
|
||||
gfile.PutContents(path + "/index.html", "index")
|
||||
gfile.PutContents(path + "/test.html", "test")
|
||||
s.SetServerRoot(path)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "index")
|
||||
gtest.Assert(client.GetContent("/index.html"), "index")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_IndexFiles2(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
defer gfile.Remove(path)
|
||||
gfile.PutContents(path + "/test.html", "test")
|
||||
s.SetIndexFiles([]string{"index.html", "test.html"})
|
||||
s.SetServerRoot(path)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "test")
|
||||
gtest.Assert(client.GetContent("/index.html"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_AddSearchPath1(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
|
||||
defer gfile.Remove(path1)
|
||||
defer gfile.Remove(path2)
|
||||
gfile.PutContents(path2 + "/test.html", "test")
|
||||
s.SetServerRoot(path1)
|
||||
s.AddSearchPath(path2)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_AddSearchPath2(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
|
||||
defer gfile.Remove(path1)
|
||||
defer gfile.Remove(path2)
|
||||
gfile.PutContents(path1 + "/test.html", "test1")
|
||||
gfile.PutContents(path2 + "/test.html", "test2")
|
||||
s.SetServerRoot(path1)
|
||||
s.AddSearchPath(path2)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_AddStaticPath(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d`, gfile.TempDir(), p, p)
|
||||
defer gfile.Remove(path1)
|
||||
defer gfile.Remove(path2)
|
||||
gfile.PutContents(path1 + "/test.html", "test1")
|
||||
gfile.PutContents(path2 + "/test.html", "test2")
|
||||
s.SetServerRoot(path1)
|
||||
s.AddStaticPath("/my-test", path2)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test1")
|
||||
gtest.Assert(client.GetContent("/my-test/test.html"), "test2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_AddStaticPath_Priority(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path1 := fmt.Sprintf(`%s/ghttp/static/test/%d/test`, gfile.TempDir(), p)
|
||||
path2 := fmt.Sprintf(`%s/ghttp/static/test/%d/%d/test`, gfile.TempDir(), p, p)
|
||||
defer gfile.Remove(path1)
|
||||
defer gfile.Remove(path2)
|
||||
gfile.PutContents(path1 + "/test.html", "test1")
|
||||
gfile.PutContents(path2 + "/test.html", "test2")
|
||||
s.SetServerRoot(path1)
|
||||
s.AddStaticPath("/test", path2)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test1")
|
||||
gtest.Assert(client.GetContent("/test/test.html"), "test2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Static_Rewrite(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
path := fmt.Sprintf(`%s/ghttp/static/test/%d`, gfile.TempDir(), p)
|
||||
defer gfile.Remove(path)
|
||||
gfile.PutContents(path + "/test1.html", "test1")
|
||||
gfile.PutContents(path + "/test2.html", "test2")
|
||||
s.SetServerRoot(path)
|
||||
s.SetRewrite("/test.html", "/test1.html")
|
||||
s.SetRewriteMap(g.MapStrStr{
|
||||
"/my-test1" : "/test1.html",
|
||||
"/my-test2" : "/test2.html",
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(time.Second)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Forbidden")
|
||||
gtest.Assert(client.GetContent("/test.html"), "test1")
|
||||
gtest.Assert(client.GetContent("/test1.html"), "test1")
|
||||
gtest.Assert(client.GetContent("/test2.html"), "test2")
|
||||
gtest.Assert(client.GetContent("/my-test1"), "test1")
|
||||
gtest.Assert(client.GetContent("/my-test2"), "test2")
|
||||
})
|
||||
}
|
||||
@ -37,6 +37,8 @@ type Config struct {
|
||||
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率)
|
||||
}
|
||||
|
||||
// New returns a new configuration management object.
|
||||
//
|
||||
// 生成一个配置管理对象
|
||||
func New(path string, file...string) *Config {
|
||||
name := DEFAULT_CONFIG_FILE
|
||||
@ -55,6 +57,8 @@ func New(path string, file...string) *Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// filePath returns the absolute configuration file path for the given filename by <file>.
|
||||
//
|
||||
// 判断从哪个配置文件中获取内容,返回配置文件的绝对路径
|
||||
func (c *Config) filePath(file...string) (path string) {
|
||||
name := c.name.Val()
|
||||
|
||||
@ -39,14 +39,14 @@ func TestCron_Add_Close(t *testing.T) {
|
||||
gtest.AssertNE(err3, nil)
|
||||
gtest.Assert(err4, nil)
|
||||
gtest.Assert(cron.Size(), 3)
|
||||
time.Sleep(1100*time.Millisecond)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
time.Sleep(1100*time.Millisecond)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 5)
|
||||
cron.Close()
|
||||
time.Sleep(1100*time.Millisecond)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
fixedLength := array.Len()
|
||||
time.Sleep(1100*time.Millisecond)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), fixedLength)
|
||||
})
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ func (w *Watcher) startWatchLoop() {
|
||||
// 关闭事件
|
||||
case <- w.closeChan: return
|
||||
|
||||
// 监听事件
|
||||
// 监听事件
|
||||
case ev := <- w.watcher.Events:
|
||||
//fmt.Println("ev:", ev.String())
|
||||
w.cache.SetIfNotExist(ev.String(), func() interface{} {
|
||||
|
||||
@ -235,7 +235,7 @@ func TestTimer_AddLeveledEntry1(t *testing.T) {
|
||||
})
|
||||
time.Sleep(1500*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 0)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
time.Sleep(1300*time.Millisecond)
|
||||
//glog.Println("check")
|
||||
gtest.Assert(array.Len(), 1)
|
||||
})
|
||||
|
||||
@ -181,14 +181,22 @@ func AssertLTE(value, expect interface{}) {
|
||||
}
|
||||
|
||||
|
||||
// 断言判断, value IN expect; 注意: expect必须为slice类型
|
||||
// 断言判断, value IN expect; 注意: expect必须为slice类型。
|
||||
// 注意:value参数可以为普通变量,也可以为slice类型。
|
||||
func AssertIN(value, expect interface{}) {
|
||||
passed := false
|
||||
passed := true
|
||||
switch reflect.ValueOf(expect).Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
for _, v := range gconv.Interfaces(expect) {
|
||||
if v == value {
|
||||
passed = true
|
||||
for _, v1 := range gconv.Interfaces(value) {
|
||||
result := false
|
||||
for _, v2 := range gconv.Interfaces(expect) {
|
||||
if v1 == v2 {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !result {
|
||||
passed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -200,24 +208,31 @@ func AssertIN(value, expect interface{}) {
|
||||
|
||||
// 断言判断, value NOT IN expect; 注意: expect必须为slice类型
|
||||
func AssertNI(value, expect interface{}) {
|
||||
passed := false
|
||||
passed := true
|
||||
switch reflect.ValueOf(expect).Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
for _, v := range gconv.Interfaces(expect) {
|
||||
if v == value {
|
||||
passed = true
|
||||
for _, v1 := range gconv.Interfaces(value) {
|
||||
result := true
|
||||
for _, v2 := range gconv.Interfaces(expect) {
|
||||
if v1 == v2 {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !result {
|
||||
passed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if passed {
|
||||
if !passed {
|
||||
panic(fmt.Sprintf(`[ASSERT] EXPECT %v NOT IN %v`, value, expect))
|
||||
}
|
||||
}
|
||||
|
||||
// 提示错误不退出进程执行
|
||||
func Error(message...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] %s\n%s", fmt.Sprint(message...), getBacktrace())
|
||||
panic(fmt.Sprintf("[ERROR] %s", fmt.Sprint(message...)))
|
||||
}
|
||||
|
||||
// 提示错误并退出进程执行
|
||||
@ -238,14 +253,21 @@ func compareMap(value, expect interface{}) error {
|
||||
if rvExpect.Kind() == reflect.Map {
|
||||
if rvValue.Kind() == reflect.Map {
|
||||
if rvExpect.Len() == rvValue.Len() {
|
||||
// 将两个map类型转换为同一个map类型, 才能执行比较,
|
||||
// 直接使用 rvValue.MapIndex(key).Interface() 当key类型不一致时会报错。
|
||||
mValue := make(map[string]string)
|
||||
mExpect := make(map[string]string)
|
||||
ksValue := rvValue.MapKeys()
|
||||
ksExpect := rvExpect.MapKeys()
|
||||
for _, key := range ksValue {
|
||||
mValue[gconv.String(key.Interface())] = gconv.String(rvValue.MapIndex(key).Interface())
|
||||
}
|
||||
for _, key := range ksExpect {
|
||||
if fmt.Sprintf("%v", rvValue.MapIndex(key).Interface()) != fmt.Sprintf("%v", rvExpect.MapIndex(key).Interface()) {
|
||||
return fmt.Errorf(`[ASSERT] EXPECT VALUE map["%v"]:%v == %v`,
|
||||
key,
|
||||
rvValue.MapIndex(key).Interface(),
|
||||
rvExpect.MapIndex(key).Interface(),
|
||||
)
|
||||
mExpect[gconv.String(key.Interface())] = gconv.String(rvExpect.MapIndex(key).Interface())
|
||||
}
|
||||
for k, v := range mExpect {
|
||||
if v != mValue[k] {
|
||||
return fmt.Errorf(`[ASSERT] EXPECT VALUE map["%v"]:%v == %v`, k, mValue[k], v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -47,7 +47,16 @@ func Convert(i interface{}, t string, extraParams...interface{}) interface{} {
|
||||
return Time(i, String(extraParams[0]))
|
||||
}
|
||||
return Time(i)
|
||||
|
||||
case "gtime.Time":
|
||||
if len(extraParams) > 0 {
|
||||
return GTime(i, String(extraParams[0]))
|
||||
}
|
||||
return *GTime(i)
|
||||
case "*gtime.Time":
|
||||
if len(extraParams) > 0 {
|
||||
return GTime(i, String(extraParams[0]))
|
||||
}
|
||||
return GTime(i)
|
||||
case "time.Duration": return TimeDuration(i)
|
||||
default:
|
||||
return i
|
||||
|
||||
@ -15,68 +15,60 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 将params键值对参数映射到对应的struct对象属性上,第三个参数mapping为非必需,表示自定义名称与属性名称的映射关系。
|
||||
// 将params键值对参数映射到对应的struct对象属性上,
|
||||
// 第三个参数mapping为非必需,表示自定义名称与属性名称的映射关系。
|
||||
// 需要注意:
|
||||
// 1、第二个参数应当为struct对象指针;
|
||||
// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值;
|
||||
// 3、map中的键名可以为小写,映射转换时会自动将键名首字母转为大写做匹配映射,如果无法匹配则忽略;
|
||||
func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error {
|
||||
if params == nil {
|
||||
return nil
|
||||
return errors.New("params cannot be nil")
|
||||
}
|
||||
isParamMap := true
|
||||
paramsMap := (map[string]interface{})(nil)
|
||||
// 先将参数转为 map[string]interface{} 类型
|
||||
if m, ok := params.(map[string]interface{}); ok {
|
||||
paramsMap = m
|
||||
} else {
|
||||
paramsMap = make(map[string]interface{})
|
||||
if reflect.ValueOf(params).Kind() == reflect.Map {
|
||||
ks := reflect.ValueOf(params).MapKeys()
|
||||
vs := reflect.ValueOf(params)
|
||||
for _, k := range ks {
|
||||
paramsMap[String(k.Interface())] = vs.MapIndex(k).Interface()
|
||||
}
|
||||
} else {
|
||||
isParamMap = false
|
||||
}
|
||||
if objPointer == nil {
|
||||
return errors.New("object pointer cannot be nil")
|
||||
}
|
||||
paramsMap := Map(params)
|
||||
if paramsMap == nil {
|
||||
return fmt.Errorf("invalid params: %v", params)
|
||||
}
|
||||
// struct的反射对象
|
||||
elem := reflect.Value{}
|
||||
if v, ok := objPointer.(reflect.Value); ok {
|
||||
elem = v
|
||||
} else {
|
||||
elem = reflect.ValueOf(objPointer).Elem()
|
||||
}
|
||||
// 如果给定的参数不是map类型,那么直接将参数值映射到第一个属性上
|
||||
if !isParamMap {
|
||||
if err := bindVarToStructByIndex(elem, 0, params); err != nil {
|
||||
return err
|
||||
rv := reflect.ValueOf(objPointer)
|
||||
if kind := rv.Kind(); kind != reflect.Ptr {
|
||||
return fmt.Errorf("object pointer should be type of: %v", kind)
|
||||
}
|
||||
return nil
|
||||
if !rv.IsValid() || rv.IsNil() {
|
||||
return errors.New("object pointer cannot be nil")
|
||||
}
|
||||
elem = rv.Elem()
|
||||
}
|
||||
// 已执行过转换的属性,只执行一次转换
|
||||
dmap := make(map[string]bool)
|
||||
// 已执行过转换的属性,只执行一次转换。
|
||||
// 或者是已经执行过转换检查的属性(即使不进行转换), 以便重复判断。
|
||||
doneMap := make(map[string]bool)
|
||||
// 首先按照传递的映射关系进行匹配
|
||||
if len(attrMapping) > 0 && len(attrMapping[0]) > 0 {
|
||||
for mappingk, mappingv := range attrMapping[0] {
|
||||
if v, ok := paramsMap[mappingk]; ok {
|
||||
dmap[mappingv] = true
|
||||
if err := bindVarToStructAttr(elem, mappingv, v); err != nil {
|
||||
for mapK, mapV := range attrMapping[0] {
|
||||
if v, ok := paramsMap[mapK]; ok {
|
||||
doneMap[mapV] = true
|
||||
if err := bindVarToStructAttr(elem, mapV, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 其次匹配对象定义时绑定的属性名称
|
||||
// 其次匹配对象定义时绑定的属性名称,
|
||||
// 标签映射关系map,如果有的话
|
||||
tagmap := getTagMapOfStruct(objPointer)
|
||||
for tagk, tagv := range tagmap {
|
||||
if _, ok := dmap[tagv]; ok {
|
||||
tagMap := getTagMapOfStruct(objPointer)
|
||||
for tagk, tagv := range tagMap {
|
||||
if _, ok := doneMap[tagv]; ok {
|
||||
continue
|
||||
}
|
||||
if v, ok := paramsMap[tagk]; ok {
|
||||
dmap[tagv] = true
|
||||
doneMap[tagv] = true
|
||||
if err := bindVarToStructAttr(elem, tagv, v); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -88,19 +80,19 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string
|
||||
for i := 0; i < elem.NumField(); i++ {
|
||||
attrMap[elemType.Field(i).Name] = struct{}{}
|
||||
}
|
||||
for mapk, mapv := range paramsMap {
|
||||
for mapK, mapV := range paramsMap {
|
||||
name := ""
|
||||
for _, checkName := range []string {
|
||||
gstr.UcFirst(mapk),
|
||||
gstr.ReplaceByMap(mapk, map[string]string{
|
||||
gstr.UcFirst(mapK),
|
||||
gstr.ReplaceByMap(mapK, map[string]string{
|
||||
"_" : "",
|
||||
"-" : "",
|
||||
" " : "",
|
||||
})} {
|
||||
if _, ok := dmap[checkName]; ok {
|
||||
})} {
|
||||
if _, ok := doneMap[checkName]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := tagmap[checkName]; ok {
|
||||
if _, ok := tagMap[checkName]; ok {
|
||||
continue
|
||||
}
|
||||
// 循环查找属性名称进行匹配
|
||||
@ -114,6 +106,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string
|
||||
break
|
||||
}
|
||||
}
|
||||
doneMap[checkName] = true
|
||||
if name != "" {
|
||||
break
|
||||
}
|
||||
@ -122,7 +115,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if err := bindVarToStructAttr(elem, name, mapv); err != nil {
|
||||
if err := bindVarToStructAttr(elem, name, mapV); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ func TimeDuration(i interface{}) time.Duration {
|
||||
return time.Duration(Int64(i))
|
||||
}
|
||||
|
||||
// 将变量i转换为time.Time类型
|
||||
// 将变量i转换为time.Time类型, 自动识别i为时间戳或者标准化的时间字符串。
|
||||
func GTime(i interface{}, format...string) *gtime.Time {
|
||||
s := String(i)
|
||||
if len(s) == 0 {
|
||||
|
||||
@ -109,11 +109,8 @@ func Test_Struct_Attr_Slice(t *testing.T) {
|
||||
type User struct {
|
||||
Scores []int
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
scores := []interface{}{99, 100, 60, 140}
|
||||
|
||||
// 通过map映射转换
|
||||
user := new(User)
|
||||
if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
@ -121,15 +118,6 @@ func Test_Struct_Attr_Slice(t *testing.T) {
|
||||
Scores : []int{99, 100, 60, 140},
|
||||
})
|
||||
}
|
||||
|
||||
// 通过变量映射转换,直接slice赋值
|
||||
if err := gconv.Struct(scores, user); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(user, &User{
|
||||
Scores : []int{99, 100, 60, 140},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -105,16 +105,20 @@ var (
|
||||
)
|
||||
|
||||
// 检测单条数据的规则:
|
||||
// value为需要校验的数据,可以为任意基本数据类型;
|
||||
// msgs为自定义错误信息,由于同一条数据的校验规则可能存在多条,为方便调用,参数类型支持 string/map[string]string ,允许传递多个自定义的错误信息,如果类型为string,那么中间使用"|"符号分隔多个自定义错误;
|
||||
// params参数为联合校验参数,对于需要联合校验的规则有效,如:required-*、same、different;
|
||||
func Check(value interface{}, rules string, msgs interface{}, params...map[string]interface{}) *Error {
|
||||
//
|
||||
// 1. value为需要校验的数据,可以为任意基本数据类型;
|
||||
//
|
||||
// 2. msgs为自定义错误信息,由于同一条数据的校验规则可能存在多条,为方便调用,参数类型支持 string/map/struct/*struct,
|
||||
// 允许传递多个自定义的错误信息,如果类型为string,那么中间使用"|"符号分隔多个自定义错误;
|
||||
//
|
||||
// 3. params参数为联合校验参数,支持任意的map/struct/*struct类型,对于需要联合校验的规则有效,如:required-*、same、different;
|
||||
func Check(value interface{}, rules string, msgs interface{}, params...interface{}) *Error {
|
||||
// 内部会将参数全部转换为字符串类型进行校验
|
||||
val := strings.TrimSpace(gconv.String(value))
|
||||
data := make(map[string]string)
|
||||
errorMsgs := make(map[string]string)
|
||||
if len(params) > 0 {
|
||||
for k, v := range params[0] {
|
||||
for k, v := range gconv.Map(params[0]) {
|
||||
data[k] = gconv.String(v)
|
||||
}
|
||||
}
|
||||
@ -122,11 +126,12 @@ func Check(value interface{}, rules string, msgs interface{}, params...map[strin
|
||||
msgArray := make([]string, 0)
|
||||
customMsgMap := make(map[string]string)
|
||||
switch v := msgs.(type) {
|
||||
case map[string]string:
|
||||
customMsgMap = v
|
||||
|
||||
case string:
|
||||
msgArray = strings.Split(v, "|")
|
||||
default:
|
||||
for k, v := range gconv.Map(msgs) {
|
||||
data[k] = gconv.String(v)
|
||||
}
|
||||
}
|
||||
ruleItems := strings.Split(strings.TrimSpace(rules), "|")
|
||||
// 规则项预处理, 主要解决规则中存在的"|"关键字符号
|
||||
|
||||
@ -13,7 +13,7 @@ func Hello2(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.Domain("127.0.0.1").BindHandler("/", Hello1)
|
||||
s.Domain("localhost").BindHandler("/", Hello2)
|
||||
s.Domain("localhost, local").BindHandler("/", Hello2)
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -5,19 +5,7 @@ import (
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
}
|
||||
|
||||
type RPCResponse struct {
|
||||
ID interface{} `json:"id,omitempty"`
|
||||
JsonRPC string `json:"jsonrpc"`
|
||||
Error *User `json:"error,omitempty"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var rpc RPCResponse
|
||||
fmt.Println(rpc.Error)
|
||||
fmt.Println(gconv.Map(rpc))
|
||||
t := gconv.GTime("2010-10-10 00:00:01")
|
||||
fmt.Println(t.String())
|
||||
}
|
||||
23
geg/util/gconv/gconv_map2.go
Normal file
23
geg/util/gconv/gconv_map2.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string `gconv:"-"`
|
||||
NickName string `gconv:"nickname, omitempty"`
|
||||
Pass1 string `gconv:"password1"`
|
||||
Pass2 string `gconv:"password2"`
|
||||
}
|
||||
user := User{
|
||||
Uid : 100,
|
||||
Name : "john",
|
||||
Pass1 : "123",
|
||||
Pass2 : "456",
|
||||
}
|
||||
fmt.Println(gconv.Map(user))
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.5.10"
|
||||
const VERSION = "v1.5.13"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user