From 8bbeb186c26caf6b78ba6658b8d34f854ff3e527 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 30 Nov 2019 20:41:53 +0800 Subject: [PATCH] add Exit feature for ghttp.Response --- .example/net/ghttp/server/hooks/hooks2.go | 32 --- .example/net/ghttp/server/middleware/param.go | 36 ++++ .../server/request/request_validation.go | 42 ++-- .example/other/test.go | 29 ++- net/ghttp/ghttp_response.go | 104 --------- net/ghttp/ghttp_response_write.go | 202 ++++++++++++++++++ net/ghttp/ghttp_server_router_group.go | 24 +-- 7 files changed, 301 insertions(+), 168 deletions(-) delete mode 100644 .example/net/ghttp/server/hooks/hooks2.go create mode 100644 .example/net/ghttp/server/middleware/param.go create mode 100644 net/ghttp/ghttp_response_write.go diff --git a/.example/net/ghttp/server/hooks/hooks2.go b/.example/net/ghttp/server/hooks/hooks2.go deleted file mode 100644 index 400128d65..000000000 --- a/.example/net/ghttp/server/hooks/hooks2.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" -) - -// 优先调用的HOOK -func beforeServeHook1(r *ghttp.Request) { - r.SetParam("name", "GoFrame") - r.Response.Writeln("set name") -} - -// 随后调用的HOOK -func beforeServeHook2(r *ghttp.Request) { - r.SetParam("site", "https://goframe.org") - r.Response.Writeln("set site") -} - -// 允许对同一个路由同一个事件注册多个回调函数,按照注册顺序进行优先级调用。 -// 为便于在路由表中对比查看优先级,这里讲HOOK回调函数单独定义为了两个函数。 -func main() { - s := g.Server() - s.BindHandler("/", func(r *ghttp.Request) { - r.Response.Writeln(r.Get("name")) - r.Response.Writeln(r.Get("site")) - }) - s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, beforeServeHook1) - s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, beforeServeHook2) - s.SetPort(8199) - s.Run() -} diff --git a/.example/net/ghttp/server/middleware/param.go b/.example/net/ghttp/server/middleware/param.go new file mode 100644 index 000000000..ece36b755 --- /dev/null +++ b/.example/net/ghttp/server/middleware/param.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +// 前置中间件1 +func MiddlewareBefore1(r *ghttp.Request) { + r.SetParam("name", "GoFrame") + r.Response.Writeln("set name") + r.Middleware.Next() +} + +// 前置中间件2 +func MiddlewareBefore2(r *ghttp.Request) { + r.SetParam("site", "https://goframe.org") + r.Response.Writeln("set site") + r.Middleware.Next() +} + +func main() { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(MiddlewareBefore1, MiddlewareBefore2) + group.ALL("/", func(r *ghttp.Request) { + r.Response.Writefln( + "%s: %s", + r.GetParamVar("name").String(), + r.GetParamVar("site").String(), + ) + }) + }) + s.SetPort(8199) + s.Run() +} diff --git a/.example/net/ghttp/server/request/request_validation.go b/.example/net/ghttp/server/request/request_validation.go index 850a63491..e1cb2b1a9 100644 --- a/.example/net/ghttp/server/request/request_validation.go +++ b/.example/net/ghttp/server/request/request_validation.go @@ -6,23 +6,35 @@ import ( "github.com/gogf/gf/util/gvalid" ) -func main() { - type User struct { - Uid int `gvalid:"uid@min:1"` - Name string `params:"username" gvalid:"username @required|length:6,30"` - Pass1 string `params:"password1" gvalid:"password1@required|password3"` - Pass2 string `params:"password2" gvalid:"password2@required|password3|same:password1#||两次密码不一致,请重新输入"` - } +type User struct { + Uid int `gvalid:"uid@min:1"` + Name string `params:"username" gvalid:"username @required|length:6,30"` + Pass1 string `params:"password1" gvalid:"password1@required|password3"` + Pass2 string `params:"password2" gvalid:"password2@required|password3|same:password1#||两次密码不一致,请重新输入"` +} +func main() { s := g.Server() - s.BindHandler("/user", func(r *ghttp.Request) { - user := new(User) - r.GetToStruct(user) - if err := gvalid.CheckStruct(user, nil); err != nil { - r.Response.WriteJson(err.Maps()) - } else { - r.Response.Write("ok") - } + s.Group("/", func(rg *ghttp.RouterGroup) { + rg.ALL("/user", func(r *ghttp.Request) { + user := new(User) + if err := r.GetToStruct(user); err != nil { + r.Response.WriteJsonExit(g.Map{ + "message": err, + "errcode": 1, + }) + } + if err := gvalid.CheckStruct(user, nil); err != nil { + r.Response.WriteJsonExit(g.Map{ + "message": err.Maps(), + "errcode": 1, + }) + } + r.Response.WriteJsonExit(g.Map{ + "message": "ok", + "errcode": 0, + }) + }) }) s.SetPort(8199) s.Run() diff --git a/.example/other/test.go b/.example/other/test.go index e4a7976a6..5ca4e66de 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,17 +1,36 @@ package main import ( - "fmt" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" ) +// 前置中间件1 +func MiddlewareBefore1(r *ghttp.Request) { + r.SetParam("name", "GoFrame") + r.Response.Writeln("set name") + r.Middleware.Next() +} + +// 前置中间件2 +func MiddlewareBefore2(r *ghttp.Request) { + r.SetParam("site", "https://goframe.org") + r.Response.Writeln("set site") + r.Middleware.Next() +} + func main() { s := g.Server() - s.BindHandler("/", func(r *ghttp.Request) { - fmt.Println(r.GetPostMap()) - r.Response.Write("ok") + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(MiddlewareBefore1, MiddlewareBefore2) + group.ALL("/user", func(r *ghttp.Request) { + r.Response.Writefln( + "%s: %s", + r.GetParamVar("name").String(), + r.GetParamVar("site").String(), + ) + }) }) - s.SetPort(8999) + s.SetPort(8199) s.Run() } diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 16ba4a4f5..88d05e56d 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -9,15 +9,12 @@ package ghttp import ( "bytes" - "encoding/json" "fmt" "net/http" "github.com/gogf/gf/os/gres" - "github.com/gogf/gf/encoding/gparser" "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/util/gconv" ) // Response is the http response manager. @@ -42,107 +39,6 @@ func newResponse(s *Server, w http.ResponseWriter) *Response { return r } -// Write writes to the response buffer. -func (r *Response) Write(content ...interface{}) { - if r.hijacked || len(content) == 0 { - return - } - if r.Status == 0 { - r.Status = http.StatusOK - } - for _, v := range content { - switch value := v.(type) { - case []byte: - r.buffer.Write(value) - case string: - r.buffer.WriteString(value) - default: - r.buffer.WriteString(gconv.String(v)) - } - } -} - -// WriteOver overwrites the response buffer with . -func (r *Response) WriteOver(content ...interface{}) { - r.ClearBuffer() - r.Write(content...) -} - -// Writef writes the response with fmt.Sprintf. -func (r *Response) Writef(format string, params ...interface{}) { - r.Write(fmt.Sprintf(format, params...)) -} - -// Writef writes the response with and new line. -func (r *Response) Writeln(content ...interface{}) { - if len(content) == 0 { - r.Write("\n") - return - } - r.Write(append(content, "\n")...) -} - -// Writefln writes the response with fmt.Sprintf and new line. -func (r *Response) Writefln(format string, params ...interface{}) { - r.Writeln(fmt.Sprintf(format, params...)) -} - -// WriteJson writes to the response with JSON format. -func (r *Response) WriteJson(content interface{}) error { - if b, err := json.Marshal(content); err != nil { - return err - } else { - r.Header().Set("Content-Type", "application/json") - r.Write(b) - } - return nil -} - -// WriteJson writes to the response with JSONP format. -// Note that there should be a "callback" parameter in the request for JSONP format. -func (r *Response) WriteJsonP(content interface{}) error { - if b, err := json.Marshal(content); err != nil { - return err - } else { - //r.Header().Set("Content-Type", "application/json") - if callback := r.Request.GetString("callback"); callback != "" { - buffer := []byte(callback) - buffer = append(buffer, byte('(')) - buffer = append(buffer, b...) - buffer = append(buffer, byte(')')) - r.Write(buffer) - } else { - r.Write(b) - } - } - return nil -} - -// WriteJson writes to the response with XML format. -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 -} - -// WriteStatus writes HTTP and to the response. -func (r *Response) WriteStatus(status int, content ...interface{}) { - r.WriteHeader(status) - if len(content) > 0 { - r.Write(content...) - } else { - r.Write(http.StatusText(status)) - } - if r.Header().Get("Content-Type") == "" { - r.Header().Set("Content-Type", "text/plain; charset=utf-8") - //r.Header().Set("X-Content-Type-Options", "nosniff") - } -} - // ServeFile serves the file to the response. func (r *Response) ServeFile(path string, allowIndex ...bool) { serveFile := (*staticServeFile)(nil) diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go new file mode 100644 index 000000000..6cd53160b --- /dev/null +++ b/net/ghttp/ghttp_response_write.go @@ -0,0 +1,202 @@ +// Copyright 2017 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 + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gogf/gf/encoding/gparser" + "github.com/gogf/gf/util/gconv" +) + +// Write writes to the response buffer. +func (r *Response) Write(content ...interface{}) { + if r.hijacked || len(content) == 0 { + return + } + if r.Status == 0 { + r.Status = http.StatusOK + } + for _, v := range content { + switch value := v.(type) { + case []byte: + r.buffer.Write(value) + case string: + r.buffer.WriteString(value) + default: + r.buffer.WriteString(gconv.String(v)) + } + } +} + +// WriteExit writes to the response buffer and exits executing of current handler. +// The "Exit" feature is commonly used to replace usage of return statement in the handler, +// for convenience. +func (r *Response) WriteExit(content ...interface{}) { + r.Write(content...) + r.Request.Exit() +} + +// WriteOver overwrites the response buffer with . +func (r *Response) WriteOver(content ...interface{}) { + r.ClearBuffer() + r.Write(content...) +} + +// WriteOverExit overwrites the response buffer with and exits executing +// of current handler. The "Exit" feature is commonly used to replace usage of return +// statement in the handler, for convenience. +func (r *Response) WriteOverExit(content ...interface{}) { + r.WriteOver(content...) + r.Request.Exit() +} + +// Writef writes the response with fmt.Sprintf. +func (r *Response) Writef(format string, params ...interface{}) { + r.Write(fmt.Sprintf(format, params...)) +} + +// WritefExit writes the response with fmt.Sprintf and exits executing of current handler. +// The "Exit" feature is commonly used to replace usage of return statement in the handler, +// for convenience. +func (r *Response) WritefExit(format string, params ...interface{}) { + r.Writef(format, params...) + r.Request.Exit() +} + +// Writef writes the response with and new line. +func (r *Response) Writeln(content ...interface{}) { + if len(content) == 0 { + r.Write("\n") + return + } + r.Write(append(content, "\n")...) +} + +// WritelnExit writes the response with and new line and exits executing +// of current handler. The "Exit" feature is commonly used to replace usage of return +// statement in the handler, for convenience. +func (r *Response) WritelnExit(content ...interface{}) { + r.Writeln(content...) + r.Request.Exit() +} + +// Writefln writes the response with fmt.Sprintf and new line. +func (r *Response) Writefln(format string, params ...interface{}) { + r.Writeln(fmt.Sprintf(format, params...)) +} + +// WriteflnExit writes the response with fmt.Sprintf and new line and exits executing +// of current handler. The "Exit" feature is commonly used to replace usage of return +// statement in the handler, for convenience. +func (r *Response) WriteflnExit(format string, params ...interface{}) { + r.Writefln(format, params...) + r.Request.Exit() +} + +// WriteJson writes to the response with JSON format. +func (r *Response) WriteJson(content interface{}) error { + if b, err := json.Marshal(content); err != nil { + return err + } else { + r.Header().Set("Content-Type", "application/json") + r.Write(b) + } + return nil +} + +// WriteJsonExit writes to the response with JSON format and exits executing +// of current handler if success. The "Exit" feature is commonly used to replace usage of +// return statement in the handler, for convenience. +func (r *Response) WriteJsonExit(content interface{}) error { + if err := r.WriteJson(content); err != nil { + return err + } + r.Request.Exit() + return nil +} + +// WriteJson writes to the response with JSONP format. +// +// Note that there should be a "callback" parameter in the request for JSONP format. +func (r *Response) WriteJsonP(content interface{}) error { + if b, err := json.Marshal(content); err != nil { + return err + } else { + //r.Header().Set("Content-Type", "application/json") + if callback := r.Request.GetString("callback"); callback != "" { + buffer := []byte(callback) + buffer = append(buffer, byte('(')) + buffer = append(buffer, b...) + buffer = append(buffer, byte(')')) + r.Write(buffer) + } else { + r.Write(b) + } + } + return nil +} + +// WriteJsonPExit writes to the response with JSONP format and exits executing +// of current handler if success. The "Exit" feature is commonly used to replace usage of +// return statement in the handler, for convenience. +// +// Note that there should be a "callback" parameter in the request for JSONP format. +func (r *Response) WriteJsonPExit(content interface{}) error { + if err := r.WriteJsonP(content); err != nil { + return err + } + r.Request.Exit() + return nil +} + +// WriteXml writes to the response with XML format. +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 +} + +// WriteXmlExit writes to the response with XML format and exits executing +// of current handler if success. The "Exit" feature is commonly used to replace usage +// of return statement in the handler, for convenience. +func (r *Response) WriteXmlExit(content interface{}, rootTag ...string) error { + if err := r.WriteXml(content, rootTag...); err != nil { + return err + } + r.Request.Exit() + return nil +} + +// WriteStatus writes HTTP and to the response. +func (r *Response) WriteStatus(status int, content ...interface{}) { + r.WriteHeader(status) + if len(content) > 0 { + r.Write(content...) + } else { + r.Write(http.StatusText(status)) + } + if r.Header().Get("Content-Type") == "" { + r.Header().Set("Content-Type", "text/plain; charset=utf-8") + //r.Header().Set("X-Content-Type-Options", "nosniff") + } +} + +// WriteStatusExit writes HTTP and to the response and exits executing +// of current handler if success. The "Exit" feature is commonly used to replace usage of +// return statement in the handler, for convenience. +func (r *Response) WriteStatusExit(status int, content ...interface{}) { + r.WriteStatus(status, content...) + r.Request.Exit() +} diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 3a1f2cab9..a7b53613c 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -55,51 +55,51 @@ func (s *Server) handlePreBindItems() { } // 获取分组路由对象 -func (s *Server) Group(prefix string, groups ...func(g *RouterGroup)) *RouterGroup { +func (s *Server) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { if len(prefix) > 0 && prefix[0] != '/' { prefix = "/" + prefix } if prefix == "/" { prefix = "" } - group := &RouterGroup{ + rg := &RouterGroup{ server: s, prefix: prefix, } if len(groups) > 0 { for _, v := range groups { - v(group) + v(rg) } } - return group + return rg } // 获取分组路由对象(绑定域名) -func (d *Domain) Group(prefix string, groups ...func(g *RouterGroup)) *RouterGroup { +func (d *Domain) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { if len(prefix) > 0 && prefix[0] != '/' { prefix = "/" + prefix } if prefix == "/" { prefix = "" } - group := &RouterGroup{ + rg := &RouterGroup{ domain: d, prefix: prefix, } if len(groups) > 0 { for _, v := range groups { - v(group) + v(rg) } } - return group + return rg } // 层级递归创建分组路由注册项 -func (g *RouterGroup) Group(prefix string, groups ...func(g *RouterGroup)) *RouterGroup { +func (g *RouterGroup) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { if prefix == "/" { prefix = "" } - group := &RouterGroup{ + rg := &RouterGroup{ parent: g, server: g.server, domain: g.domain, @@ -107,10 +107,10 @@ func (g *RouterGroup) Group(prefix string, groups ...func(g *RouterGroup)) *Rout } if len(groups) > 0 { for _, v := range groups { - v(group) + v(rg) } } - return group + return rg } func (g *RouterGroup) Clone() *RouterGroup {