improve Map converting feature for gconv; improve package gproc for local shell searching; improve JSON/XML response for ghttp.Response

This commit is contained in:
John
2020-01-02 19:45:41 +08:00
parent a5a0e381bd
commit 27b677b0c0
11 changed files with 233 additions and 65 deletions

View File

@ -0,0 +1,22 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
group := s.Group("/api")
group.ALL("/all", func(r *ghttp.Request) {
r.Response.Write("all")
})
group.GET("/get", func(r *ghttp.Request) {
r.Response.Write("get")
})
group.POST("/post", func(r *ghttp.Request) {
r.Response.Write("post")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,38 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
type Object struct{}
func (o *Object) Show(r *ghttp.Request) {
r.Response.Writeln("Show")
}
func (o *Object) Delete(r *ghttp.Request) {
r.Response.Writeln("REST Delete")
}
func Handler(r *ghttp.Request) {
r.Response.Writeln("Handler")
}
func HookHandler(r *ghttp.Request) {
r.Response.Writeln("HOOK Handler")
}
func main() {
s := g.Server()
obj := new(Object)
s.Group("/api").Bind([]ghttp.GroupItem{
{"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE},
{"ALL", "/handler", Handler},
{"ALL", "/obj", obj},
{"GET", "/obj/show", obj, "Show"},
{"REST", "/obj/rest", obj},
})
s.SetPort(8199)
s.Run()
}

View File

@ -5,7 +5,6 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func MiddlewareAuth(r *ghttp.Request) {
@ -24,39 +23,37 @@ func MiddlewareCORS(r *ghttp.Request) {
func MiddlewareLog(r *ghttp.Request) {
r.Middleware.Next()
glog.Println(r.Response.Status, r.URL.Path)
g.Log().Println(r.Response.Status, r.URL.Path)
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareLog)
})
s.Use(MiddlewareLog)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
g.GET("/test", func(r *ghttp.Request) {
group.GET("/test", func(r *ghttp.Request) {
r.Response.Write("test")
})
g.Group("/order", func(group *ghttp.RouterGroup) {
g.GET("/list", func(r *ghttp.Request) {
group.Group("/order", func(group *ghttp.RouterGroup) {
group.GET("/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
g.PUT("/update", func(r *ghttp.Request) {
group.PUT("/update", func(r *ghttp.Request) {
r.Response.Write("update")
})
})
g.Group("/user", func(group *ghttp.RouterGroup) {
g.GET("/info", func(r *ghttp.Request) {
group.Group("/user", func(group *ghttp.RouterGroup) {
group.GET("/info", func(r *ghttp.Request) {
r.Response.Write("info")
})
g.POST("/edit", func(r *ghttp.Request) {
group.POST("/edit", func(r *ghttp.Request) {
r.Response.Write("edit")
})
g.DELETE("/drop", func(r *ghttp.Request) {
group.DELETE("/drop", func(r *ghttp.Request) {
r.Response.Write("drop")
})
})
g.Group("/hook", func(group *ghttp.RouterGroup) {
group.Group("/hook", func(group *ghttp.RouterGroup) {
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook any")
})

View File

@ -1,9 +1,12 @@
package main
import "github.com/gogf/gf/net/ghttp"
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/user/:name", func(r *ghttp.Request) {
r.Response.Writeln(r.Router.Uri)
})

View File

@ -1,17 +1,32 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/util/gconv"
)
func main() {
s := g.Server()
s.BindHandler("/test", func(r *ghttp.Request) {
fmt.Println(r.GetBody())
r.Response.Write(r.GetBody())
})
s.SetPort(8199)
s.Run()
type Ids struct {
Id int `c:"id"`
Uid int `c:"uid"`
}
type Base struct {
Ids
CreateTime string `c:"create_time"`
}
type User struct {
Base
Passport string `c:"passport"`
Password string `c:"password"`
Nickname string `c:"nickname"`
}
user := new(User)
user.Id = 1
user.Uid = 100
user.Nickname = "John"
user.Passport = "johng"
user.Password = "123456"
user.CreateTime = "2019"
g.Dump(gconv.Map(user))
g.Dump(gconv.MapDeep(user))
}

View File

@ -7,18 +7,18 @@ import (
func main() {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
Id int `c:"id"`
Uid int `c:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
CreateTime string `c:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Passport string `c:"passport"`
Password string `c:"password"`
Nickname string `c:"nickname"`
}
user := new(User)
user.Id = 1
@ -27,5 +27,6 @@ func main() {
user.Passport = "johng"
user.Password = "123456"
user.CreateTime = "2019"
g.Dump(gconv.Map(user))
g.Dump(gconv.MapDeep(user))
}

View File

@ -103,6 +103,14 @@ func (r *Response) WriteflnExit(format string, params ...interface{}) {
// WriteJson writes <content> to the response with JSON format.
func (r *Response) WriteJson(content interface{}) error {
// If given string/[]byte, response it directly to client.
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/json")
r.Write(content)
return nil
}
// Else use json.Marshal function to encode the parameter.
if b, err := json.Marshal(content); err != nil {
return err
} else {
@ -127,6 +135,14 @@ func (r *Response) WriteJsonExit(content interface{}) error {
//
// Note that there should be a "callback" parameter in the request for JSONP format.
func (r *Response) WriteJsonP(content interface{}) error {
// If given string/[]byte, response it directly to client.
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/json")
r.Write(content)
return nil
}
// Else use json.Marshal function to encode the parameter.
if b, err := json.Marshal(content); err != nil {
return err
} else {
@ -159,6 +175,14 @@ func (r *Response) WriteJsonPExit(content interface{}) error {
// WriteXml writes <content> to the response with XML format.
func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
// If given string/[]byte, response it directly to client.
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/xml")
r.Write(content)
return nil
}
// Else use gparser.VarToXml function to encode the parameter.
if b, err := gparser.VarToXml(content, rootTag...); err != nil {
return err
} else {

View File

@ -41,3 +41,9 @@ func (s *Server) BindMiddlewareDefault(handlers ...HandlerFunc) {
})
}
}
// Use is alias of BindMiddlewareDefault.
// See BindMiddlewareDefault.
func (s *Server) Use(handlers ...HandlerFunc) {
s.BindMiddlewareDefault(handlers...)
}

View File

@ -155,6 +155,14 @@ func getShell() string {
case "windows":
return SearchBinary("cmd.exe")
default:
// Check the default binary storage path.
if gfile.Exists("/bin/bash") {
return "/bin/bash"
}
if gfile.Exists("/bin/sh") {
return "/bin/sh"
}
// Else search the env PATH.
path := SearchBinary("bash")
if path == "" {
path = SearchBinary("sh")

View File

@ -26,6 +26,19 @@ type apiMapStrAny interface {
// If <value> is a struct/*struct object, the second parameter <tags> specifies the most priority
// tags that will be detected, otherwise it detects the tags in order of: gconv, json, and then the field name.
func Map(value interface{}, tags ...string) map[string]interface{} {
return doMapConvert(value, false, tags...)
}
// MapDeep does Map function recursively, which means if the attribute of <value>
// is also a struct/*struct, calls Map function on this attribute converting it to
// a map[string]interface{} type variable.
// Also see Map.
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
return doMapConvert(value, true, tags...)
}
// doMapConvert implements the map converting.
func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} {
if value == nil {
return nil
}
@ -121,14 +134,18 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
default:
tagArray = append(tags, structTagPriority...)
}
var rtField reflect.StructField
var rvField reflect.Value
for i := 0; i < rv.NumField(); i++ {
rtField = rt.Field(i)
rvField = rv.Field(i)
// Only convert the public attributes.
fieldName := rt.Field(i).Name
fieldName := rtField.Name
if !utilstr.IsLetterUpper(fieldName[0]) {
continue
}
name = ""
fieldTag := rt.Field(i).Tag
fieldTag := rtField.Tag
for _, tag := range tagArray {
if name = fieldTag.Get(tag); name != "" {
break
@ -146,7 +163,7 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
if len(array) > 1 {
switch strings.TrimSpace(array[1]) {
case "omitempty":
if empty.IsEmpty(rv.Field(i).Interface()) {
if empty.IsEmpty(rvField.Interface()) {
continue
} else {
name = strings.TrimSpace(array[0])
@ -156,7 +173,30 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
}
}
}
m[name] = rv.Field(i).Interface()
switch rvField.Kind() {
case reflect.Ptr:
if rvField.Elem().Kind() == reflect.Struct {
if recursive {
for k, v := range doMapConvert(rvField.Interface(), recursive, tags...) {
m[k] = v
}
} else {
m[name] = doMapConvert(rvField.Interface(), recursive, tags...)
}
} else {
m[name] = rvField.Interface()
}
case reflect.Struct:
if recursive {
for k, v := range doMapConvert(rvField.Interface(), recursive, tags...) {
m[k] = v
}
} else {
m[name] = doMapConvert(rvField.Interface(), recursive, tags...)
}
default:
m[name] = rvField.Interface()
}
}
default:
return nil
@ -166,30 +206,6 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
}
}
// MapDeep does Map function recursively, which means if the attribute of <value>
// is also a struct/*struct, calls Map function on this attribute converting it to
// a map[string]interface{} type variable.
// Also see Map.
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
data := Map(value, tags...)
for key, value := range data {
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
delete(data, key)
for k, v := range MapDeep(value, tags...) {
data[k] = v
}
}
}
return data
}
// MapStrStr converts <value> to map[string]string.
// Note that there might be data copy for this map type converting.
func MapStrStr(value interface{}, tags ...string) map[string]string {

View File

@ -113,6 +113,44 @@ func Test_Map_StructWithJsonTag(t *testing.T) {
})
}
func Test_Map_StructWithCTag(t *testing.T) {
gtest.Case(t, func() {
type User struct {
Uid int
Name string
SiteUrl string `c:"-"`
NickName string `c:"nickname, omitempty"`
Pass1 string `c:"password1"`
Pass2 string `c:"password2"`
}
user1 := User{
Uid: 100,
Name: "john",
SiteUrl: "https://goframe.org",
Pass1: "123",
Pass2: "456",
}
user2 := &user1
map1 := gconv.Map(user1)
map2 := gconv.Map(user2)
gtest.Assert(map1["Uid"], 100)
gtest.Assert(map1["Name"], "john")
gtest.Assert(map1["SiteUrl"], nil)
gtest.Assert(map1["NickName"], nil)
gtest.Assert(map1["nickname"], nil)
gtest.Assert(map1["password1"], "123")
gtest.Assert(map1["password2"], "456")
gtest.Assert(map2["Uid"], 100)
gtest.Assert(map2["Name"], "john")
gtest.Assert(map2["SiteUrl"], nil)
gtest.Assert(map2["NickName"], nil)
gtest.Assert(map2["nickname"], nil)
gtest.Assert(map2["password1"], "123")
gtest.Assert(map2["password2"], "456")
})
}
func Test_Map_PrivateAttribute(t *testing.T) {
type User struct {
Id int
@ -126,18 +164,18 @@ func Test_Map_PrivateAttribute(t *testing.T) {
func Test_Map_StructInherit(t *testing.T) {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
Id int `c:"id"`
Uid int `c:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
CreateTime string `c:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Passport string `c:"passport"`
Password string `c:"password"`
Nickname string `c:"nickname"`
}
gtest.Case(t, func() {
user := new(User)