处理包和函数名称的动态性

huangapple go评论90阅读模式
英文:

Handling package and function names dynamically

问题

我正在进行我的第一个Golang项目,其中包括一个用于MVC结构的小型路由器。基本上,我希望它能够接收请求的URL,将其分解成块,并将执行流程转发到这些块中指定的包和函数,当应用程序中没有匹配这些值时,提供某种回退。

另一种方法是将所有包和函数映射到变量中,然后在这些变量的内容中查找匹配项,但这不是一种动态解决方案。

我在其他语言(如PHP和JS)中使用的替代方法是在语法上引用这些名称,其中语言以某种方式考虑变量的值而不是考虑其字面值。类似于:{packageName}.{functionName}()。问题是我还没有找到在Golang中以这种语法方式实现的方法。

有什么建议吗?

func ParseUrl(request *http.Request) {

	//out of this URL http://www.example.com/controller/method/key=2&anotherKey=3

	var requestedFullURI = request.URL.RequestURI()       // returns '/controller/method?key=2key=2&anotherKey=3'
	controlFlowString, _ := url.Parse(requestedFullURI)
	substrings := strings.Split(controlFlowString.Path, "/")       // returns ["controller","method"]
	
	if len(substrings[1]) > 0 {

		// Here  we'll check if substrings[1] mathes an existing package(controller) name
        // and provide some error return in case it does not

		if len(substrings[2]) > 0 {
			// check if substrings[2] mathes an existing function name(method) inside 
            // the requested package and run it, passing on the control flow
		}else{
			// there's no requested method, we'll just run some fallback
		}
	} else {
		err := errors.New("You have not determined a valid controller.")
		fmt.Println(err)
	}

}
英文:

I'm on my first Golang project, which consists of a small router for an MVC structure. Basically, what I hope it does is take the request URL, separate it into chunks, and forward the execution flow to the package and function specified in those chunks, providing some kind of fallback when there is no match for these values in the application .

An alternative would be to map all packages and functions into variables, then look for a match in the contents of those variables, but this is not a dynamic solution.

The alternative I have used in other languages (PHP and JS) is to reference those names sintatically, in which the language somehow considers the value of the variable instead of considering its literal. Something like: {packageName}.{functionName}() . The problem is that I still haven't found any syntactical way to do this in Golang.

Any suggestions?

func ParseUrl(request *http.Request) {

	//out of this URL http://www.example.com/controller/method/key=2&anotherKey=3

	var requestedFullURI = request.URL.RequestURI()       // returns '/controller/method?key=2key=2&anotherKey=3'
	controlFlowString, _ := url.Parse(requestedFullURI)
	substrings := strings.Split(controlFlowString.Path, "/")       // returns ["controller","method"]
	
	if len(substrings[1]) > 0 {

		// Here  we'll check if substrings[1] mathes an existing package(controller) name
        // and provide some error return in case it does not

		if len(substrings[2]) > 0 {
			// check if substrings[2] mathes an existing function name(method) inside 
            // the requested package and run it, passing on the control flow
		}else{
			// there's no requested method, we'll just run some fallback
		}
	} else {
		err := errors.New("You have not determined a valid controller.")
		fmt.Println(err)
	}

}

答案1

得分: 2

你仍然可以以半动态的方式解决这个问题。将你的处理程序定义为空结构体的方法,并仅注册该结构体。这将大大减少你需要进行的注册数量,使你的代码更加明确和可读。例如:

handler.register(MyStruct{}) // 实现部分是另一个问题

以下代码展示了使MyStruct的所有方法可通过名称访问所需的全部内容。现在,通过一些努力和使用反射包的帮助,你可以支持像MyStruct/SomeMethod这样的路由。你甚至可以定义带有一些字段的结构体,这些字段可以作为分支,因此甚至可以实现MaStruct/NestedStruct/SomeMethod

请不要这样做

你的想法可能听起来不错,但相信我,这并不是一个好主意。最好使用像go-chi这样的框架,它比进行一些无法理解的反射操作更灵活和可读。更不用说在Go中遍历类型树从来都不是一项快速任务。你的路由不应该由后端结构体的名称来定义。如果你坚持这样做,最终你将得到一些奇怪命名的路由,它们使用的是PascalCase而不是像something-like-this这样的命名方式。

英文:

You can still solve this in half dynamic manner. Define your handlers as methods of empty struct and register just that struct. This will greatly reduce amount of registrations you have to do and your code will be more explicit and readable. For example:

handler.register(MyStruct{}) // the implementation is for another question

Following code shows all that's needed to make all methods of MyStruct accessible by name. Now with some effort and help of reflect package you can support the routing like MyStruct/SomeMethod. You can even define struct with some fields witch can serve as branches so even MaStruct/NestedStruct/SomeMethod is possible to do.

dont do this please

Your idea may sound like a good one but believe me its not. Its lot better to use framework like go-chi that is more flexible and readable then doing some reflect madness that no one will understand. Not to mention that traversing type trees in go was newer the fast task. Your routes should not be defined by names of structures in your backend. When you commit into this you will end up with strangely named routes that use PascalCase instead of something-like-this.

答案2

得分: 0

你所描述的情况非常典型的是PHP和JavaScript的特点,而对于Go来说则完全不适用。PHP和JavaScript是动态解释型语言,而Go是一种静态编译语言。与其试图应用不适合的习惯用法,我建议你寻找更适合当前语言的实现方式来实现相同的目标。

在这种情况下,我认为在保持代码合理性的同时,最接近你所描述的方式是使用一个处理程序注册表,就像你描述的那样,在包的init()函数中自动注册。每个init函数在启动时只会被调用一次,给予包初始化变量和注册处理程序和驱动程序的机会。当你看到需要导入数据库驱动程序包但又没有引用它们的情况时,正是因为init函数的存在:导入包给予它注册驱动程序的机会。甚至expvar包也是这样注册HTTP处理程序的。

你可以对处理程序做同样的事情,给每个包添加一个init函数,用于注册该包的处理程序及其路由。虽然这不是“动态”的,但在这里动态性没有任何价值——代码在编译后无法更改,这意味着动态性只会导致执行速度变慢。如果“动态”的路由发生变化,你仍然需要重新编译和重新启动。

英文:

What you're describing is very typical of PHP and JavaScript, and completely inappropriate to Go. PHP and JavaScript are dynamic, interpreted languages. Go is a static, compiled language. Rather than trying to apply idioms which do not fit, I'd recommend looking for ways to achieve the same goals using implementations more suitable to the language at hand.

In this case, I think the closest you get to what you're describing while still maintaining reasonable code would be to use a handler registry as you described, but register to it automatically in package init() functions. Each init function will be called once, at startup, giving the package an opportunity to initialize variables and register things like handlers and drivers. When you see things like database driver packages that need to be imported even though they're not referenced, init functions are why: importing the package gives it the chance to register the driver. The expvar package even does this to register an HTTP handler.

You can do the same thing with your handlers, giving each package an init function that registers the handler(s) for that package along with their routes. While this isn't "dynamic", being dynamic has zero value here - the code can't change after it's compiled, which means that all you get from being dynamic is slower execution. If the "dynamic" routes change, you'd have to recompile and restart anyway.

huangapple
  • 本文由 发表于 2021年6月4日 19:51:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/67836749.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定