英文:
dynamically determine function parameters in Go
问题
有没有办法以编程方式确定Go函数的参数(序列和类型)?
我想将HTTP请求动态转换为方法/函数调用,以避免编写重复的编组代码(是的,我知道我需要非常小心地暴露哪些方法以及可能涉及的安全风险)。只有在我能够检查函数所期望的参数、从请求中解析适当的值,然后动态调用函数时,这才似乎是可能的(最后一步似乎会使用reflect.Value.Call - 至少这部分看起来很简单)。
编辑:能够这样做的优点是,您可以创建一个适用于直接在Go中使用以及远程使用的方法。例如,如果您有这样一个函数:
func UpdatePerson(p *Person) error { ... }
显然这在Go中可用;我希望能够通过HTTP将其外部公开。
使用json-rpc接近了这个目标,但看起来它可能需要一些更复杂的结构来进行输入/输出。
编辑2:基本上,我有一大堆CRUD要编写,我只想暴露DAO的部分功能,而不必编写所有这些疯狂的编组代码来创建另一个处理HTTP内容的层。(对于某些安全敏感的事情,我需要更加小心地编写代码,但我的许多DAO函数应该只能从浏览器中“直接调用” - 也可以从Go中调用。)json-rpc满足了第一个要求,但不一定满足第二个要求。
英文:
Is there a way to programmatically determine the parameters (sequence and types thereof) to a function in Go?
I would like to translate an HTTP request into a method/func call dynamically, to avoid having to write duplicative marshaling code (yes I know I'll need to be very careful about what methods I expose and the possible security risks involved). This would only seem to be possible if I can inspect the parameters a function expects, parse the appropriate values from the request and then dynamically call the function (looks like the last step would use reflect.Value.Call - at least that part seems straight forward).
EDIT: The advantage of being able to do this is that you can create a method that is suitable for use directly in Go as well as remotely. For example, if you have a func like this:
func UpdatePerson(p *Person) error { ... }
That obviously is usable in Go; and I want to be able to expose that externally via HTTP as well.
Using json-rpc comes close but looks like it would (in some cases) require some more crufty structures to be used for the i/o on it.
EDIT 2: Basically I have a whole shipload of CRUD to write and I want to just expose parts of the DAO without having to write all this crazy-ass marshaling code to make another layer that deals with the HTTP stuff. (For some security sensitive things I'll need to write things more carefully, but many of my DAO functions should literally just be callable "directly from the browser" - and also from Go.) json-rpc meets the first requirement, but not necessarily the second.
答案1
得分: 2
如果我理解你的意思,你正在寻找json-rpc
。下面是一个非常简单的使用示例:
package main
import (
"github.com/gorilla/rpc"
"github.com/gorilla/rpc/json"
"net/http"
)
type MyArgs struct {
Msg string
}
type MyResponse struct {
Msg string
}
type MyService struct{}
func (h *MyService) Test(r *http.Request, args *MyArgs, response *MyResponse) error {
response.Msg = "Received: " + args.Msg
return nil
}
func main() {
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterService(new(MyService), "")
http.Handle("/rpc", s)
http.ListenAndServe(":10000", nil)
}
使用curl进行测试:
curl -X POST -H "Content-Type: application/json" \
-d '{"method":"MyService.Test","params":[{"Msg":"Test"}], "id":"1"}' \
http://localhost:10000/rpc
响应:
{"result":{"Msg":"Received: Test"},"error":null,"id":"1"}
编辑:
似乎有一点混淆-你不需要在两端都使用Go-你可以使用jQuery从浏览器调用方法,像这样:
$.ajax({
url: '/rpc',
type:"POST",
data: JSON.stringify({"method":"MyService.Test","params":[{"Msg":"Test"}], "id":"1"}),
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(data){
console.log(data);
}
});
英文:
If I understand what you are trying to do, you are looking for json-rpc
. A very simple example of how to use it is below:
package main
import (
"github.com/gorilla/rpc"
"github.com/gorilla/rpc/json"
"net/http"
)
type MyArgs struct {
Msg string
}
type MyResponse struct {
Msg string
}
type MyService struct{}
func (h *MyService) Test(r *http.Request, args *MyArgs, response *MyResponse) error {
response.Msg = "Received: " + args.Msg
return nil
}
func main() {
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterService(new(MyService), "")
http.Handle("/rpc", s)
http.ListenAndServe(":10000", nil)
}
Test with curl:
curl -X POST -H "Content-Type: application/json" \
-d '{"method":"MyService.Test","params":[{"Msg":"Test"}], "id":"1"}' \
http://localhost:10000/rpc
Response:
{"result":{"Msg":"Received: Test"},"error":null,"id":"1"}
EDIT:
There seems to be a little confusion - you don't need Go on both ends - you can call the method from the browser with jQuery like so:
$.ajax({
url: '/rpc',
type:"POST",
data: JSON.stringify({"method":"MyService.Test","params":[{"Msg":"Test"}], "id":"1"}),
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(data){
console.log(data);
}
});
答案2
得分: 2
你可以使用reflect
包来确定函数的参数。例如:
// 参数是interface{}类型,这样我们可以接受任何签名的函数
func printArguments(f interface{}) {
t := reflect.TypeOf(f)
fmt.Println("函数参数:")
for i := 0; i < t.NumIn(); i++ {
fmt.Printf(" %d. %v\n", i, t.In(i))
}
fmt.Println("函数返回值:")
for i := 0; i < t.NumOut(); i++ {
fmt.Printf(" %d. %v\n", i, t.Out(i))
}
}
In
和Out
函数返回表示参数和返回值类型的reflect.Type
值。你可以使用这些类型信息构建一个Value
对象的参数向量来调用函数。
你可以在这里尝试这个示例:http://play.golang.org/p/qLThrI_Cw9
英文:
You can determine a function's parameters using the reflect
package. For example:
// argument is interface{} so we can accept functions of any signature
func printArguments(f interface{}) {
t := reflect.TypeOf(f)
fmt.Println("Function arguments:")
for i := 0; i < t.NumIn(); i++ {
fmt.Printf(" %d. %v\n", i, t.In(i))
}
fmt.Println("Function return values:")
for i := 0; i < t.NumOut(); i++ {
fmt.Printf(" %d. %v\n", i, t.Out(i))
}
}
The In
and Out
functions return reflect.Type
values representing the argument and return value types respectively. You can use this type information to construct an argument vector of Value
objects to call the function with.
You can play around with this example here: http://play.golang.org/p/qLThrI_Cw9
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论