add middleware feature for ghttp.Server

This commit is contained in:
John
2019-08-03 15:54:12 +08:00
parent e622f6aff2
commit 0460a53f53
20 changed files with 385 additions and 386 deletions

View File

@ -0,0 +1,50 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
)
var (
tableName = "orders"
dao = g.DB().Table(tableName).Safe()
)
type OrderServiceEntity struct {
GoodsPrice float64 `json:"goods_price" gvalid:"required"`
PayTo int8 `json:"payTo" gvalid:"required"`
PayStatus int8 `json:"payStatus" `
CreateTime string `json:"createTime" `
AppId string `json:"appId" gvalid:"required"`
PayUser string `json:"pay_user" gvalid:"required"`
QrUrl string `json:"qr_url" `
}
type Create struct {
Id int64 `json:"id" gconv:"id"`
GoodsPrice float64 `json:"goodsPrice" gconv:"goods_price"`
PayTo int8 `json:"payTo" gconv:"pay_to"`
PayStatus int8 `json:"payStatus" gconv:"pay_status"`
CreateTime string `json:"createTime" gconv:"create_time"`
UserId int `json:"user_id" `
PayUser string `json:"pay_user" `
QrUrl string `json:"qr_url" `
}
func main() {
g.DB().SetDebug(true)
userInfo := Create{
Id: 3,
}
orderService := OrderServiceEntity{
GoodsPrice: 0.1,
PayTo: 1,
}
size, err := dao.Where("user_id", userInfo.Id).
And("goods_price", float64(100.10)).
And("pay_status", 0).
And("pay_to", orderService.PayTo).Count()
fmt.Println(err)
fmt.Println(size)
}

View File

@ -36,3 +36,20 @@ func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr strin
}
return
}
// 友好地调用方法
func niceCallFunc(f func()) {
defer func() {
if err := recover(); err != nil {
switch err {
case gEXCEPTION_EXIT:
fallthrough
case gEXCEPTION_EXIT_ALL:
return
default:
panic(err)
}
}
}()
f()
}

View File

@ -21,12 +21,7 @@ import (
// 请求对象
type Request struct {
*http.Request
parsedGet bool // GET参数是否已经解析
parsedPost bool // POST参数是否已经解析
queryVars map[string][]string // GET参数
routerVars map[string][]string // 路由解析参数
exit bool // 是否退出当前请求流程执行
Id int // 请求id(唯一)
Id int // 请求ID(当前Server对象唯一)
Server *Server // 请求关联的服务器对象
Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全)
Session *Session // 与当前请求绑定的Session对象(并发安全)
@ -34,6 +29,14 @@ type Request struct {
Router *Router // 匹配到的路由对象
EnterTime int64 // 请求进入时间(微秒)
LeaveTime int64 // 请求完成时间(微秒)
MiddleWare *MiddleWare // 中间件功能调用对象
handlers []*handlerParsedItem // 请求执行服务函数列表(包含中间件、路由函数、钩子函数)
handlerIndex int // 当前执行函数的索引号
parsedGet bool // GET参数是否已经解析
parsedPost bool // POST参数是否已经解析
queryVars map[string][]string // GET参数
routerVars map[string][]string // 路由解析参数
exit bool // 是否退出当前请求流程执行
params map[string]interface{} // 开发者自定义参数(请求流程中有效)
parsedHost string // 解析过后不带端口号的服务器域名名称
clientIp string // 解析过后的客户端IP地址
@ -55,6 +58,9 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
request.Cookie = GetCookie(request)
request.Session = GetSession(request)
request.Response.request = request
request.MiddleWare = &MiddleWare{
request: request,
}
return request
}

View File

@ -0,0 +1,77 @@
// 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 "reflect"
// 中间件对象
type MiddleWare struct {
request *Request
}
// 执行下一个请求流程处理函数
func (m *MiddleWare) Next() {
item := (*handlerParsedItem)(nil)
for ; m.request.handlerIndex < len(m.request.handlers); m.request.handlerIndex++ {
// 是否停止请求执行
if m.request.IsExited() {
return
}
item = m.request.handlers[m.request.handlerIndex]
// 通过中间件模式不执行钩子函数
if item.handler.itemType == gHANDLER_TYPE_HOOK {
continue
}
// 路由参数赋值
for k, v := range item.values {
m.request.routerVars[k] = v
}
m.request.Router = item.handler.router
// 执行函数处理
switch item.handler.itemType {
case gHANDLER_TYPE_CONTROLLER:
c := reflect.New(item.handler.ctrlInfo.reflect)
niceCallFunc(func() {
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(m.request)})
})
if m.request.IsExited() {
niceCallFunc(func() {
c.MethodByName(item.handler.ctrlInfo.name).Call(nil)
})
}
if m.request.IsExited() {
niceCallFunc(func() {
c.MethodByName("Shut").Call(nil)
})
}
case gHANDLER_TYPE_OBJECT:
if item.handler.initFunc != nil {
niceCallFunc(func() {
item.handler.initFunc(m.request)
})
}
if m.request.IsExited() {
niceCallFunc(func() {
item.handler.itemFunc(m.request)
})
}
if m.request.IsExited() && item.handler.shutFunc != nil {
niceCallFunc(func() {
item.handler.shutFunc(m.request)
})
}
case gHANDLER_TYPE_HANDLER:
niceCallFunc(func() {
item.handler.itemFunc(m.request)
})
case gHANDLER_TYPE_MIDDLEWARE:
niceCallFunc(func() {
item.handler.itemFunc(m.request)
})
}
}
}

