mirror of
https://github.com/gogf/gf.git
synced 2025-04-05 11:18:50 +08:00
add default value from struct tag for ghttp.Request
This commit is contained in:
parent
d178102f82
commit
e06b62ecf2
@ -319,14 +319,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.CurrentTag, ",")
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) {
|
||||
return array[0], []interface{}{field.Value()}, nil
|
||||
}
|
||||
if len(where) > 0 {
|
||||
where += " "
|
||||
}
|
||||
where += field.CurrentTag + "=?"
|
||||
where += field.TagValue + "=?"
|
||||
args = append(args, field.Value())
|
||||
}
|
||||
return
|
||||
@ -340,7 +340,7 @@ func GetPrimaryKey(pointer interface{}) (string, error) {
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.CurrentTag, ",")
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY {
|
||||
return array[0], nil
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ import (
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
// Retrieved tag name. There might be more than one tags in the field,
|
||||
// Retrieved tag value. There might be more than one tags in the field,
|
||||
// but only one can be retrieved according to calling function rules.
|
||||
CurrentTag string
|
||||
TagValue string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
|
@ -24,8 +24,8 @@ func MapField(pointer interface{}, priority []string) (map[string]*Field, error)
|
||||
for _, field := range tagFields {
|
||||
tagField := field
|
||||
tagFieldMap[field.Name()] = tagField
|
||||
if tagField.CurrentTag != "" {
|
||||
tagFieldMap[tagField.CurrentTag] = tagField
|
||||
if tagField.TagValue != "" {
|
||||
tagFieldMap[tagField.TagValue] = tagField
|
||||
}
|
||||
}
|
||||
return tagFieldMap, nil
|
||||
|
@ -20,6 +20,41 @@ func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
|
||||
return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
|
||||
}
|
||||
|
||||
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagMap := make(map[string]string, len(fields))
|
||||
for _, field := range fields {
|
||||
tagMap[field.TagValue] = field.Name()
|
||||
}
|
||||
return tagMap, nil
|
||||
}
|
||||
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagMap := make(map[string]*Field, len(fields))
|
||||
for _, field := range fields {
|
||||
tagField := field
|
||||
tagMap[field.TagValue] = tagField
|
||||
}
|
||||
return tagMap, nil
|
||||
}
|
||||
|
||||
func getFieldValues(value interface{}) ([]*Field, error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
@ -81,7 +116,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
tagName = ""
|
||||
tagValue = ""
|
||||
tagFields = make([]*Field, 0)
|
||||
)
|
||||
for _, field := range fields {
|
||||
@ -89,20 +124,20 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
tagName = ""
|
||||
tagValue = ""
|
||||
for _, p := range priority {
|
||||
tagName = field.Tag(p)
|
||||
if tagName != "" && tagName != "-" {
|
||||
tagValue = field.Tag(p)
|
||||
if tagValue != "" && tagValue != "-" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if tagName != "" {
|
||||
if tagValue != "" {
|
||||
// Filter repeated tag.
|
||||
if _, ok := tagMap[tagName]; ok {
|
||||
if _, ok := tagMap[tagValue]; ok {
|
||||
continue
|
||||
}
|
||||
tagField := field
|
||||
tagField.CurrentTag = tagName
|
||||
tagField.TagValue = tagValue
|
||||
tagFields = append(tagFields, tagField)
|
||||
}
|
||||
// If this is an embedded attribute, it retrieves the tags recursively.
|
||||
@ -116,38 +151,3 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
|
||||
}
|
||||
return tagFields, nil
|
||||
}
|
||||
|
||||
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagMap := make(map[string]string, len(fields))
|
||||
for _, field := range fields {
|
||||
tagMap[field.CurrentTag] = field.Name()
|
||||
}
|
||||
return tagMap, nil
|
||||
}
|
||||
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagMap := make(map[string]*Field, len(fields))
|
||||
for _, field := range fields {
|
||||
tagField := field
|
||||
tagMap[field.CurrentTag] = tagField
|
||||
}
|
||||
return tagMap, nil
|
||||
}
|
||||
|
35
internal/structs/structs_z_bench_test.go
Normal file
35
internal/structs/structs_z_bench_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2020 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 structs_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `params:"name"`
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
|
||||
var (
|
||||
user = new(User)
|
||||
userNilPointer *User
|
||||
)
|
||||
|
||||
func Benchmark_TagFields(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
structs.TagFields(user, []string{"params", "my-tag1"})
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_TagFields_NilPointer(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
structs.TagFields(&userNilPointer, []string{"params", "my-tag1"})
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -79,13 +78,13 @@ func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
|
||||
// RemoveSymbols removes all symbols from string and lefts only numbers and letters.
|
||||
func RemoveSymbols(s string) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
var b []byte
|
||||
for _, c := range s {
|
||||
if (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') {
|
||||
buffer.WriteByte(byte(c))
|
||||
b = append(b, byte(c))
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// EqualFoldWithoutChars checks string <s1> and <s2> equal case-insensitively,
|
||||
|
@ -6,3 +6,153 @@
|
||||
|
||||
// Package ghttp provides powerful http server and simple client implements.
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
// Server wraps the http.Server and provides more feature.
|
||||
Server struct {
|
||||
name string // Unique name for instance management.
|
||||
config ServerConfig // Configuration.
|
||||
plugins []Plugin // Plugin array.
|
||||
servers []*gracefulServer // Underlying http.Server array.
|
||||
serverCount *gtype.Int // Underlying http.Server count.
|
||||
closeChan chan struct{} // Used for underlying server closing event notification.
|
||||
serveTree map[string]interface{} // The route map tree.
|
||||
serveCache *gcache.Cache // Server cache for internal usage.
|
||||
routesMap map[string][]registeredRouteItem // Route map mainly for route dumps and repeated route checks.
|
||||
statusHandlerMap map[string]HandlerFunc // Custom status handler map.
|
||||
sessionManager *gsession.Manager // Session manager.
|
||||
}
|
||||
|
||||
// Router object.
|
||||
Router struct {
|
||||
Uri string // URI.
|
||||
Method string // HTTP method
|
||||
Domain string // Bound domain.
|
||||
RegRule string // Parsed regular expression for route matching.
|
||||
RegNames []string // Parsed router parameter names.
|
||||
Priority int // Just for reference.
|
||||
}
|
||||
|
||||
// Router item just for route dumps.
|
||||
RouterItem struct {
|
||||
Server string // Server name.
|
||||
Address string // Listening address.
|
||||
Domain string // Bound domain.
|
||||
Type int // Router type.
|
||||
Middleware string // Bound middleware.
|
||||
Method string // Handler method name.
|
||||
Route string // Route URI.
|
||||
Priority int // Just for reference.
|
||||
IsServiceHandler bool // Is service handler.
|
||||
handler *handlerItem // The handler.
|
||||
}
|
||||
|
||||
// handlerItem is the registered handler for route handling,
|
||||
// including middleware and hook functions.
|
||||
handlerItem struct {
|
||||
itemId int // Unique handler item id mark.
|
||||
itemName string // Handler name, which is automatically retrieved from runtime stack when registered.
|
||||
itemType int // Handler type: object/handler/controller/middleware/hook.
|
||||
itemFunc HandlerFunc // Handler address.
|
||||
initFunc HandlerFunc // Initialization function when request enters the object(only available for object register type).
|
||||
shutFunc HandlerFunc // Shutdown function when request leaves out the object(only available for object register type).
|
||||
middleware []HandlerFunc // Bound middleware array.
|
||||
ctrlInfo *handlerController // Controller information for reflect usage.
|
||||
hookName string // Hook type name.
|
||||
router *Router // Router object.
|
||||
source string // Source file path:line when registering.
|
||||
}
|
||||
|
||||
// handlerParsedItem is the item parsed from URL.Path.
|
||||
handlerParsedItem struct {
|
||||
handler *handlerItem // Handler information.
|
||||
values map[string]string // Router values parsed from URL.Path.
|
||||
}
|
||||
|
||||
// handlerController is the controller information used for reflect.
|
||||
handlerController struct {
|
||||
name string // Handler method name.
|
||||
reflect reflect.Type // Reflect type of the controller.
|
||||
}
|
||||
|
||||
// registeredRouteItem stores the information of the router and is used for route map.
|
||||
registeredRouteItem struct {
|
||||
source string // Source file path and its line number.
|
||||
handler *handlerItem // Handler object.
|
||||
}
|
||||
|
||||
// Request handler function.
|
||||
HandlerFunc = func(r *Request)
|
||||
|
||||
// Listening file descriptor mapping.
|
||||
// The key is either "http" or "https" and the value is its FD.
|
||||
listenerFdMap = map[string]string
|
||||
)
|
||||
|
||||
const (
|
||||
SERVER_STATUS_STOPPED = 0
|
||||
SERVER_STATUS_RUNNING = 1
|
||||
HOOK_BEFORE_SERVE = "HOOK_BEFORE_SERVE"
|
||||
HOOK_AFTER_SERVE = "HOOK_AFTER_SERVE"
|
||||
HOOK_BEFORE_OUTPUT = "HOOK_BEFORE_OUTPUT"
|
||||
HOOK_AFTER_OUTPUT = "HOOK_AFTER_OUTPUT"
|
||||
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
||||
gDEFAULT_SERVER = "default"
|
||||
gDEFAULT_DOMAIN = "default"
|
||||
gDEFAULT_METHOD = "ALL"
|
||||
gHANDLER_TYPE_HANDLER = 1
|
||||
gHANDLER_TYPE_OBJECT = 2
|
||||
gHANDLER_TYPE_CONTROLLER = 3
|
||||
gHANDLER_TYPE_MIDDLEWARE = 4
|
||||
gHANDLER_TYPE_HOOK = 5
|
||||
gEXCEPTION_EXIT = "exit"
|
||||
gEXCEPTION_EXIT_ALL = "exit_all"
|
||||
gEXCEPTION_EXIT_HOOK = "exit_hook"
|
||||
gROUTE_CACHE_DURATION = time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
// methodsMap stores all supported HTTP method,
|
||||
// it is used for quick HTTP method searching using map.
|
||||
methodsMap = make(map[string]struct{})
|
||||
|
||||
// serverMapping stores more than one server instances for current process.
|
||||
// The key is the name of the server, and the value is its instance.
|
||||
serverMapping = gmap.NewStrAnyMap(true)
|
||||
|
||||
// serverRunning marks the running server count.
|
||||
// If there no successful server running or all servers shutdown, this value is 0.
|
||||
serverRunning = gtype.NewInt()
|
||||
|
||||
// wsUpGrader is the default up-grader configuration for websocket.
|
||||
wsUpGrader = websocket.Upgrader{
|
||||
// It does not check the origin in default, the application can do it itself.
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
// allDoneChan is the event for all server have done its serving and exit.
|
||||
// It is used for process blocking purpose.
|
||||
allDoneChan = make(chan struct{}, 1000)
|
||||
|
||||
// serverProcessInitialized is used for lazy initialization for server.
|
||||
// The process can only be initialized once.
|
||||
serverProcessInitialized = gtype.NewBool()
|
||||
|
||||
// gracefulEnabled is used for graceful reload feature, which is false in default.
|
||||
gracefulEnabled = false
|
||||
|
||||
// defaultValueTags is the struct tag names for default value storing.
|
||||
defaultValueTags = []string{"d", "default"}
|
||||
)
|
||||
|
@ -97,7 +97,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
// It returns a new WebSocket object if success, or the error if failure.
|
||||
// Note that the request should be a websocket request, or it will surely fail upgrading.
|
||||
func (r *Request) WebSocket() (*WebSocket, error) {
|
||||
if conn, err := wsUpgrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
|
||||
if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
|
||||
return &WebSocket{
|
||||
conn,
|
||||
}, nil
|
||||
|
@ -281,13 +281,6 @@ func (r *Request) GetStruct(pointer interface{}, mapping ...map[string]string) e
|
||||
return r.GetRequestStruct(pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetToStruct is an alias and convenient function for GetRequestStruct.
|
||||
// See GetRequestToStruct.
|
||||
// Deprecated.
|
||||
func (r *Request) GetToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return r.GetRequestStruct(pointer, mapping...)
|
||||
}
|
||||
|
||||
// parseQuery parses query string into r.queryMap.
|
||||
func (r *Request) parseQuery() {
|
||||
if r.parsedQuery {
|
||||
|
@ -189,15 +189,12 @@ func (r *Request) GetFormMapStrVar(kvMap ...map[string]interface{}) map[string]*
|
||||
// The optional parameter <mapping> is used to specify the key to attribute mapping.
|
||||
func (r *Request) GetFormStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
r.parseForm()
|
||||
m := r.formMap
|
||||
if m == nil {
|
||||
m = map[string]interface{}{}
|
||||
data := r.formMap
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
return gconv.Struct(m, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetFormToStruct is alias of GetFormStruct. See GetFormStruct.
|
||||
// Deprecated.
|
||||
func (r *Request) GetFormToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return r.GetFormStruct(pointer, mapping...)
|
||||
if err := r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return nil
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping...)
|
||||
}
|
||||
|
@ -197,15 +197,12 @@ func (r *Request) GetQueryMapStrVar(kvMap ...map[string]interface{}) map[string]
|
||||
// attribute mapping.
|
||||
func (r *Request) GetQueryStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
r.parseQuery()
|
||||
m := r.GetQueryMap()
|
||||
if m == nil {
|
||||
m = map[string]interface{}{}
|
||||
data := r.GetQueryMap()
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
return gconv.Struct(m, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetQueryToStruct is alias of GetQueryStruct. See GetQueryStruct.
|
||||
// Deprecated.
|
||||
func (r *Request) GetQueryToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return r.GetQueryStruct(pointer, mapping...)
|
||||
if err := r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return nil
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping...)
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// GetRequest retrieves and returns the parameter named <key> passed from client and
|
||||
@ -267,15 +270,37 @@ func (r *Request) GetRequestMapStrVar(kvMap ...map[string]interface{}) map[strin
|
||||
// the parameter <pointer> is a pointer to the struct object.
|
||||
// The optional parameter <mapping> is used to specify the key to attribute mapping.
|
||||
func (r *Request) GetRequestStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
m := r.GetRequestMap()
|
||||
if m == nil {
|
||||
m = map[string]interface{}{}
|
||||
data := r.GetRequestMap()
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
return gconv.Struct(m, pointer, mapping...)
|
||||
if err := r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return nil
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetRequestToStruct is alias of GetRequestStruct. See GetRequestStruct.
|
||||
// Deprecated.
|
||||
func (r *Request) GetRequestToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return r.GetRequestStruct(pointer, mapping...)
|
||||
// mergeDefaultStructValue merges the request parameters with default values from struct tag definition.
|
||||
func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
|
||||
tagFields, err := structs.TagFields(pointer, defaultValueTags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(tagFields) > 0 {
|
||||
var (
|
||||
foundKey string
|
||||
foundValue interface{}
|
||||
)
|
||||
for _, field := range tagFields {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = field.TagValue
|
||||
} else {
|
||||
if empty.IsEmpty(foundValue) {
|
||||
data[foundKey] = field.TagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@ -22,7 +21,6 @@ import (
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/genv"
|
||||
@ -32,146 +30,9 @@ import (
|
||||
"github.com/gogf/gf/os/gtimer"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type (
|
||||
// Server wraps the http.Server and provides more feature.
|
||||
Server struct {
|
||||
name string // Unique name for instance management.
|
||||
config ServerConfig // Configuration.
|
||||
plugins []Plugin // Plugin array.
|
||||
servers []*gracefulServer // Underlying http.Server array.
|
||||
serverCount *gtype.Int // Underlying http.Server count.
|
||||
closeChan chan struct{} // Used for underlying server closing event notification.
|
||||
serveTree map[string]interface{} // The route map tree.
|
||||
serveCache *gcache.Cache // Server cache for internal usage.
|
||||
routesMap map[string][]registeredRouteItem // Route map mainly for route dumps and repeated route checks.
|
||||
statusHandlerMap map[string]HandlerFunc // Custom status handler map.
|
||||
sessionManager *gsession.Manager // Session manager.
|
||||
}
|
||||
|
||||
// Router object.
|
||||
Router struct {
|
||||
Uri string // URI.
|
||||
Method string // HTTP method
|
||||
Domain string // Bound domain.
|
||||
RegRule string // Parsed regular expression for route matching.
|
||||
RegNames []string // Parsed router parameter names.
|
||||
Priority int // Just for reference.
|
||||
}
|
||||
|
||||
// Router item just for route dumps.
|
||||
RouterItem struct {
|
||||
Server string // Server name.
|
||||
Address string // Listening address.
|
||||
Domain string // Bound domain.
|
||||
Type int // Router type.
|
||||
Middleware string // Bound middleware.
|
||||
Method string // Handler method name.
|
||||
Route string // Route URI.
|
||||
Priority int // Just for reference.
|
||||
IsServiceHandler bool // Is service handler.
|
||||
handler *handlerItem // The handler.
|
||||
}
|
||||
|
||||
// handlerItem is the registered handler for route handling,
|
||||
// including middleware and hook functions.
|
||||
handlerItem struct {
|
||||
itemId int // Unique handler item id mark.
|
||||
itemName string // Handler name, which is automatically retrieved from runtime stack when registered.
|
||||
itemType int // Handler type: object/handler/controller/middleware/hook.
|
||||
itemFunc HandlerFunc // Handler address.
|
||||
initFunc HandlerFunc // Initialization function when request enters the object(only available for object register type).
|
||||
shutFunc HandlerFunc // Shutdown function when request leaves out the object(only available for object register type).
|
||||
middleware []HandlerFunc // Bound middleware array.
|
||||
ctrlInfo *handlerController // Controller information for reflect usage.
|
||||
hookName string // Hook type name.
|
||||
router *Router // Router object.
|
||||
source string // Source file path:line when registering.
|
||||
}
|
||||
|
||||
// handlerParsedItem is the item parsed from URL.Path.
|
||||
handlerParsedItem struct {
|
||||
handler *handlerItem // Handler information.
|
||||
values map[string]string // Router values parsed from URL.Path.
|
||||
}
|
||||
|
||||
// handlerController is the controller information used for reflect.
|
||||
handlerController struct {
|
||||
name string // Handler method name.
|
||||
reflect reflect.Type // Reflect type of the controller.
|
||||
}
|
||||
|
||||
// registeredRouteItem stores the information of the router and is used for route map.
|
||||
registeredRouteItem struct {
|
||||
source string // Source file path and its line number.
|
||||
handler *handlerItem // Handler object.
|
||||
}
|
||||
|
||||
// Request handler function.
|
||||
HandlerFunc = func(r *Request)
|
||||
|
||||
// Listening file descriptor mapping.
|
||||
// The key is either "http" or "https" and the value is its FD.
|
||||
listenerFdMap = map[string]string
|
||||
)
|
||||
|
||||
const (
|
||||
SERVER_STATUS_STOPPED = 0
|
||||
SERVER_STATUS_RUNNING = 1
|
||||
HOOK_BEFORE_SERVE = "HOOK_BEFORE_SERVE"
|
||||
HOOK_AFTER_SERVE = "HOOK_AFTER_SERVE"
|
||||
HOOK_BEFORE_OUTPUT = "HOOK_BEFORE_OUTPUT"
|
||||
HOOK_AFTER_OUTPUT = "HOOK_AFTER_OUTPUT"
|
||||
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
||||
gDEFAULT_SERVER = "default"
|
||||
gDEFAULT_DOMAIN = "default"
|
||||
gDEFAULT_METHOD = "ALL"
|
||||
gHANDLER_TYPE_HANDLER = 1
|
||||
gHANDLER_TYPE_OBJECT = 2
|
||||
gHANDLER_TYPE_CONTROLLER = 3
|
||||
gHANDLER_TYPE_MIDDLEWARE = 4
|
||||
gHANDLER_TYPE_HOOK = 5
|
||||
gEXCEPTION_EXIT = "exit"
|
||||
gEXCEPTION_EXIT_ALL = "exit_all"
|
||||
gEXCEPTION_EXIT_HOOK = "exit_hook"
|
||||
gROUTE_CACHE_DURATION = time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
// methodsMap stores all supported HTTP method,
|
||||
// it is used for quick HTTP method searching using map.
|
||||
methodsMap = make(map[string]struct{})
|
||||
|
||||
// serverMapping stores more than one server instances for current process.
|
||||
// The key is the name of the server, and the value is its instance.
|
||||
serverMapping = gmap.NewStrAnyMap(true)
|
||||
|
||||
// serverRunning marks the running server count.
|
||||
// If there no successful server running or all servers shutdown, this value is 0.
|
||||
serverRunning = gtype.NewInt()
|
||||
|
||||
// wsUpgrader is the default up-grader configuration for websocket.
|
||||
wsUpgrader = websocket.Upgrader{
|
||||
// It does not check the origin in default, the application can do it itself.
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
// allDoneChan is the event for all server have done its serving and exit.
|
||||
// It is used for process blocking purpose.
|
||||
allDoneChan = make(chan struct{}, 1000)
|
||||
|
||||
// serverProcessInited is used for lazy initialization for server.
|
||||
// The process can only be initialized once.
|
||||
serverProcessInited = gtype.NewBool()
|
||||
|
||||
// gracefulEnabled is used for graceful reload feature, which is false in default.
|
||||
gracefulEnabled = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Initialize the methods map.
|
||||
for _, v := range strings.Split(HTTP_METHODS, ",") {
|
||||
@ -190,7 +51,7 @@ func SetGraceful(enabled bool) {
|
||||
|
||||
// serverProcessInit initializes some process configurations, which can only be done once.
|
||||
func serverProcessInit() {
|
||||
if !serverProcessInited.Cas(false, true) {
|
||||
if !serverProcessInitialized.Cas(false, true) {
|
||||
return
|
||||
}
|
||||
// This means it is a restart server, it should kill its parent before starting its listening,
|
||||
|
@ -40,7 +40,7 @@ func Test_Params_Parse(t *testing.T) {
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := ghttp.NewClient()
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`)
|
||||
})
|
||||
|
@ -555,3 +555,34 @@ func Test_Params_Modify(t *testing.T) {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
//func Test_Params_Parse_DefaultValueTag(t *testing.T) {
|
||||
// type T struct {
|
||||
// Name string `d:"john"`
|
||||
// Score float32 `d:"60"`
|
||||
// }
|
||||
// p, _ := ports.PopRand()
|
||||
// s := g.Server(p)
|
||||
// s.BindHandler("/parse", func(r *ghttp.Request) {
|
||||
// var t *T
|
||||
// if err := r.Parse(&t); err != nil {
|
||||
// r.Response.WriteExit(err)
|
||||
// }
|
||||
// r.Response.WriteExit(t)
|
||||
// })
|
||||
// s.SetPort(p)
|
||||
// s.SetDumpRouterMap(false)
|
||||
// s.Start()
|
||||
// defer s.Shutdown()
|
||||
//
|
||||
// time.Sleep(100 * time.Millisecond)
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
// client := g.Client()
|
||||
// client.SetPrefix(prefix)
|
||||
//
|
||||
// t.Assert(client.PostContent("/parse"), `{"Name":"john","Score":60}`)
|
||||
// t.Assert(client.PostContent("/parse", `{"name":"smith"}`), `{"Name":"smith","Score":60}`)
|
||||
// t.Assert(client.PostContent("/parse", `{"name":"smith", "score":100}`), `{"Name":"smith","Score":100}`)
|
||||
// })
|
||||
//}
|
||||
|
@ -22,13 +22,16 @@ func MapCopy(data map[string]interface{}) (copy map[string]interface{}) {
|
||||
|
||||
// MapContains checks whether map <data> contains <key>.
|
||||
func MapContains(data map[string]interface{}, key string) (ok bool) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
_, ok = data[key]
|
||||
return
|
||||
}
|
||||
|
||||
// MapDelete deletes all <keys> from map <data>.
|
||||
func MapDelete(data map[string]interface{}, keys ...string) {
|
||||
if data == nil {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
for _, key := range keys {
|
||||
@ -59,11 +62,13 @@ func MapMergeCopy(src ...map[string]interface{}) (copy map[string]interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// MapPossibleItemByKey tries to find the possible key-value pair for given key with or without
|
||||
// cases or chars '-'/'_'/'.'/' '.
|
||||
// MapPossibleItemByKey tries to find the possible key-value pair for given key ignoring cases and symbols.
|
||||
//
|
||||
// Note that this function might be of low performance.
|
||||
func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey string, foundValue interface{}) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
if v, ok := data[key]; ok {
|
||||
return key, v
|
||||
}
|
||||
@ -77,7 +82,7 @@ func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey str
|
||||
}
|
||||
|
||||
// MapContainsPossibleKey checks if the given <key> is contained in given map <data>.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
// It checks the key ignoring cases and symbols.
|
||||
//
|
||||
// Note that this function might be of low performance.
|
||||
func MapContainsPossibleKey(data map[string]interface{}, key string) bool {
|
||||
@ -87,8 +92,11 @@ func MapContainsPossibleKey(data map[string]interface{}, key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MapOmitEmpty deletes all empty values from guven map.
|
||||
// MapOmitEmpty deletes all empty values from given map.
|
||||
func MapOmitEmpty(data map[string]interface{}) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
for k, v := range data {
|
||||
if IsEmpty(v) {
|
||||
delete(data, k)
|
||||
|
@ -90,7 +90,7 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *
|
||||
fieldName := field.Name()
|
||||
// sequence tag == struct tag
|
||||
// The name here is alias of field name.
|
||||
name, rule, msg := parseSequenceTag(field.CurrentTag)
|
||||
name, rule, msg := parseSequenceTag(field.TagValue)
|
||||
if len(name) == 0 {
|
||||
name = fieldName
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user