diff --git a/TODO b/TODO index ad6504006..1e81851e8 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; -2. gdb Where方法参数的改进,研究是否可以将string参数类型修改为interfaceP{; -3. 增加对于数据表Model的封装; -4. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作; -5. ghttp.Response对象完善并改进数据返回方法(Write/WriteString); \ No newline at end of file +1. gdb Where方法参数的改进,研究是否可以将string参数类型修改为interfaceP{; +2. 增加对于数据表Model的封装; +3. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作; \ No newline at end of file diff --git a/g/encoding/gbinary/gbinary.go b/g/encoding/gbinary/gbinary.go index d3389ac5a..42013644d 100644 --- a/g/encoding/gbinary/gbinary.go +++ b/g/encoding/gbinary/gbinary.go @@ -8,33 +8,43 @@ package gbinary import ( + "fmt" + "math" "bytes" "encoding/binary" - "math" ) // 二进制位(0|1) type Bit int8 // 针对基本类型进行二进制打包,支持的基本数据类型包括:int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte -func Encode(vs ...interface{}) ([]byte, error) { +// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理 +func Encode(vs ...interface{}) []byte { buf := new(bytes.Buffer) for i := 0; i < len(vs); i++ { - var err error = nil - switch vs[i].(type) { - case int: buf.Write(EncodeInt(vs[i].(int))) - case uint: buf.Write(EncodeUint(vs[i].(uint))) - case bool: buf.Write(EncodeBool(vs[i].(bool))) - case string: buf.Write(EncodeString(vs[i].(string))) - case []byte: buf.Write(vs[i].([]byte)) + switch value := vs[i].(type) { + case int: buf.Write(EncodeInt(value)) + case int8: buf.Write(EncodeInt8(value)) + case int16: buf.Write(EncodeInt16(value)) + case int32: buf.Write(EncodeInt32(value)) + case int64: buf.Write(EncodeInt64(value)) + case uint: buf.Write(EncodeUint(value)) + case uint8: buf.Write(EncodeUint8(value)) + case uint16: buf.Write(EncodeUint16(value)) + case uint32: buf.Write(EncodeUint32(value)) + case uint64: buf.Write(EncodeUint64(value)) + case bool: buf.Write(EncodeBool(value)) + case string: buf.Write(EncodeString(value)) + case []byte: buf.Write(value) + case float32: buf.Write(EncodeFloat32(value)) + case float64: buf.Write(EncodeFloat64(value)) default: - err = binary.Write(buf, binary.LittleEndian, vs[i]) - } - if err != nil { - return nil, err + if err := binary.Write(buf, binary.LittleEndian, value); err != nil { + buf.Write(EncodeString(fmt.Sprintf("%v", value))) + } } } - return buf.Bytes(), nil + return buf.Bytes() } // 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度, diff --git a/g/net/ghttp/http_request.go b/g/net/ghttp/http_request.go index f09812c93..dfb4da72a 100644 --- a/g/net/ghttp/http_request.go +++ b/g/net/ghttp/http_request.go @@ -29,7 +29,7 @@ type Request struct { // 创建一个Request对象 func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { - return &Request{ + request := &Request{ parsedGet : gtype.NewBool(), parsedPost : gtype.NewBool(), values : make(map[string][]string), @@ -40,6 +40,10 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { ResponseWriter : w, }, } + // 会话处理 + request.Cookie = GetCookie(request) + request.Session = GetSession(request) + return request } diff --git a/g/net/ghttp/http_response.go b/g/net/ghttp/http_response.go index 5b4c61fdc..d3046a273 100644 --- a/g/net/ghttp/http_response.go +++ b/g/net/ghttp/http_response.go @@ -10,6 +10,8 @@ package ghttp import ( "sync" "net/http" + "gitee.com/johng/gf/g/util/gconv" + "gitee.com/johng/gf/g/encoding/gparser" ) // 服务端请求返回对象 @@ -19,39 +21,52 @@ type Response struct { buffer []byte // 每个请求的返回数据缓冲区 } -// 返回的固定JSON数据结构 -//type ResponseJson struct { -// Result int `json:"result"` // 标识消息状态 -// Message string `json:"message"` // 消息使用string存储 -// Data []byte `json:"data"` // 二进制数据(不管什么数据结构) -//} - -// 返回信息(byte) -func (r *Response) Write(content []byte) { +// 返回信息,任何变量自动转换为bytes +func (r *Response) Write(content interface{}) { r.bufmu.Lock() - defer r.bufmu.Unlock() - r.buffer = append(r.buffer, content...) + r.buffer = append(r.buffer, gconv.Bytes(content)...) + r.bufmu.Unlock() } -// 返回信息(string) -func (r *Response) WriteString(content string) { - r.bufmu.Lock() - defer r.bufmu.Unlock() - r.buffer = append(r.buffer, content...) +// 返回JSON +func (r *Response) WriteJson(content interface{}) error { + if b, err := gparser.VarToJson(content); err != nil { + return err + } else { + r.Header().Set("Content-Type", "application/json") + r.Write(b) + } + return nil } -// 返回固定格式的json -//func (r *Response) WriteJson(result int, message string, data []byte) error { -// r.Header().Set("Content-Type", "application/json") -// r.bufmu.Lock() -// defer r.bufmu.Unlock() -// if jsonstr, err := gjson.Encode(ResponseJson{ result, message, data }); err != nil { -// return err -// } else { -// r.buffer = append(r.buffer, jsonstr...) -// } -// return nil -//} +// 返回XML +func (r *Response) WriteXml(content interface{}, rootTag...string) error { + if b, err := gparser.VarToXml(content, rootTag...); err != nil { + return err + } else { + r.Header().Set("Content-Type", "application/xml") + r.Write(b) + } + return nil +} + +// 返回HTTP Code状态码 +func (r *Response) WriteStatus(code int, content...string) { + r.Header().Set("Content-Type", "text/plain; charset=utf-8") + r.Header().Set("X-Content-Type-Options", "nosniff") + if len(content) > 0 { + r.Write(content[0]) + } else { + r.Write(http.StatusText(code)) + } + r.WriteHeader(code) +} + +// 返回location标识,引导客户端跳转 +func (r *Response) RedirectTo(location string) { + r.Header().Set("Location", location) + r.WriteHeader(http.StatusFound) +} // 获取当前缓冲区中的数据 func (r *Response) Buffer() []byte { @@ -60,19 +75,26 @@ func (r *Response) Buffer() []byte { return r.buffer } +// 手动设置缓冲区内容 +func (r *Response) SetBuffer(buffer []byte) { + r.bufmu.Lock() + r.buffer = buffer + r.bufmu.Unlock() +} + // 清空缓冲区内容 func (r *Response) ClearBuffer() { r.bufmu.Lock() - defer r.bufmu.Unlock() r.buffer = make([]byte, 0) + r.bufmu.Unlock() } // 输出缓冲区数据到客户端 func (r *Response) OutputBuffer() { r.bufmu.Lock() - defer r.bufmu.Unlock() if len(r.buffer) > 0 { r.ResponseWriter.Write(r.buffer) r.buffer = make([]byte, 0) } + r.bufmu.Unlock() } diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index e156f01fb..0dcbc4fc7 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -30,22 +30,33 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) { // 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置; // 最后,如果以上都没有找到处理接口,那么进行文件处理; func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { + // 创建请求处理对象 request := newRequest(s, r, w) + // 事件 - BeforeServe + s.callHookHandler(request, "BeforeServe") if h := s.getHandler(request); h != nil { s.callHandler(h, request) } else { - s.serveFile(w, r) + s.serveFile(request) } + // 事件 - AfterServe + s.callHookHandler(request, "AfterServe") + // 事件 - BeforeOutput + s.callHookHandler(request, "BeforeOutput") + // 输出Cookie + request.Cookie.Output() + // 输出缓冲区 + request.Response.OutputBuffer() + // 事件 - AfterOutput + s.callHookHandler(request, "AfterOutput") + // 将Request对象指针丢到队列中异步处理 + s.closeQueue.PushBack(request) } // 初始化控制器 func (s *Server)callHandler(h *HandlerItem, r *Request) { - // 会话处理 - r.Cookie = GetCookie(r) - r.Session = GetSession(r) - // 请求处理 - s.callHookHandler(r, "BeforeServe") + if h.faddr == nil { // 新建一个控制器对象处理请求 c := reflect.New(h.ctype) @@ -55,23 +66,11 @@ func (s *Server)callHandler(h *HandlerItem, r *Request) { } else { h.faddr(r) } - s.callHookHandler(r, "AfterServe") - s.callHookHandler(r, "BeforeOutput") - - // 输出Cookie - r.Cookie.Output() - // 输出缓冲区 - r.Response.OutputBuffer() - - s.callHookHandler(r, "AfterOutput") - - // 将Request对象指针丢到队列中异步处理 - s.closeQueue.PushBack(r) } // 处理静态文件请求 -func (s *Server)serveFile(w http.ResponseWriter, r *http.Request) { +func (s *Server)serveFile(r *Request) { uri := r.URL.String() if s.config.ServerRoot != "" { // 获取文件的绝对路径 @@ -79,17 +78,17 @@ func (s *Server)serveFile(w http.ResponseWriter, r *http.Request) { path = path + uri path = gfile.RealPath(path) if path != "" { - s.doServeFile(w, r, path) + s.doServeFile(r, path) } else { - s.NotFound(w, r) + r.Response.WriteStatus(http.StatusNotFound) } } else { - s.NotFound(w, r) + r.Response.WriteStatus(http.StatusNotFound) } } // http server静态文件处理 -func (s *Server)doServeFile(w http.ResponseWriter, r *http.Request, path string) { +func (s *Server)doServeFile(r *Request, path string) { f, err := os.Open(path) if err != nil { return @@ -101,53 +100,41 @@ func (s *Server)doServeFile(w http.ResponseWriter, r *http.Request, path string) fpath := path + "/" + file if gfile.Exists(fpath) { f.Close() - s.doServeFile(w, r, fpath) + s.doServeFile(r, fpath) return } } } if s.config.IndexFolder { - s.listDir(w, f) + s.listDir(r, f) } else { - s.ResponseStatus(w, http.StatusForbidden) + r.Response.WriteStatus(http.StatusForbidden) } } else { - http.ServeContent(w, r, info.Name(), info.ModTime(), f) + // 读取文件内容返回 + http.ServeContent(r.Response.ResponseWriter, &r.Request, info.Name(), info.ModTime(), f) } f.Close() } // 目录列表 -func (s *Server)listDir(w http.ResponseWriter, f http.File) { +func (s *Server)listDir(r *Request, f http.File) { dirs, err := f.Readdir(-1) if err != nil { - http.Error(w, "Error reading directory", http.StatusInternalServerError) + r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory") return } sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) - w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprintf(w, "
\n")
+    r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
+    r.Response.Write("
\n")
     for _, d := range dirs {
         name := d.Name()
         if d.IsDir() {
             name += "/"
         }
         u := url.URL{Path: name}
-        fmt.Fprintf(w, "%s\n", u.String(), ghtml.SpecialChars(name))
+        r.Response.Write(fmt.Sprintf("%s\n", u.String(), ghtml.SpecialChars(name)))
     }
-    fmt.Fprintf(w, "
\n") + r.Response.Write("
\n") } - -// 返回http状态码,并使用默认配置的字符串返回信息 -func (s *Server)ResponseStatus(w http.ResponseWriter, code int) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(code) - w.Write([]byte(http.StatusText(code))) -} - -// 404 -func (s *Server)NotFound(w http.ResponseWriter, r *http.Request) { - http.NotFound(w, r) -} \ No newline at end of file diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go index bfa110122..e181127a0 100644 --- a/g/util/gconv/gconv.go +++ b/g/util/gconv/gconv.go @@ -5,13 +5,12 @@ // You can obtain one at https://gitee.com/johng/gf. // 类型转换. -// 如果给定的interface{}参数不是指定转换的输出类型,那么会进行强制转换,效率会比较低, -// 建议已知类型的转换自行调用相关方法来单独处理。 +// 内部使用了bytes作为底层转换类型,效率很高。 package gconv import ( "fmt" - "strconv" + "gitee.com/johng/gf/g/encoding/gbinary" ) func Bytes(i interface{}) []byte { @@ -21,10 +20,11 @@ func Bytes(i interface{}) []byte { if r, ok := i.([]byte); ok { return r } else { - return []byte(String(i)) + return gbinary.Encode(i) } } +// 基础的字符串类型转换 func String(i interface{}) string { if i == nil { return "" @@ -32,7 +32,7 @@ func String(i interface{}) string { if r, ok := i.(string); ok { return r } else { - return fmt.Sprintf("%v", i) + return string(Bytes(i)) } } @@ -73,19 +73,97 @@ func Int(i interface{}) int { if v, ok := i.(int); ok { return v } - v, _ := strconv.Atoi(String(i)) - return v + return gbinary.DecodeToInt(Bytes(i)) } -func Uint (i interface{}) uint { +func Int8(i interface{}) int8 { + if i == nil { + return 0 + } + if v, ok := i.(int8); ok { + return v + } + return gbinary.DecodeToInt8(Bytes(i)) +} + +func Int16(i interface{}) int16 { + if i == nil { + return 0 + } + if v, ok := i.(int16); ok { + return v + } + return gbinary.DecodeToInt16(Bytes(i)) +} + +func Int32(i interface{}) int32 { + if i == nil { + return 0 + } + if v, ok := i.(int32); ok { + return v + } + return gbinary.DecodeToInt32(Bytes(i)) +} + +func Int64(i interface{}) int64 { + if i == nil { + return 0 + } + if v, ok := i.(int64); ok { + return v + } + return gbinary.DecodeToInt64(Bytes(i)) +} + +func Uint(i interface{}) uint { if i == nil { return 0 } if v, ok := i.(uint); ok { return v } - v, _ := strconv.ParseUint(String(i), 10, 8) - return uint(v) + return gbinary.DecodeToUint(Bytes(i)) +} + +func Uint8(i interface{}) uint8 { + if i == nil { + return 0 + } + if v, ok := i.(uint8); ok { + return v + } + return gbinary.DecodeToUint8(Bytes(i)) +} + +func Uint16(i interface{}) uint16 { + if i == nil { + return 0 + } + if v, ok := i.(uint16); ok { + return v + } + return gbinary.DecodeToUint16(Bytes(i)) +} + +func Uint32(i interface{}) uint32 { + if i == nil { + return 0 + } + if v, ok := i.(uint32); ok { + return v + } + return gbinary.DecodeToUint32(Bytes(i)) +} + +func Uint64(i interface{}) uint64 { + if i == nil { + return 0 + } + if v, ok := i.(uint64); ok { + return v + } + return gbinary.DecodeToUint64(Bytes(i)) } func Float32 (i interface{}) float32 { @@ -95,8 +173,7 @@ func Float32 (i interface{}) float32 { if v, ok := i.(float32); ok { return v } - v, _ := strconv.ParseFloat(String(i), 8) - return float32(v) + return gbinary.DecodeToFloat32(Bytes(i)) } func Float64 (i interface{}) float64 { @@ -106,6 +183,5 @@ func Float64 (i interface{}) float64 { if v, ok := i.(float64); ok { return v } - v, _ := strconv.ParseFloat(String(i), 8) - return v + return gbinary.DecodeToFloat64(Bytes(i)) } diff --git a/geg/net/ghttp/hello.go b/geg/net/ghttp/hello.go index 5cb869e97..fc77cffbd 100644 --- a/geg/net/ghttp/hello.go +++ b/geg/net/ghttp/hello.go @@ -1,24 +1,28 @@ package main -import "gitee.com/johng/gf/g/net/ghttp" +import ( + "gitee.com/johng/gf/g/net/ghttp" +) func main() { ghttp.GetServer().BindHandler("get:/h", func(r *ghttp.Request) { - r.Response.WriteString("hello world") + //r.Response.RedirectTo("http://www.baidu.com/") + r.Response.Write("hello world") + //r.Response.WriteStatus(302) }) ghttp.GetServer().BindHandler("/:name/*any", func(r *ghttp.Request) { - r.Response.WriteString("any") - r.Response.WriteString(r.GetQueryString("name")) - r.Response.WriteString(r.GetQueryString("any")) + r.Response.Write("any") + r.Response.Write(r.GetQueryString("name")) + r.Response.Write(r.GetQueryString("any")) }) //ghttp.GetServer().BindHandler("/:name/action", func(r *ghttp.Request) { // r.Response.WriteString(r.GetQueryString("name")) //}) ghttp.GetServer().BindHandler("/:name/:action/:aaa", func(r *ghttp.Request) { - r.Response.WriteString("name") - r.Response.WriteString(r.GetQueryString("name")) - r.Response.WriteString(r.GetQueryString("action")) + r.Response.Write("name") + r.Response.Write(r.GetQueryString("name")) + r.Response.Write(r.GetQueryString("action")) }) ghttp.GetServer().SetPort(10000) ghttp.GetServer().Run() diff --git a/geg/other/test.go b/geg/other/test.go index b3c16412b..233ca467f 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -4,6 +4,14 @@ import ( "fmt" ) +type T struct{ + name string +} func main() { - fmt.Println(make(map[string]interface{}) == nil) + var i interface{} = T{"john"} + switch v := i.(type) { + case T: + default: + fmt.Println(v) + } } \ No newline at end of file