View File

@ -137,7 +137,7 @@ func (r *Response) WriteStatus(status int, content ...interface{}) {
// 状态码注册回调函数处理
if status != http.StatusOK {
if f := r.request.Server.getStatusHandler(status, r.request); f != nil {
r.Server.niceCallFunc(func() {
niceCallFunc(func() {
f(r.request)
})
// 防止多次设置(http: multiple response.WriteHeader calls)

View File

@ -1,70 +0,0 @@
// 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
// 默认的gzip压缩文件类型
var defaultGzipContentTypes = []string{
"application/atom+xml",
"application/font-sfnt",
"application/javascript",
"application/json",
"application/ld+json",
"application/manifest+json",
"application/rdf+xml",
"application/rss+xml",
"application/schema+json",
"application/vnd.geo+json",
"application/vnd.ms-fontobject",
"application/x-font-ttf",
"application/x-javascript",
"application/x-web-app-manifest+json",
"application/xhtml+xml",
"application/xml",
"font/eot",
"font/opentype",
"image/bmp",
"image/svg+xml",
"image/vnd.microsoft.icon",
"image/x-icon",
"text/cache-manifest",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/vcard",
"text/vnd.rim.location.xloc",
"text/vtt",
"text/x-component",
"text/x-cross-domain-policy",
"text/xml",
}
//// 返回内容gzip检查处理
//func (r *Response) handleGzip() {
// // 如果客户端支持gzip压缩并且服务端设置开启gzip压缩特性那么执行压缩
// encoding := r.request.Header.Get("Accept-Encoding")
// if encoding != "" && strings.Contains(encoding, "gzip") {
// mimeType := ""
// ext := gfile.Ext(r.request.URL.Path)
// if ext != "" {
// mimeType = strings.Split(mime.TypeByExtension(ext), ";")[0]
// }
// if mimeType == "" {
// contentType := r.Header().Get("Content-Type")
// if contentType != "" {
// mimeType = strings.Split(contentType, ";")[0]
// }
// }
//
// if _, ok := r.Server.gzipMimesMap[mimeType]; ok {
// r.SetBuffer(gcompress.Gzip(r.buffer))
// r.Header().Set("Content-Length", gconv.String(len(r.buffer)))
// r.Header().Set("Content-Encoding", "gzip")
// }
// }
//}

View File

@ -15,7 +15,6 @@ import (
"reflect"
"runtime"
"strings"
"sync"
"time"
"github.com/gogf/gf/container/garray"
@ -44,10 +43,10 @@ type (
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)同时作为请求ID
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
middlewareTree map[string]interface{} // 所有注册的中间件(路由表,树型结构,哈希表+链表优先级匹配)
serveCache *gcache.Cache // 服务注册路由内存缓存
hooksCache *gcache.Cache // 事件回调路由内存缓存
routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
statusHandlerMu sync.RWMutex // status handler互斥锁
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
sessions *gcache.Cache // Session内存缓存
logger *glog.Logger // 日志管理对象
@ -63,16 +62,15 @@ type (
Priority int // 优先级,用于链表排序,值越大优先级越高
}
// http回调函数注册信息
// 服务函数注册信息
handlerItem struct {
name string // 注册的方法名称信息
rtype int // 注册方式(执行对象/回调函数/控制器)
ctype reflect.Type // 控制器类型(反射类型)
fname string // 回调方法名称
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
finit HandlerFunc // 初始化请求回调方法(执行对象注册方式下有效)
fshut HandlerFunc // 完成请求回调方法(执行对象注册方式下有效)
router *Router // 注册时绑定的路由对象
itemName string // 注册的函数名称信息(用于路由信息打印)
itemType int // 注册函数类型(对象/函数/控制器/中间件)
itemFunc HandlerFunc // 函数内存地址(与以上两个参数二选一)
initFunc HandlerFunc // 初始化请求回调函数(对象注册方式下有效)
shutFunc HandlerFunc // 完成请求回调函数(对象注册方式下有效)
ctrlInfo *handlerController // 控制器服务函数反射信息
router *Router // 注册时绑定的路由对象
}
// 根据特定URL.Path解析后的路由检索结果项
@ -81,6 +79,12 @@ type (
values map[string][]string // 特定URL.Path的Router解析参数
}
// 控制器服务函数反射信息
handlerController struct {
name string // 方法名称
reflect reflect.Type // 控制器类型
}
// 已注册的路由项
registeredRouteItem struct {
file string // 文件路径及行数地址
@ -98,25 +102,24 @@ type (
)
const (
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
HOOK_BEFORE_SERVE = "BeforeServe" // 回调事件,在执行服务前
HOOK_AFTER_SERVE = "AfterServe" // 回调事件,在执行服务后
HOOK_BEFORE_OUTPUT = "BeforeOutput" // 回调事件,在输出结果前
HOOK_AFTER_OUTPUT = "AfterOutput" // 回调事件,在输出结果后
HOOK_BEFORE_CLOSE = "BeforeClose" // Deprecated.
HOOK_AFTER_CLOSE = "AfterClose" // Deprecated.
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
gDEFAULT_SERVER = "default"
gDEFAULT_DOMAIN = "default"
gDEFAULT_METHOD = "ALL"
gROUTE_REGISTER_HANDLER = 1
gROUTE_REGISTER_OBJECT = 2
gROUTE_REGISTER_CONTROLLER = 3
gEXCEPTION_EXIT = "exit"
gEXCEPTION_EXIT_ALL = "exit_all"
gEXCEPTION_EXIT_HOOK = "exit_hook"
SERVER_STATUS_STOPPED = 0 // Server状态停止
SERVER_STATUS_RUNNING = 1 // Server状态运行
HOOK_BEFORE_SERVE = "BeforeServe" // 回调事件,在执行服务前
HOOK_AFTER_SERVE = "AfterServe" // 回调事件,在执行服务后
HOOK_BEFORE_OUTPUT = "BeforeOutput" // 回调事件,在输出结果前
HOOK_AFTER_OUTPUT = "AfterOutput" // 回调事件,在输出结果后
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"
)
var (
@ -240,23 +243,6 @@ func (s *Server) Start() error {
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
}
// 不允许访问的路由注册(使用HOOK实现)
// TODO 去掉HOOK的实现方式
if s.config.DenyRoutes != nil {
for _, v := range s.config.DenyRoutes {
s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) {
r.Response.WriteStatus(403)
r.ExitAll()
})
}
}
// gzip压缩文件类型
//if s.config.GzipContentTypes != nil {
// for _, v := range s.config.GzipContentTypes {
// s.gzipMimesMap[v] = struct{}{}
// }
//}
// 启动http server
reloaded := false
@ -323,7 +309,7 @@ func (s *Server) GetRouteMap() string {
domain: array[4],
method: array[2],
route: array[3],
handler: registeredItem.handler.name,
handler: registeredItem.handler.itemName,
priority: len(registeredItems) - index - 1,
}
if _, ok := m[item.domain]; !ok {
@ -347,19 +333,19 @@ func (s *Server) GetRouteMap() string {
m[item.domain].Add(item)
}
}
addr := s.config.Addr
itemFunc := s.config.Addr
if s.config.HTTPSAddr != "" {
if len(addr) > 0 {
addr += ","
if len(itemFunc) > 0 {
itemFunc += ","
}
addr += "tls" + s.config.HTTPSAddr
itemFunc += "tls" + s.config.HTTPSAddr
}
for _, a := range m {
data := make([]string, 8)
for _, v := range a.Slice() {
item := v.(*tableItem)
data[0] = s.name
data[1] = addr
data[1] = itemFunc
data[2] = item.domain
data[3] = item.method
data[4] = gconv.String(len(strings.Split(item.route, "/")) - 1 + item.priority)
@ -422,19 +408,19 @@ func (s *Server) startServer(fdMap listenerFdMap) {
continue
}
fd := 0
addr := v
itemFunc := v
array := strings.Split(v, "#")
if len(array) > 1 {
addr = array[0]
itemFunc = array[0]
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if runtime.GOOS != "windows" {
fd = gconv.Int(array[1])
}
}
if fd > 0 {
s.servers = append(s.servers, s.newGracefulServer(addr, fd))
s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
} else {
s.servers = append(s.servers, s.newGracefulServer(addr))
s.servers = append(s.servers, s.newGracefulServer(itemFunc))
}
s.servers[len(s.servers)-1].isHttps = true
}
@ -457,19 +443,19 @@ func (s *Server) startServer(fdMap listenerFdMap) {
continue
}
fd := 0
addr := v
itemFunc := v
array := strings.Split(v, "#")
if len(array) > 1 {
addr = array[0]
itemFunc = array[0]
// windows系统不支持文件描述符传递socket通信平滑交接因此只能完整重启
if runtime.GOOS != "windows" {
fd = gconv.Int(array[1])
}
}
if fd > 0 {
s.servers = append(s.servers, s.newGracefulServer(addr, fd))
s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
} else {
s.servers = append(s.servers, s.newGracefulServer(addr))
s.servers = append(s.servers, s.newGracefulServer(itemFunc))
}
}
// 开始执行异步监听
@ -523,7 +509,7 @@ func (s *Server) getListenerFdMap() map[string]string {
}
// s.servers是从HTTPS到HTTP优先级遍历解析的时候也应当按照这个顺序读取fd
for _, v := range s.servers {
str := v.addr + "#" + gconv.String(v.Fd()) + ","
str := v.itemFunc + "#" + gconv.String(v.Fd()) + ","
if v.isHttps {
m["https"] += str
} else {

View File

@ -91,33 +91,28 @@ type ServerConfig struct {
// 默认HTTP Server配置
var defaultServerConfig = ServerConfig{
Addr: "",
HTTPSAddr: "",
Handler: nil,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1024,
KeepAlive: true,
Addr: "",
HTTPSAddr: "",
Handler: nil,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 1024,
KeepAlive: true,
IndexFiles: []string{"index.html", "index.htm"},
IndexFolder: false,
ServerAgent: "gf",
ServerRoot: "",
StaticPaths: make([]staticPathItem, 0),
FileServerEnabled: false,
CookieMaxAge: gDEFAULT_COOKIE_MAX_AGE,
CookiePath: gDEFAULT_COOKIE_PATH,
CookieDomain: "",
SessionMaxAge: gDEFAULT_SESSION_MAX_AGE,
SessionIdName: gDEFAULT_SESSION_ID_NAME,
CookieMaxAge: gDEFAULT_COOKIE_MAX_AGE,
CookiePath: gDEFAULT_COOKIE_PATH,
CookieDomain: "",
SessionMaxAge: gDEFAULT_SESSION_MAX_AGE,
SessionIdName: gDEFAULT_SESSION_ID_NAME,
LogStdout: true,
ErrorLogEnabled: true,
AccessLogEnabled: false,
GzipContentTypes: defaultGzipContentTypes,
DumpRouteMap: true,
RouterCacheExpire: 60,
Rewrites: make(map[string]string),
@ -146,12 +141,12 @@ func (s *Server) SetConfig(c ServerConfig) {
}
// 设置http server参数 - Addr
func (s *Server) SetAddr(addr string) {
func (s *Server) SetAddr(itemFunc string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
s.config.Addr = addr
s.config.Addr = itemFunc
}
// 设置http server参数 - Port
@ -171,12 +166,12 @@ func (s *Server) SetPort(port ...int) {
}
// 设置http server参数 - HTTPS Addr
func (s *Server) SetHTTPSAddr(addr string) {
func (s *Server) SetHTTPSAddr(itemFunc string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
s.config.HTTPSAddr = addr
s.config.HTTPSAddr = itemFunc
}
// 设置http server参数 - HTTPS Port

View File

@ -23,7 +23,7 @@ import (
// 优雅的Web Server对象封装
type gracefulServer struct {
fd uintptr // 热重启时传递的socket监听文件句柄
addr string // 监听地址信息
itemFunc string // 监听地址信息
httpServer *http.Server // 底层http.Server
rawListener net.Listener // 原始listener
listener net.Listener // 接口化封装的listener
@ -32,10 +32,10 @@ type gracefulServer struct {
}
// 创建一个优雅的Http Server
func (s *Server) newGracefulServer(addr string, fd ...int) *gracefulServer {
func (s *Server) newGracefulServer(itemFunc string, fd ...int) *gracefulServer {
gs := &gracefulServer{
addr: addr,
httpServer: s.newHttpServer(addr),
itemFunc: itemFunc,
httpServer: s.newHttpServer(itemFunc),
}
// 是否有继承的文件描述符
if len(fd) > 0 && fd[0] > 0 {
@ -45,9 +45,9 @@ func (s *Server) newGracefulServer(addr string, fd ...int) *gracefulServer {
}
// 生成一个底层的Web Server对象
func (s *Server) newHttpServer(addr string) *http.Server {
func (s *Server) newHttpServer(itemFunc string) *http.Server {
server := &http.Server{
Addr: addr,
Addr: itemFunc,
Handler: s.config.Handler,
ReadTimeout: s.config.ReadTimeout,
WriteTimeout: s.config.WriteTimeout,
@ -60,8 +60,8 @@ func (s *Server) newHttpServer(addr string) *http.Server {
// 执行HTTP监听
func (s *gracefulServer) ListenAndServe() error {
addr := s.httpServer.Addr
ln, err := s.getNetListener(addr)
itemFunc := s.httpServer.Addr
ln, err := s.getNetListener(itemFunc)
if err != nil {
return err
}
@ -88,7 +88,7 @@ func (s *gracefulServer) setFd(fd int) {
// 执行HTTPS监听
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error {
addr := s.httpServer.Addr
itemFunc := s.httpServer.Addr
config := (*tls.Config)(nil)
if len(tlsConfig) > 0 {
config = tlsConfig[0]
@ -106,7 +106,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig .
if err != nil {
return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()))
}
ln, err := s.getNetListener(addr)
ln, err := s.getNetListener(itemFunc)
if err != nil {
return err
}
@ -131,7 +131,7 @@ func (s *gracefulServer) doServe() error {
if s.fd != 0 {
action = "reloaded"
}
glog.Printf("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
glog.Printf("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.itemFunc)
s.status = SERVER_STATUS_RUNNING
err := s.httpServer.Serve(s.listener)
s.status = SERVER_STATUS_STOPPED
@ -139,7 +139,7 @@ func (s *gracefulServer) doServe() error {
}
// 自定义的net.Listener
func (s *gracefulServer) getNetListener(addr string) (net.Listener, error) {
func (s *gracefulServer) getNetListener(itemFunc string) (net.Listener, error) {
var ln net.Listener
var err error
if s.fd > 0 {
@ -152,7 +152,7 @@ func (s *gracefulServer) getNetListener(addr string) (net.Listener, error) {
} else {
// 如果监听失败1秒后重试最多重试3次
for i := 0; i < 3; i++ {
ln, err = net.Listen("tcp", addr)
ln, err = net.Listen("tcp", itemFunc)
if err != nil {
err = fmt.Errorf("%d: net.Listen error: %v", gproc.Pid(), err)
time.Sleep(time.Second)
@ -174,7 +174,7 @@ func (s *gracefulServer) shutdown() {
return
}
if err := s.httpServer.Shutdown(context.Background()); err != nil {
glog.Errorf("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
glog.Errorf("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.itemFunc, err)
}
}
@ -184,6 +184,6 @@ func (s *gracefulServer) close() {
return
}
if err := s.httpServer.Close(); err != nil {
glog.Errorf("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
glog.Errorf("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.itemFunc, err)
}
}

View File

@ -9,7 +9,6 @@ package ghttp
import (
"net/http"
"os"
"reflect"
"sort"
"strings"
@ -97,19 +96,12 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
}
// 动态服务检索
handler := (*handlerItem)(nil)
if !request.isFileRequest || isStaticDir {
if parsedItem := s.getServeHandlerWithCache(request); parsedItem != nil {
handler = parsedItem.handler
for k, v := range parsedItem.values {
request.routerVars[k] = v
}
request.Router = parsedItem.handler.router
}
request.handlers = s.getHandlersWithCache(request)
}
// 判断最终对该请求提供的服务方式
if isStaticDir && handler != nil {
if isStaticDir && request.handlers != nil {
request.isFileRequest = false
}
@ -118,14 +110,13 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
// 执行静态文件服务/回调控制器/执行对象/方法
if !request.IsExited() {
// 需要再次判断文件是否真实存在,
// 因为文件检索可能使用了缓存,从健壮性考虑这里需要二次判断
if request.isFileRequest /* && gfile.Exists(staticFile) */ {
if request.isFileRequest {
// 静态服务
s.serveFile(request, staticFile)
} else {
if handler != nil {
if request.handlers != nil {
// 动态服务
s.callServeHandler(handler, request)
request.MiddleWare.Next()
} else {
if isStaticDir {
// 静态目录
@ -172,59 +163,6 @@ func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) {
return "", false
}
// 调用服务接口
func (s *Server) callServeHandler(h *handlerItem, r *Request) {
if h.faddr == nil {
c := reflect.New(h.ctype)
s.niceCallFunc(func() {
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r)})
})
if !r.IsExited() {
s.niceCallFunc(func() {
c.MethodByName(h.fname).Call(nil)
})
}
if !r.IsExited() {
s.niceCallFunc(func() {
c.MethodByName("Shut").Call(nil)
})
}
} else {
if h.finit != nil {
s.niceCallFunc(func() {
h.finit(r)
})
}
if !r.IsExited() {
s.niceCallFunc(func() {
h.faddr(r)
})
}
if h.fshut != nil && !r.IsExited() {
s.niceCallFunc(func() {
h.fshut(r)
})
}
}
}
// 友好地调用方法
func (s *Server) niceCallFunc(f func()) {
defer func() {
if err := recover(); err != nil {
switch err {
case gEXCEPTION_EXIT:
fallthrough
case gEXCEPTION_EXIT_ALL:
return
default:
panic(err)
}
}
}()
f()
}
// http server静态文件处理path可以为相对路径也可以为绝对路径
func (s *Server) serveFile(r *Request, path string, allowIndex ...bool) {
f, err := os.Open(path)

View File

@ -3,7 +3,6 @@
// 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
@ -49,17 +48,18 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err
// 获得服务注册的文件地址信息
func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string {
skip := 5
if handler.rtype == gROUTE_REGISTER_HANDLER {
if handler.itemType == gHANDLER_TYPE_HANDLER {
skip = 4
}
if _, cfile, cline, ok := runtime.Caller(skip); ok {
return fmt.Sprintf("%s:%d", cfile, cline)
if _, file, line, ok := runtime.Caller(skip); ok {
return fmt.Sprintf("%s:%d", file, line)
}
return ""
}
// 路由注册处理方法。
// 如果带有hook参数表示是回调注册方法; 否则为普通路由执行方法。
// 非叶节点为哈希表检索节点按照URI注册的层级进行高效检索直至到叶子链表节点
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高;
func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string) {
// Web Server正常运行时无法动态注册路由方法
if s.Status() == SERVER_STATUS_RUNNING {
@ -89,7 +89,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string
}
}
// 路由对象
// 注册的路由信息对象
handler.router = &Router{
Uri: uri,
Domain: domain,
@ -98,20 +98,11 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string
}
handler.router.RegRule, handler.router.RegNames = s.patternToRegRule(uri)
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中。
// 非叶节点为哈希表检索节点按照URI注册的层级进行高效检索直至到叶子链表节点
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高;
tree := (map[string]interface{})(nil)
if len(hookName) == 0 {
tree = s.serveTree
} else {
tree = s.hooksTree
}
if _, ok := tree[domain]; !ok {
tree[domain] = make(map[string]interface{})
if _, ok := s.serveTree[domain]; !ok {
s.serveTree[domain] = make(map[string]interface{})
}
// 用于遍历的指针
p := tree[domain]
p := s.serveTree[domain]
if len(hookName) > 0 {
if _, ok := p.(map[string]interface{})[hookName]; !ok {
p.(map[string]interface{})[hookName] = make(map[string]interface{})
@ -166,22 +157,27 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string
pushed := false
for e := l.Front(); e != nil; e = e.Next() {
item = e.Value.(*handlerItem)
// 判断是否已存在相同的路由注册项,(如果不是hook注册)是则进行替换
if len(hookName) == 0 {
if strings.EqualFold(handler.router.Domain, item.router.Domain) &&
strings.EqualFold(handler.router.Method, item.router.Method) &&
strings.EqualFold(handler.router.Uri, item.router.Uri) {
e.Value = handler
switch handler.itemType {
// 判断是否已存在相同的路由注册项,如果是普通路由注册则进行替换
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
if handler.itemType == gHANDLER_TYPE_HANDLER {
if strings.EqualFold(handler.router.Domain, item.router.Domain) &&
strings.EqualFold(handler.router.Method, item.router.Method) &&
strings.EqualFold(handler.router.Uri, item.router.Uri) {
e.Value = handler
pushed = true
break
}
}
// 否则,那么判断优先级,决定插入顺序
default:
if s.compareRouterPriority(handler.router, item.router) {
l.InsertBefore(handler, e)
pushed = true
break
}
}
// 如果路由注册项不相等,那么判断优先级,决定插入顺序
if s.compareRouterPriority(handler.router, item.router) {
l.InsertBefore(handler, e)
pushed = true
break
}
}
if !pushed {
l.PushBack(handler)

View File

@ -3,7 +3,6 @@
// 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

View File

@ -3,28 +3,24 @@
// 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 (
"container/list"
"fmt"
"reflect"
"runtime"
"strings"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/text/gregex"
)
// 绑定指定的hook回调函数, pattern参数同BindHandler支持命名路由hook参数的值由ghttp server设定参数不区分大小写
func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) {
s.setHandler(pattern, &handlerItem{
name: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
ctype: nil,
fname: "",
faddr: handler,
itemType: gHANDLER_TYPE_HOOK,
itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
itemFunc: handler,
}, hook)
}
@ -63,7 +59,7 @@ func (s *Server) callHookHandler(hook string, r *Request) {
}
// 不使用hook的router对象保留路由注册服务的router对象不能覆盖
// r.Router = item.handler.router
if err := s.niceCallHookHandler(item.handler.faddr, r); err != nil {
if err := s.niceCallHookHandler(item.handler.itemFunc, r); err != nil {
switch err {
case gEXCEPTION_EXIT:
break
@ -122,7 +118,7 @@ func (s *Server) searchHookHandler(method, path, domain, hook string) []*handler
} else {
array = strings.Split(path[1:], "/")
}
parsedItems := make([]*handlerParsedItem, 0)
parsedItems := make([]*handlerParsedItem, 0, 8)
for _, domain := range domains {
p, ok := s.hooksTree[domain]
if !ok {
@ -163,7 +159,6 @@ func (s *Server) searchHookHandler(method, path, domain, hook string) []*handler
}
// 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高
pushedSet := gset.NewStringSet()
for i := len(lists) - 1; i >= 0; i-- {
for e := lists[i].Front(); e != nil; e = e.Next() {
handler := e.Value.(*handlerItem)
@ -186,11 +181,7 @@ func (s *Server) searchHookHandler(method, path, domain, hook string) []*handler
}
}
}
address := fmt.Sprintf("%p", handler)
if !pushedSet.Contains(address) {
parsedItems = append(parsedItems, parsedItem)
pushedSet.Add(address)
}
parsedItems = append(parsedItems, parsedItem)
}
}
}

View File

@ -0,0 +1,7 @@
// Copyright 2018 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

View File

@ -3,7 +3,6 @@
// 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
@ -16,26 +15,29 @@ import (
// 查询请求处理方法.
// 内部带锁机制可以并发读但是不能并发写并且有缓存机制按照Host、Method、Path进行缓存.
func (s *Server) getServeHandlerWithCache(r *Request) *handlerParsedItem {
cacheItem := (*handlerParsedItem)(nil)
func (s *Server) getHandlersWithCache(r *Request) []*handlerParsedItem {
cacheKey := s.serveHandlerKey(r.Method, r.URL.Path, r.GetHost())
cacheItems := ([]*handlerParsedItem)(nil)
if v := s.serveCache.Get(cacheKey); v == nil {
cacheItem = s.searchServeHandler(r.Method, r.URL.Path, r.GetHost())
if cacheItem != nil {
s.serveCache.Set(cacheKey, cacheItem, s.config.RouterCacheExpire*1000)
cacheItems = s.searchHandlers(r.Method, r.URL.Path, r.GetHost())
if cacheItems != nil {
s.serveCache.Set(cacheKey, cacheItems, s.config.RouterCacheExpire*1000)
}
} else {
cacheItem = v.(*handlerParsedItem)
cacheItems = v.([]*handlerParsedItem)
}
return cacheItem
if len(cacheItems) == 0 {
return nil
}
return cacheItems
}
// 服务方法检索
func (s *Server) searchServeHandler(method, path, domain string) *handlerParsedItem {
// 路由注册方法检索,返回所有该路由的注册函数,构造成数组返回
func (s *Server) searchHandlers(method, path, domain string) []*handlerParsedItem {
if len(path) == 0 {
return nil
}
// 遍历检索的域名列表
// 遍历检索的域名列表,优先遍历默认域名
domains := []string{gDEFAULT_DOMAIN}
if !strings.EqualFold(gDEFAULT_DOMAIN, domain) {
domains = append(domains, domain)
@ -47,13 +49,15 @@ func (s *Server) searchServeHandler(method, path, domain string) *handlerParsedI
} else {
array = strings.Split(path[1:], "/")
}
parsedItems := make([]*handlerParsedItem, 0, 16)
isServeHandlerAdded := false
for _, domain := range domains {
p, ok := s.serveTree[domain]
if !ok {
continue
}
// 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
lists := make([]*list.List, 0)
lists := make([]*list.List, 0, 16)
for k, v := range array {
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
@ -86,6 +90,13 @@ func (s *Server) searchServeHandler(method, path, domain string) *handlerParsedI
for i := len(lists) - 1; i >= 0; i-- {
for e := lists[i].Front(); e != nil; e = e.Next() {
item := e.Value.(*handlerItem)
// 服务路由函数只能添加一次
if isServeHandlerAdded {
switch item.itemType {
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
continue
}
}
// 动态匹配规则带有gDEFAULT_METHOD的情况不会像静态规则那样直接解析为所有的HTTP METHOD存储
if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, method) {
// 注意当不带任何动态路由规则时len(match) == 1
@ -105,13 +116,20 @@ func (s *Server) searchServeHandler(method, path, domain string) *handlerParsedI
}
}
}
return parsedItem
parsedItems = append(parsedItems, parsedItem)
// 服务路由函数只能添加一次
if !isServeHandlerAdded {
switch item.itemType {
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
isServeHandlerAdded = true
}
}
}
}
}
}
}
return nil
return parsedItems
}
// 生成回调方法查询的Key

View File

@ -71,11 +71,12 @@ func (s *Server) BindController(pattern string, c Controller, methods ...string)
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
rtype: gROUTE_REGISTER_CONTROLLER,
ctype: v.Elem().Type(),
fname: mname,
faddr: nil,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
itemType: gHANDLER_TYPE_CONTROLLER,
ctrlInfo: &handlerController{
name: mname,
reflect: v.Elem().Type(),
},
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI
// 例如: pattern为/user, 那么会同时注册/user及/user/index
@ -88,11 +89,12 @@ func (s *Server) BindController(pattern string, c Controller, methods ...string)
k = "/" + k
}
m[k] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
rtype: gROUTE_REGISTER_CONTROLLER,
ctype: v.Elem().Type(),
fname: mname,
faddr: nil,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
itemType: gHANDLER_TYPE_CONTROLLER,
ctrlInfo: &handlerController{
name: mname,
reflect: v.Elem().Type(),
},
}
}
}
@ -124,11 +126,12 @@ func (s *Server) BindControllerMethod(pattern string, c Controller, method strin
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
rtype: gROUTE_REGISTER_CONTROLLER,
ctype: v.Elem().Type(),
fname: mname,
faddr: nil,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
itemType: gHANDLER_TYPE_CONTROLLER,
ctrlInfo: &handlerController{
name: mname,
reflect: v.Elem().Type(),
},
}
s.bindHandlerByMap(m)
}
@ -163,11 +166,12 @@ func (s *Server) BindControllerRest(pattern string, c Controller) {
}
key := s.mergeBuildInNameToPattern(mname+":"+pattern, sname, mname, false)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
rtype: gROUTE_REGISTER_CONTROLLER,
ctype: v.Elem().Type(),
fname: mname,
faddr: nil,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
itemType: gHANDLER_TYPE_CONTROLLER,
ctrlInfo: &handlerController{
name: mname,
reflect: v.Elem().Type(),
},
}
}
s.bindHandlerByMap(m)

View File

@ -19,11 +19,9 @@ import (
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server) BindHandler(pattern string, handler HandlerFunc) {
s.bindHandlerItem(pattern, &handlerItem{
name: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
rtype: gROUTE_REGISTER_HANDLER,
ctype: nil,
fname: "",
faddr: handler,
itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
itemType: gHANDLER_TYPE_HANDLER,
itemFunc: handler,
})
}

View File

@ -41,13 +41,13 @@ func (s *Server) BindObject(pattern string, obj interface{}, methods ...string)
v := reflect.ValueOf(obj)
t := v.Type()
sname := t.Elem().Name()
finit := (func(*Request))(nil)
fshut := (func(*Request))(nil)
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
if v.MethodByName("Init").IsValid() {
finit = v.MethodByName("Init").Interface().(func(*Request))
initFunc = v.MethodByName("Init").Interface().(func(*Request))
}
if v.MethodByName("Shut").IsValid() {
fshut = v.MethodByName("Shut").Interface().(func(*Request))
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
}
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
@ -63,7 +63,7 @@ func (s *Server) BindObject(pattern string, obj interface{}, methods ...string)
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := v.Method(i).Interface().(func(*Request))
itemFunc, ok := v.Method(i).Interface().(func(*Request))
if !ok {
if len(methodMap) > 0 {
// 指定的方法名称注册,那么需要使用错误提示
@ -78,13 +78,11 @@ func (s *Server) BindObject(pattern string, obj interface{}, methods ...string)
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
rtype: gROUTE_REGISTER_OBJECT,
ctype: nil,
fname: "",
faddr: faddr,
finit: finit,
fshut: fshut,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
itemType: gHANDLER_TYPE_OBJECT,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
}
// 如果方法中带有Index方法那么额外自动增加一个路由规则匹配主URI。
// 注意当pattern带有内置变量时不会自动加该路由。
@ -95,13 +93,11 @@ func (s *Server) BindObject(pattern string, obj interface{}, methods ...string)
k = "/" + k
}
m[k] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
rtype: gROUTE_REGISTER_OBJECT,
ctype: nil,
fname: "",
faddr: faddr,
finit: finit,
fshut: fshut,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
itemType: gHANDLER_TYPE_OBJECT,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
}
}
}
@ -121,13 +117,13 @@ func (s *Server) BindObjectMethod(pattern string, obj interface{}, method string
glog.Error("invalid method name:" + mname)
return
}
finit := (func(*Request))(nil)
fshut := (func(*Request))(nil)
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
if v.MethodByName("Init").IsValid() {
finit = v.MethodByName("Init").Interface().(func(*Request))
initFunc = v.MethodByName("Init").Interface().(func(*Request))
}
if v.MethodByName("Shut").IsValid() {
fshut = v.MethodByName("Shut").Interface().(func(*Request))
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
}
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
@ -135,7 +131,7 @@ func (s *Server) BindObjectMethod(pattern string, obj interface{}, method string
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := fval.Interface().(func(*Request))
itemFunc, ok := fval.Interface().(func(*Request))
if !ok {
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, mname, fval.Type().String())
@ -143,13 +139,11 @@ func (s *Server) BindObjectMethod(pattern string, obj interface{}, method string
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
rtype: gROUTE_REGISTER_OBJECT,
ctype: nil,
fname: "",
faddr: faddr,
finit: finit,
fshut: fshut,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
itemType: gHANDLER_TYPE_OBJECT,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
}
s.bindHandlerByMap(m)
@ -162,13 +156,13 @@ func (s *Server) BindObjectRest(pattern string, obj interface{}) {
v := reflect.ValueOf(obj)
t := v.Type()
sname := t.Elem().Name()
finit := (func(*Request))(nil)
fshut := (func(*Request))(nil)
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
if v.MethodByName("Init").IsValid() {
finit = v.MethodByName("Init").Interface().(func(*Request))
initFunc = v.MethodByName("Init").Interface().(func(*Request))
}
if v.MethodByName("Shut").IsValid() {
fshut = v.MethodByName("Shut").Interface().(func(*Request))
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
}
pkgPath := t.Elem().PkgPath()
for i := 0; i < v.NumMethod(); i++ {
@ -182,7 +176,7 @@ func (s *Server) BindObjectRest(pattern string, obj interface{}) {
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := v.Method(i).Interface().(func(*Request))
itemFunc, ok := v.Method(i).Interface().(func(*Request))
if !ok {
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, mname, v.Method(i).Type().String())
@ -190,13 +184,11 @@ func (s *Server) BindObjectRest(pattern string, obj interface{}) {
}
key := s.mergeBuildInNameToPattern(mname+":"+pattern, sname, mname, false)
m[key] = &handlerItem{
name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
rtype: gROUTE_REGISTER_OBJECT,
ctype: nil,
fname: "",
faddr: faddr,
finit: finit,
fshut: fshut,
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
itemType: gHANDLER_TYPE_OBJECT,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
}
}
s.bindHandlerByMap(m)

View File

@ -14,8 +14,6 @@ import (
// 查询状态码回调函数
func (s *Server) getStatusHandler(status int, r *Request) HandlerFunc {
domains := []string{r.GetHost(), gDEFAULT_DOMAIN}
s.statusHandlerMu.RLock()
defer s.statusHandlerMu.RUnlock()
for _, domain := range domains {
if f, ok := s.statusHandlerMap[s.statusHandlerKey(status, domain)]; ok {
return f
@ -27,9 +25,7 @@ func (s *Server) getStatusHandler(status int, r *Request) HandlerFunc {
// 不同状态码下的回调方法处理
// pattern格式domain#status
func (s *Server) setStatusHandler(pattern string, handler HandlerFunc) {
s.statusHandlerMu.Lock()
s.statusHandlerMap[pattern] = handler
s.statusHandlerMu.Unlock()
}
// 生成状态码回调函数map存储键名

View File

@ -4,7 +4,6 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// 基本路由功能以及优先级测试
package ghttp_test
import (
@ -24,18 +23,18 @@ func Test_Router_Basic(t *testing.T) {
s.BindHandler("/:name", func(r *ghttp.Request) {
r.Response.Write("/:name")
})
s.BindHandler("/:name/update", func(r *ghttp.Request) {
r.Response.Write(r.Get("name"))
})
s.BindHandler("/:name/:action", func(r *ghttp.Request) {
r.Response.Write(r.Get("action"))
})
//s.BindHandler("/:name/update", func(r *ghttp.Request) {
// r.Response.Write(r.Get("name"))
//})
//s.BindHandler("/:name/:action", func(r *ghttp.Request) {
// r.Response.Write(r.Get("action"))
//})
s.BindHandler("/:name/*any", func(r *ghttp.Request) {
r.Response.Write(r.Get("any"))
})
s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request) {
r.Response.Write(r.Get("field"))
})
//s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request) {
// r.Response.Write(r.Get("field"))
//})
s.SetPort(p)
s.SetDumpRouteMap(false)
s.Start()