初步完成ghttp动态路由特性

This commit is contained in:
John
2018-04-13 15:19:31 +08:00
parent 03478fe72d
commit 9b61727a00
12 changed files with 501 additions and 403 deletions

3
TODO Normal file
View File

@ -0,0 +1,3 @@
1. gconv完善针对不同类型的判断例如尽量减少sprintf("%v", xxx)来执行string类型的转换
2. gdb Where方法参数的改进研究是否可以将string参数类型修改为interfaceP{
3. 增加对于数据表Model的封装

View File

@ -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.
// HTTP客户端请求.
package ghttp

View File

@ -3,6 +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.
// HTTP客户端请求返回.
package ghttp

View File

@ -13,12 +13,9 @@ import (
"reflect"
"net/http"
"gitee.com/johng/gf/g/os/gcache"
"gitee.com/johng/gf/g/util/gregx"
"gitee.com/johng/gf/g/util/gutil"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/container/gqueue"
"container/list"
)
const (
@ -134,316 +131,4 @@ func (s *Server) Run() error {
}
s.status = 1
return nil
}
// 生成回调方法查询的Key
func (s *Server) handlerKey(domain, method, pattern string) string {
return strings.ToUpper(method) + ":" + pattern + "@" + strings.ToLower(domain)
}
// 注册服务处理方法
func (s *Server) setHandler(pattern string, item *HandlerItem) error {
domain, method, uri, err := s.parsePatternForBindHandler(pattern)
if err != nil {
return errors.New("invalid pattern")
}
item.uri = uri
item.domain = domain
item.method = method
// 静态注册
s.hmmu.Lock()
if method == gDEFAULT_METHOD {
for v, _ := range s.methodsMap {
s.handlerMap[s.handlerKey(domain, v, pattern)] = item
}
} else {
s.handlerMap[s.handlerKey(domain, method, pattern)] = item
}
s.hmmu.Unlock()
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中
if s.isUriHasRule(uri) {
if _, ok := s.handlerTree[domain]; !ok {
s.handlerTree[domain] = make(map[string]interface{})
}
p := s.handlerTree[domain]
array := strings.Split(uri[1:], "/")
item.priority = len(array)
for _, v := range array {
if len(v) == 0 {
continue
}
switch v[0] {
case ':':
fallthrough
case '*':
v = "/"
fallthrough
default:
if _, ok := p.(map[string]interface{})[v]; !ok {
p.(map[string]interface{})[v] = make(map[string]interface{})
} else {
p = p.(map[string]interface{})[v]
}
}
}
// 到达叶子节点
var l *list.List
if v, ok := p.(map[string]interface{})["*list"]; !ok {
l = list.New()
p.(map[string]interface{})["*list"] = l
} else {
l = v.(*list.List)
}
l.PushBack(item)
}
return nil
}
// 判断URI中是否包含动态注册规则
func (s *Server) isUriHasRule(uri string) bool {
if len(uri) > 1 && (strings.Index(uri, "/:") != -1 || strings.Index(uri, "/*") != -1) {
return true
}
return false
}
// 解析pattern
func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri string, err error) {
uri = pattern
domain = gDEFAULT_DOMAIN
method = gDEFAULT_METHOD
if array, err := gregx.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil {
method = array[1]
pattern = array[2]
}
if array, err := gregx.MatchString(`(.+)@([\w\.\-]+)`, pattern); len(array) > 1 && err == nil {
uri = array[1]
domain = array[2]
}
if uri == "" {
err = errors.New("invalid pattern")
}
return
}
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user, post:/user@johng.cn
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error {
if s.status == 1 {
return errors.New("server handlers cannot be changed while running")
}
return s.setHandler(pattern, item)
}
// 通过映射数组绑定URI到操作函数/方法
func (s *Server)bindHandlerByMap(m HandlerMap) error {
for p, h := range m {
if err := s.bindHandlerItem(p, h); err != nil {
return err
}
}
return nil
}
// 将方法名称按照设定的规则转换为URI并附加到指定的URI后面
func (s *Server)appendMethodNameToUriWithPattern(pattern string, name string) string {
// 检测域名后缀
array := strings.Split(pattern, "@")
// 分离URI(其实可能包含HTTP Method)
uri := array[0]
uri = strings.TrimRight(uri, "/") + "/"
// 方法名中间存在大写字母转换为小写URI地址以“-”号链接每个单词
for i := 0; i < len(name); i++ {
if i > 0 && gutil.IsLetterUpper(name[i]) {
uri += "-"
}
uri += strings.ToLower(string(name[i]))
}
// 加上指定域名后缀
if len(array) > 1 {
uri += "@" + array[1]
}
return uri
}
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, &HandlerItem{
ctype : nil,
fname : "",
faddr : handler,
})
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义
func (s *Server)BindObject(pattern string, obj interface{}) error {
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,区分大小写
func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string) error {
m := make(HandlerMap)
for _, v := range strings.Split(methods, ",") {
method := strings.TrimSpace(v)
fval := reflect.ValueOf(obj).MethodByName(method)
if !fval.IsValid() {
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : fval.Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义
func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
if _, ok := s.methodsMap[strings.ToUpper(name)]; !ok {
continue
}
key := name + ":" + pattern
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定控制器控制器需要实现gmvc.Controller接口
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
func (s *Server)BindController(pattern string, c Controller) error {
// 遍历控制器获取方法列表并构造成uri
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
if name == "Init" || name == "Shut" {
continue
}
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 绑定控制器(RESTFul)控制器需要实现gmvc.Controller接口
// 方法会识别HTTP方法并做REST绑定处理例如Post方法会绑定到HTTP POST的方法请求处理Delete方法会绑定到HTTP DELETE的方法请求处理
// 因此只会绑定HTTP Method对应的方法其他方法不会自动注册绑定
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
func (s *Server)BindControllerRest(pattern string, c Controller) error {
// 遍历控制器获取方法列表并构造成uri
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
methods := make(map[string]bool)
for _, v := range strings.Split(gHTTP_METHODS, ",") {
methods[v] = true
}
// 如果存在与HttpMethod对应名字的方法那么绑定这些方法
for i := 0; i < v.NumMethod(); i++ {
name := strings.ToUpper(t.Method(i).Name)
if name == "Init" || name == "Shut" {
continue
}
if _, ok := s.methodsMap[name]; !ok {
continue
}
key := name + ":" + pattern
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,不区分大小写
func (s *Server)BindControllerMethod(pattern string, c Controller, methods string) error {
m := make(HandlerMap)
cval := reflect.ValueOf(c)
for _, v := range strings.Split(methods, ",") {
ctype := reflect.ValueOf(c).Elem().Type()
method := strings.TrimSpace(v)
if !cval.MethodByName(method).IsValid() {
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = &HandlerItem{
ctype : ctype,
fname : method,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 绑定指定的hook回调函数, pattern参数同BindHandler支持命名路由hook参数的值由ghttp server设定参数不区分大小写
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error {
//domain, method, uri, err := s.parsePatternForBindHookHandler(pattern)
//if err != nil {
// return errors.New("invalid pattern")
//}
//var l *glist.List
//if method == gDEFAULT_METHOD {
// for v, _ := range s.methodsMap {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
// }
//} else {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
//}
return nil
}
// 通过map批量绑定回调函数
func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error {
for k, v := range hookmap {
if err := s.BindHookHandler(pattern, k, v); err != nil {
return err
}
}
return nil
}
// 构造用于hooksMap检索的键名
func (s *Server)handlerHookKey(domain, method, uri, hook string) string {
return strings.ToUpper(hook) + "^" + s.handlerKey(domain, method, uri)
}
}

View File

@ -3,6 +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.
// 异步工作协程.
package ghttp

View File

@ -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.
// 配置管理数据结构定义.
package ghttp

View File

@ -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.
//
// 域名服务注册管理.
package ghttp

View File

@ -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.
//
// 请求处理.
package ghttp
@ -13,7 +13,6 @@ import (
"sort"
"reflect"
"strings"
"strconv"
"net/url"
"net/http"
"path/filepath"
@ -33,7 +32,6 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
// 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置;
// 最后,如果以上都没有找到处理接口,那么进行文件处理;
func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
fmt.Println(s.handlerTree)
request := newRequest(s, r, w)
if h := s.getHandler(request); h != nil {
s.callHandler(h, request)
@ -49,51 +47,6 @@ func (s *Server) getHandler(r *Request) *HandlerItem {
return handler
}
// 服务方法检索
func (s *Server) searchHandler(r *Request) *HandlerItem {
s.hmmu.RLock()
defer s.hmmu.RUnlock()
domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]}
// 首先进行静态匹配
for _, domain := range domains {
if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok {
return f
}
}
// 其次进行正则匹配(会比较耗效率)
for k, v := range s.handlerMap {
if array, err := gregx.MatchString(`([a-zA-Z]+):(.+)@([\w\.\-]+)`, k); len(array) > 2 && err == nil {
// method匹配
if !strings.EqualFold(r.Method, array[1]) {
continue
}
// domain匹配
for _, domain := range domains {
if !strings.EqualFold(domain, array[3]) {
continue
}
// method & domain匹配时那么执行pattern的正则匹配
regrule, querystr := s.patternToRegRule(array[2])
if gregx.IsMatchString(regrule, r.URL.Path) {
// 如果需要query匹配那么需要重新解析URL
if len(querystr) > 0 {
if query, err := gregx.ReplaceString(regrule, querystr, r.URL.Path); err == nil && len(query) > 0 {
if vals, err := url.ParseQuery(query); err == nil {
for k, v := range vals {
r.values[k] = v
}
}
}
}
return v
}
}
}
}
return nil
}
// 按照指定hook回调函数的注册顺序进行调用
func (s *Server)callHookHandler(r *Request, hook string) {
l := s.searchHookHandler(r, hook)
@ -164,40 +117,6 @@ func (s *Server)searchHookHandler(r *Request, hook string) []HandlerFunc {
return funcs
}
// 将pattern不带method和domain解析成正则表达式匹配以及对应的query字符串
func (s *Server) patternToRegRule(rule string) (regrule string, querystr string) {
if len(rule) < 2 {
return rule, ""
}
regrule = "^/"
array := strings.Split(rule[1:], "/")
index := 1
for _, v := range array {
if len(v) == 0 {
continue
}
switch v[0] {
case ':':
regrule += `/([\w\.\-]+)`
if len(querystr) > 0 {
querystr += "&"
}
querystr += v[1:] + "=$" + strconv.Itoa(index)
index++
case '*':
regrule += `/(.*)`
if len(querystr) > 0 {
querystr += "&"
}
querystr += v[1:] + "=$" + strconv.Itoa(index)
return
default:
regrule += v
}
}
return
}
// 初始化控制器
func (s *Server)callHandler(h *HandlerItem, r *Request) {
// 会话处理

View File

@ -3,6 +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.
// 参数管理.
package ghttp

View File

@ -0,0 +1,241 @@
// 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 (
"errors"
"strings"
"container/list"
"gitee.com/johng/gf/g/util/gregx"
)
// 解析pattern
func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri string, err error) {
uri = pattern
domain = gDEFAULT_DOMAIN
method = gDEFAULT_METHOD
if array, err := gregx.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil {
method = array[1]
pattern = array[2]
}
if array, err := gregx.MatchString(`(.+)@([\w\.\-]+)`, pattern); len(array) > 1 && err == nil {
uri = array[1]
domain = array[2]
}
if uri == "" {
err = errors.New("invalid pattern")
}
return
}
// 注册服务处理方法
func (s *Server) setHandler(pattern string, item *HandlerItem) error {
domain, method, uri, err := s.parsePatternForBindHandler(pattern)
if err != nil {
return errors.New("invalid pattern")
}
item.uri = uri
item.domain = domain
item.method = method
// 静态注册
s.hmmu.Lock()
defer s.hmmu.Unlock()
if method == gDEFAULT_METHOD {
for v, _ := range s.methodsMap {
s.handlerMap[s.handlerKey(domain, v, pattern)] = item
}
} else {
s.handlerMap[s.handlerKey(domain, method, pattern)] = item
}
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中
// 非叶节点为哈希表检索节点按照URI注册的层级进行高效检索直至到叶子链表节点
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表一般数据量不大,所以效率比较高;
if s.isUriHasRule(uri) {
s.htmu.Lock()
defer s.htmu.Unlock()
if _, ok := s.handlerTree[domain]; !ok {
s.handlerTree[domain] = make(map[string]interface{})
}
p := s.handlerTree[domain]
array := strings.Split(uri[1:], "/")
item.priority = len(array)
for _, v := range array {
if len(v) == 0 {
continue
}
switch v[0] {
case ':':
fallthrough
case '*':
v = "/"
fallthrough
default:
if _, ok := p.(map[string]interface{})[v]; !ok {
p.(map[string]interface{})[v] = make(map[string]interface{})
}
p = p.(map[string]interface{})[v]
}
}
// 到达叶子节点
var l *list.List
if v, ok := p.(map[string]interface{})["*list"]; !ok {
l = list.New()
p.(map[string]interface{})["*list"] = l
} else {
l = v.(*list.List)
}
//b,_ := gjson.New(s.handlerTree).ToJsonIndent()
//fmt.Println(string(b))
// 从头开始遍历链表,优先级高的放在前面
for e := l.Front(); e != nil; e = e.Next() {
if s.compareHandlerItemPriority(item, e.Value.(*HandlerItem)) {
l.InsertBefore(item, e)
return nil
}
}
l.PushBack(item)
}
return nil
}
// 对比两个HandlerItem的优先级需要非常注意的是注意新老对比项的参数先后顺序
func (s *Server) compareHandlerItemPriority(newItem, oldItem *HandlerItem) bool {
if newItem.priority > oldItem.priority {
return true
}
if newItem.priority < oldItem.priority {
return false
}
if strings.Count(newItem.uri, "/:") > strings.Count(oldItem.uri, "/:") {
return true
}
return false
}
// 服务方法检索
func (s *Server) searchHandler(r *Request) *HandlerItem {
s.hmmu.RLock()
domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]}
// 首先进行静态匹配
for _, domain := range domains {
if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok {
s.hmmu.RUnlock()
return f
}
}
s.hmmu.RUnlock()
// 其次进行动态匹配
array := strings.Split(r.URL.Path[1:], "/")
s.htmu.RLock()
for _, domain := range domains {
p, ok := s.handlerTree[domain]
if !ok {
continue
}
// 多层链表的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
lists := make([]*list.List, 0)
for k, v := range array {
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
if _, ok := p.(map[string]interface{})[v]; !ok {
if _, ok := p.(map[string]interface{})["/"]; ok {
p = p.(map[string]interface{})["/"]
if k == len(array) - 1 {
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
}
}
} else {
p = p.(map[string]interface{})[v]
if k == len(array) - 1 {
if _, ok := p.(map[string]interface{})["*list"]; ok {
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
}
}
}
}
// 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高
for i := len(lists) - 1; i >= 0; i-- {
for e := lists[i].Front(); e != nil; e = e.Next() {
item := e.Value.(*HandlerItem)
if strings.EqualFold(item.method, gDEFAULT_METHOD) || strings.EqualFold(item.method, r.Method) {
regrule, names := s.patternToRegRule(item.uri)
if gregx.IsMatchString(regrule, r.URL.Path) {
// 如果需要query匹配那么需要重新解析URL
if len(names) > 0 {
if match, err := gregx.MatchString(regrule, r.URL.Path); err == nil {
array := strings.Split(names, ",")
if len(match) > len(array) {
for index, name := range array {
r.values[name] = []string{match[index + 1]}
}
}
}
}
s.htmu.RUnlock()
return item
}
}
}
}
}
s.htmu.RUnlock()
return nil
}
// 将pattern不带method和domain解析成正则表达式匹配以及对应的query字符串
func (s *Server) patternToRegRule(rule string) (regrule string, names string) {
if len(rule) < 2 {
return rule, ""
}
regrule = "^"
array := strings.Split(rule[1:], "/")
for _, v := range array {
if len(v) == 0 {
continue
}
switch v[0] {
case ':':
regrule += `/([\w\.\-]+)`
if len(names) > 0 {
names += ","
}
names += v[1:]
case '*':
regrule += `/(.*)`
if len(names) > 0 {
names += ","
}
names += v[1:]
return
default:
regrule += "/" + v
}
}
regrule += `$`
return
}
// 判断URI中是否包含动态注册规则
func (s *Server) isUriHasRule(uri string) bool {
if len(uri) > 1 && (strings.Index(uri, "/:") != -1 || strings.Index(uri, "/*") != -1) {
return true
}
return false
}
// 生成回调方法查询的Key
func (s *Server) handlerKey(domain, method, pattern string) string {
return strings.ToUpper(method) + ":" + pattern + "@" + strings.ToLower(domain)
}

View File

@ -0,0 +1,237 @@
// 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.
// 服务注册 + hook管理.
package ghttp
import (
"errors"
"strings"
"reflect"
"gitee.com/johng/gf/g/util/gutil"
)
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user, post:/user@johng.cn
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error {
if s.status == 1 {
return errors.New("server handlers cannot be changed while running")
}
return s.setHandler(pattern, item)
}
// 通过映射数组绑定URI到操作函数/方法
func (s *Server)bindHandlerByMap(m HandlerMap) error {
for p, h := range m {
if err := s.bindHandlerItem(p, h); err != nil {
return err
}
}
return nil
}
// 将方法名称按照设定的规则转换为URI并附加到指定的URI后面
func (s *Server)appendMethodNameToUriWithPattern(pattern string, name string) string {
// 检测域名后缀
array := strings.Split(pattern, "@")
// 分离URI(其实可能包含HTTP Method)
uri := array[0]
uri = strings.TrimRight(uri, "/") + "/"
// 方法名中间存在大写字母转换为小写URI地址以“-”号链接每个单词
for i := 0; i < len(name); i++ {
if i > 0 && gutil.IsLetterUpper(name[i]) {
uri += "-"
}
uri += strings.ToLower(string(name[i]))
}
// 加上指定域名后缀
if len(array) > 1 {
uri += "@" + array[1]
}
return uri
}
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, &HandlerItem{
ctype : nil,
fname : "",
faddr : handler,
})
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义
func (s *Server)BindObject(pattern string, obj interface{}) error {
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,区分大小写
func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string) error {
m := make(HandlerMap)
for _, v := range strings.Split(methods, ",") {
method := strings.TrimSpace(v)
fval := reflect.ValueOf(obj).MethodByName(method)
if !fval.IsValid() {
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : fval.Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
// 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义
func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
m := make(HandlerMap)
v := reflect.ValueOf(obj)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
if _, ok := s.methodsMap[strings.ToUpper(name)]; !ok {
continue
}
key := name + ":" + pattern
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
// 绑定控制器控制器需要实现gmvc.Controller接口
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
func (s *Server)BindController(pattern string, c Controller) error {
// 遍历控制器获取方法列表并构造成uri
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
if name == "Init" || name == "Shut" {
continue
}
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 绑定控制器(RESTFul)控制器需要实现gmvc.Controller接口
// 方法会识别HTTP方法并做REST绑定处理例如Post方法会绑定到HTTP POST的方法请求处理Delete方法会绑定到HTTP DELETE的方法请求处理
// 因此只会绑定HTTP Method对应的方法其他方法不会自动注册绑定
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
func (s *Server)BindControllerRest(pattern string, c Controller) error {
// 遍历控制器获取方法列表并构造成uri
m := make(HandlerMap)
v := reflect.ValueOf(c)
t := v.Type()
methods := make(map[string]bool)
for _, v := range strings.Split(gHTTP_METHODS, ",") {
methods[v] = true
}
// 如果存在与HttpMethod对应名字的方法那么绑定这些方法
for i := 0; i < v.NumMethod(); i++ {
name := strings.ToUpper(t.Method(i).Name)
if name == "Init" || name == "Shut" {
continue
}
if _, ok := s.methodsMap[name]; !ok {
continue
}
key := name + ":" + pattern
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
// 第三个参数methods支持多个方法注册多个方法以英文“,”号分隔,不区分大小写
func (s *Server)BindControllerMethod(pattern string, c Controller, methods string) error {
m := make(HandlerMap)
cval := reflect.ValueOf(c)
for _, v := range strings.Split(methods, ",") {
ctype := reflect.ValueOf(c).Elem().Type()
method := strings.TrimSpace(v)
if !cval.MethodByName(method).IsValid() {
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = &HandlerItem{
ctype : ctype,
fname : method,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 绑定指定的hook回调函数, pattern参数同BindHandler支持命名路由hook参数的值由ghttp server设定参数不区分大小写
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error {
//domain, method, uri, err := s.parsePatternForBindHookHandler(pattern)
//if err != nil {
// return errors.New("invalid pattern")
//}
//var l *glist.List
//if method == gDEFAULT_METHOD {
// for v, _ := range s.methodsMap {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
// }
//} else {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
//}
return nil
}
// 通过map批量绑定回调函数
func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) error {
for k, v := range hookmap {
if err := s.BindHookHandler(pattern, k, v); err != nil {
return err
}
}
return nil
}
// 构造用于hooksMap检索的键名
func (s *Server)handlerHookKey(domain, method, uri, hook string) string {
return strings.ToUpper(hook) + "^" + s.handlerKey(domain, method, uri)
}

View File

@ -3,8 +3,18 @@ package main
import "gitee.com/johng/gf/g/net/ghttp"
func main() {
ghttp.GetServer().BindHandler("/:name", func(r *ghttp.Request) {
r.Response.WriteString("Hello World!")
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"))
})
//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"))
})
ghttp.GetServer().SetPort(10000)
ghttp.GetServer().Run()