feat(http): 实现动态路由生成和处理机制
- 添加 HandlerEntry 和 Route 结构体用于描述路由条目和路由规则 - 实现 generateRoutes 函数自动生成路由列表- 支持通过反射获取处理对象的方法并映射为路由 - 根据方法名前缀自动识别 HTTP 方法(GET、POST 等) - 忽略匿名字段中提升的方法以避免重复路由- 为 GET 请求动态生成带参数的正则表达式路径匹配规则 - 支持路径前缀和动作名称的自动解析与拼接 - 提供 matchesPromotedMethodName 和 getAnonymousFieldMethods 辅助函数优化方法识别逻辑
This commit is contained in:
123
32-platform/platform/http/handing/routes.go
Normal file
123
32-platform/platform/http/handing/routes.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package handing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HandlerEntry 路由条目
|
||||
type HandlerEntry struct {
|
||||
// 路由前缀
|
||||
Prefix string
|
||||
// 路由处理对象
|
||||
Handler interface{}
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
// HTTP 方法
|
||||
httpMethod string
|
||||
// 路由前缀
|
||||
prefix string
|
||||
// 路由处理对象名称
|
||||
handlerName string
|
||||
actionName string
|
||||
// 路由的正则表达式
|
||||
expression regexp.Regexp
|
||||
// 路由方法
|
||||
handlerMethod reflect.Method
|
||||
}
|
||||
|
||||
var httpMethods = []string{
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodDelete,
|
||||
http.MethodPut,
|
||||
}
|
||||
|
||||
func generateRoutes(entries ...HandlerEntry) []Route {
|
||||
routes := make([]Route, 0, 10)
|
||||
for _, entry := range entries {
|
||||
// 获取处理对象的类型
|
||||
handlerType := reflect.TypeOf(entry.Handler)
|
||||
// 获取处理对象中字段的所有方法
|
||||
promotedMethods := getAnonymousFieldMethods(handlerType)
|
||||
// 遍历处理对象的所有方法
|
||||
for i := 0; i < handlerType.NumMethod(); i++ {
|
||||
method := handlerType.Method(i)
|
||||
methodName := strings.ToUpper(method.Name)
|
||||
// 遍历 http 方法类型
|
||||
for _, httpMethod := range httpMethods {
|
||||
if strings.Index(methodName, httpMethod) == 0 {
|
||||
// 假如方法为字段的方法,则忽略
|
||||
if matchesPromotedMethodName(method, promotedMethods) {
|
||||
continue
|
||||
}
|
||||
// 建立路由
|
||||
route := Route{
|
||||
// HTTP 方法
|
||||
httpMethod: httpMethod,
|
||||
// 路由前缀
|
||||
prefix: entry.Prefix,
|
||||
// 路由处理对象名称
|
||||
handlerName: strings.Split(handlerType.Name(), "Handler")[0],
|
||||
// 动作名称
|
||||
actionName: strings.Split(methodName, httpMethod)[1],
|
||||
// 调用方法
|
||||
handlerMethod: method,
|
||||
}
|
||||
// 生成正则表达式地址匹配
|
||||
generateRegularExpression(entry.Prefix, &route)
|
||||
routes = append(routes, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func matchesPromotedMethodName(method reflect.Method, methods []reflect.Method) bool {
|
||||
for _, m := range methods {
|
||||
if m.Name == method.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getAnonymousFieldMethods(target reflect.Type) []reflect.Method {
|
||||
var methods []reflect.Method
|
||||
// 获取处理对象的所有字段
|
||||
for i := 0; i < target.NumField(); i++ {
|
||||
field := target.Field(i)
|
||||
if field.Anonymous && field.IsExported() {
|
||||
// 获取字段的所有方法
|
||||
for j := 0; j < field.Type.NumField(); j++ {
|
||||
method := field.Type.Method(j)
|
||||
if method.IsExported() {
|
||||
methods = append(methods, method)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
func generateRegularExpression(prefix string, route *Route) {
|
||||
if prefix != "" && !strings.HasPrefix(prefix, "/") {
|
||||
prefix += "/"
|
||||
}
|
||||
pattern := "(?i)" + "/" + prefix + route.actionName
|
||||
if route.httpMethod == http.MethodGet {
|
||||
for i := 1; i < route.handlerMethod.Type.NumIn(); i++ {
|
||||
if route.handlerMethod.Type.In(i).Kind() == reflect.Int {
|
||||
pattern += "/([0-9]*)"
|
||||
} else {
|
||||
pattern += "/([a-zA-Z0-9_]*)"
|
||||
}
|
||||
}
|
||||
}
|
||||
pattern = "^" + pattern + "[/]?$"
|
||||
route.expression = *regexp.MustCompile(pattern)
|
||||
}
|
||||
41
32-platform/platform/placeholder/name_handler.go
Normal file
41
32-platform/platform/placeholder/name_handler.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package placeholder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"platform/logging"
|
||||
)
|
||||
|
||||
var names = []string{"Alice", "Bob", "charlie", "Dora"}
|
||||
|
||||
type NameHandler struct {
|
||||
logging.Logger
|
||||
}
|
||||
|
||||
type NewName struct {
|
||||
Name string
|
||||
InsertAtStart bool
|
||||
}
|
||||
|
||||
func (n NameHandler) GetName(i int) string {
|
||||
n.Logger.Debugf("GetName method invoked with argument: %v", i)
|
||||
if i < len(names) {
|
||||
return fmt.Sprintf("Name #%v : %v", i, names[i])
|
||||
} else {
|
||||
return fmt.Sprintf("Index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
func (n NameHandler) GetNames() string {
|
||||
n.Logger.Debug("GetNames method invoked")
|
||||
return fmt.Sprintf("Names: %v", names)
|
||||
}
|
||||
|
||||
func (n NameHandler) PostName(new NewName) string {
|
||||
n.Logger.Debugf("PostName method invoked with argument: %v", new)
|
||||
if new.InsertAtStart {
|
||||
names = append([]string{new.Name}, names...)
|
||||
} else {
|
||||
names = append(names, new.Name)
|
||||
}
|
||||
return fmt.Sprintf("Names: %v", names)
|
||||
}
|
||||
Reference in New Issue
Block a user