diff --git a/TODO b/TODO index 4563052f1..0194b756a 100644 --- a/TODO +++ b/TODO @@ -6,6 +6,12 @@ ON THE WAY: 5. FAQ 6. gdb对象管理增加二级连接池特性; 7. 完善gconv类型转换功能,增加time.Time/time.Duration类型转换,并增加benchmark测试脚本 +8. 增加ghttp.Server不同状态码的自定义处理方法; + + + + + DONE: 1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换; diff --git a/g/net/ghttp/http_response.go b/g/net/ghttp/http_response.go index 78337f762..933e53b97 100644 --- a/g/net/ghttp/http_response.go +++ b/g/net/ghttp/http_response.go @@ -134,15 +134,17 @@ func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods s } // 返回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)) +func (r *Response) WriteStatus(status int, content...string) { + if len(r.buffer) == 0 { + 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(status)) + } } - r.WriteHeader(code) + r.WriteHeader(status) } // 返回location标识,引导客户端跳转 @@ -158,6 +160,13 @@ func (r *Response) Buffer() []byte { return r.buffer } +// 获取当前缓冲区中的数据大小 +func (r *Response) BufferLength() int { + r.mu.RLock() + defer r.mu.RUnlock() + return len(r.buffer) +} + // 手动设置缓冲区内容 func (r *Response) SetBuffer(buffer []byte) { r.mu.Lock() diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 400fc571b..49b1b9ed2 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -35,6 +35,7 @@ const ( type Server struct { hmmu sync.RWMutex // handler互斥锁 hhmu sync.RWMutex // hooks互斥锁 + hsmu sync.RWMutex // status handler互斥锁 hmcmu sync.RWMutex // handlerCache互斥锁 hhcmu sync.RWMutex // hooksCache互斥锁 name string // 服务名称,方便识别 @@ -42,6 +43,7 @@ type Server struct { status int8 // 当前服务器状态(0:未启动,1:运行中) methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充) handlerMap HandlerMap // 所有注册的回调函数(静态匹配) + statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配) hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配) handlerCache *gcache.Cache // 服务注册路由内存缓存 @@ -100,6 +102,7 @@ func GetServer(name...interface{}) (*Server) { name : sname, methodsMap : make(map[string]bool), handlerMap : make(HandlerMap), + statusHandlerMap : make(map[string]HandlerFunc), handlerTree : make(map[string]interface{}), hooksTree : make(map[string]interface{}), handlerCache : gcache.New(), diff --git a/g/net/ghttp/http_server_domain.go b/g/net/ghttp/http_server_domain.go index 7c9175c08..57e604e06 100644 --- a/g/net/ghttp/http_server_domain.go +++ b/g/net/ghttp/http_server_domain.go @@ -132,3 +132,17 @@ func (d *Domain)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerF } return nil } + +// 绑定指定的状态码回调函数 +func (d *Domain)BindStatusHandler(status int, handler HandlerFunc) { + for domain, _ := range d.m { + d.s.setStatusHandler(d.s.statusHandlerKey(status, domain), handler) + } +} + +// 通过map批量绑定状态码回调函数 +func (d *Domain)BindStatusHandlerByMap(handlerMap map[int]HandlerFunc) { + for k, v := range handlerMap { + d.BindStatusHandler(k, v) + } +} \ No newline at end of file diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index 61e604873..fb13f5bc3 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -58,8 +58,15 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { } // 事件 - AfterServe s.callHookHandler(request, "AfterServe") + + // 状态码注册回调函数处理 + if f := request.Server.getStatusHandler(request.Response.Status, request); f != nil { + f(request) + } + // 设置请求完成时间 request.LeaveTime = gtime.Microsecond() + // 事件 - BeforeOutput s.callHookHandler(request, "BeforeOutput") // 输出Cookie diff --git a/g/net/ghttp/http_server_hooks.go b/g/net/ghttp/http_server_hooks.go index 4e0c826e6..501e91cd4 100644 --- a/g/net/ghttp/http_server_hooks.go +++ b/g/net/ghttp/http_server_hooks.go @@ -124,7 +124,7 @@ func (s *Server) searchHookHandler(r *Request, hook string) []*hookCacheItem { s.hhmu.RLock() defer s.hhmu.RUnlock() hookItems := make([]*hookCacheItem, 0) - domains := []string{gDEFAULT_DOMAIN, r.GetHost()} + domains := []string{r.GetHost(), gDEFAULT_DOMAIN} array := strings.Split(r.URL.Path[1:], "/") for _, domain := range domains { p, ok := s.hooksTree[domain] diff --git a/g/net/ghttp/http_server_router.go b/g/net/ghttp/http_server_router.go index 680fcfaf6..ec7f11c10 100644 --- a/g/net/ghttp/http_server_router.go +++ b/g/net/ghttp/http_server_router.go @@ -177,7 +177,7 @@ func (s *Server) searchHandler(r *Request) *handlerCacheItem { func (s *Server) searchHandlerStatic(r *Request) *handlerCacheItem { s.hmmu.RLock() defer s.hmmu.RUnlock() - domains := []string{gDEFAULT_DOMAIN, r.GetHost()} + domains := []string{r.GetHost(), gDEFAULT_DOMAIN} // 首先进行静态匹配 for _, domain := range domains { if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok { diff --git a/g/net/ghttp/http_server_service.go b/g/net/ghttp/http_server_service.go index 909fc9d27..54a907b36 100644 --- a/g/net/ghttp/http_server_service.go +++ b/g/net/ghttp/http_server_service.go @@ -3,7 +3,7 @@ // 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://gitee.com/johng/gf. -// 服务注册 + hook管理. +// 服务注册. package ghttp diff --git a/g/net/ghttp/http_server_status.go b/g/net/ghttp/http_server_status.go new file mode 100644 index 000000000..8a84dea7d --- /dev/null +++ b/g/net/ghttp/http_server_status.go @@ -0,0 +1,50 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. +// 状态码回调函数注册. + +package ghttp + +import ( + "fmt" +) + +// 查询状态码回调函数 +func (s *Server)getStatusHandler(status int, r *Request) HandlerFunc { + domains := []string{r.GetHost(), gDEFAULT_DOMAIN} + s.hsmu.RLock() + defer s.hsmu.RUnlock() + for _, domain := range domains { + if f, ok := s.statusHandlerMap[s.statusHandlerKey(status, domain)]; ok { + return f + } + } + return nil +} + +// 不同状态码下的回调方法处理 +// pattern格式:domain#status +func (s *Server)setStatusHandler(pattern string, handler HandlerFunc) { + s.hsmu.Lock() + s.statusHandlerMap[pattern] = handler + s.hsmu.Unlock() +} + +// 生成状态码回调函数map存储键名 +func (s *Server)statusHandlerKey(status int, domain string) string { + return fmt.Sprintf("%s#%d", domain, status) +} + +// 绑定指定的状态码回调函数 +func (s *Server)BindStatusHandler(status int, handler HandlerFunc) { + s.setStatusHandler(s.statusHandlerKey(status, gDEFAULT_DOMAIN), handler) +} + +// 通过map批量绑定状态码回调函数 +func (s *Server)BindStatusHandlerByMap(handlerMap map[int]HandlerFunc) { + for k, v := range handlerMap { + s.BindStatusHandler(k, v) + } +} \ No newline at end of file diff --git a/geg/net/ghttp/status.go b/geg/net/ghttp/status.go new file mode 100644 index 000000000..0e823056c --- /dev/null +++ b/geg/net/ghttp/status.go @@ -0,0 +1,20 @@ +package main + +import ( + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request){ + r.Response.Writeln("halo 世界!") + r.Response.WriteStatus(404) + }) + s.BindStatusHandler(404, func(r *ghttp.Request){ + r.Response.ClearBuffer() + r.Response.Writeln("This is customized 404 page") + }) + s.SetPort(8199) + s.Run() +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index b7975600a..06a95ce71 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,8 +2,9 @@ package main import ( "fmt" + "gitee.com/johng/gf/g/os/gfile" ) func main() { - fmt.Println(int(float64(149860800000))) + fmt.Println(gfile.RealPath("./")) } \ No newline at end of file