一、背景
在使用websocket功能的时候要实现根据不同的请求url,执行不同的方法。怎么样能简单一点呢?
解决办法
- 实际上golnag的 自带路由处理方式。只不过每次都要在里面处理,一个路由定义一个handleFunc方法,如果方法多了确实挺麻烦,每次都要添加
go http.HandleFunc("/", onMessage)
2.实用参数判断基根据每次url的不同来返回对应的结构体然后执行他的对应的方法,这个其实是简单的工厂模式。缺点就是如果该结构提的方法很多的话,调用的参数判断结构就很多哦而且很杂。
3.使用反射(reflect)实现,其实现思路是先将所有的结构体存在一个map类型里key则为结构体名字,值则为每个结构体实例。
然后比如请求url参数为 Message/GetUser ,首先分割结构体名字 message从而找到对应的结构体。最后通过MethodByName 找到对应的GetUser方法,然后 执行就可以。本方法实际上有个缺点就是需要还需要手动维护 存结构体的map,但是比之前简单多了一点,具体看代码。
package routers
import (
"reflect"
"strings"
"xfy_whotalk_socket/model"
"xfy_whotalk_socket/runtime"
"xfy_whotalk_socket/server"
"xfy_whotalk_socket/server/message"
"xfy_whotalk_socket/server/user"
)
var regStruct map[string]interface{}
func init() {
// 初始化服务map(必须要)
regStruct = make(map[string]interface{})
regStruct["User"] = user.NewUserServer()
regStruct["Message"] = message.NewMessageServer()
}
// @Title GetRouter
// @Description 系统路由
// @Param method model.ReceiveMessage 消息类型
// @return code int8
// @return res string
func GetRouter(method model.ReceiveMessage) (code int16, res string) {
server := server.NewBaseServer()
methods := strings.Split(method.Method, "/")
if len(methods) != 2 {
go server.SendToId(method.Client, method.FromId, 10003, "api参数有误", method.Method, "", 0)
return
}
serverName := methods[0]
methodName := methods[1]
if _, ok := regStruct[serverName]; !ok {
// 不存在
go server.SendToId(method.Client, method.FromId, 10004, "url not found", method.Method, "", 0)
return
}
code, res = execute(serverName, methodName, method)
if code > 0 {
go server.SendToId(method.Client, method.FromId, 10004, res, method.Method, "", 0)
}
return
}
// @Title execute
// @Description 通过反射调用对象的操作方法
// @Param ruleClassName string 服务名
// @Param methodName methodName 方法名
// @Param message model.ReceiveMessage 消息结构
// @return code int8
// @return res string
func execute(ruleClassName string, methodName string, message model.ReceiveMessage) (code int16, res string) {
t := reflect.TypeOf(regStruct[ruleClassName])
value := reflect.ValueOf(regStruct[ruleClassName])
if _, ok := t.MethodByName(methodName); !ok {
return 1, "no method"
}
// 所有方法need args 必须要
args := []reflect.Value{reflect.ValueOf(message)}
response := value.MethodByName(methodName).Call(args)
for i := range response {
if i == 0 {
x := response[i].Int()
code = int16(x)
}
if i == 1 {
res = response[i].String()
}
}
runtime.Info.Println("execute--->:", response, code, res)
return
}
其中有点一需要注意的是,在使用reflect 的 call来执行每个结构体方法的时候,如果有参数必须带上参数 正如上面的例子一样。
// 所有方法need args 必须要
args := []reflect.Value{reflect.ValueOf(message)}
reflect.ValueOf().MethodByName(methodName).Call(args)
反射执行的结果是那个方法的返回结果是一个该方法的执行结果。
反射就是动态获取对象的类型,和动态执行他的方法。 比如接口,被赋予各种值以后是没法直接确认他的类型和属性的。