【问题标题】:dynamically determine function parameters in Go在 Go 中动态确定函数参数
【发布时间】:2014-11-05 02:17:18
【问题描述】:

有没有办法以编程方式确定 Go 中函数的参数(序列及其类型)?

我想将 HTTP 请求动态转换为方法/函数调用,以避免编写重复的编组代码(是的,我知道我需要非常小心我公开的方法以及可能涉及的安全风险)。如果我可以检查函数期望的参数,从请求中解析适当的值然后动态调用函数,这似乎才有可能(看起来最后一步将使用 reflect.Value.Call - 至少那部分看起来很直接前进)。

编辑:能够做到这一点的好处是您可以创建一个适合直接在 Go 中以及远程使用的方法。例如,如果你有一个这样的函数:

func UpdatePerson(p *Person) error { ... }

这显然可以在 Go 中使用;我也希望能够通过 HTTP 向外部公开它。

使用 json-rpc 很接近,但看起来它(在某些情况下)需要一些更复杂的结构来用于其上的 i/o。

编辑 2:基本上我有一大堆 CRUD 要编写,我只想公开 DAO 的一部分,而不必编写所有这些疯狂的编组代码来创建另一个处理 HTTP 内容的层。 (对于一些安全敏感的东西,我需要更仔细地编写东西,但我的许多 DAO 函数实际上应该可以“直接从浏览器”调用 - 也可以从 Go 调用。) json-rpc 满足第一个要求,但不必然是第二个。

【问题讨论】:

  • 不——该函数需要在某个地方引用,否则它可能会在构建过程中被删除。为此,我认为没有办法仅通过查询运行时来动态发现(可能丢弃的)函数。但是,有 runtime.FuncForPC .. 但这是静态检查的,需要您将函数传递给它。还有一些方法可以通过堆栈跟踪提取函数的详细信息。但同样,如果您计划使用动态分辨率,这对您没有太大帮助。
  • 明白了。如果我有对 func 本身的引用(因此它不会在链接阶段丢失)但没有调用它,例如: var MyFuncs = map[string]interface{} { "func1": func1 } Is there有什么反映让我在运行时更详细地看一下?
  • 我不知道......不。 reflect 包处理类型 .. 不是包。不过,您也许可以调查reflect.MakeFunc 以动态调度您的电话(我自己没有使用过它......不过看起来很有希望)。

标签: function reflection go


【解决方案1】:

您可以使用reflect package 确定函数的参数。例如:

// 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))
    }
}

InOut 函数分别返回代表参数和返回值类型的 reflect.Type 值。您可以使用此类型信息来构造 Value 对象的参数向量以调用函数。

你可以在这里玩这个例子:http://play.golang.org/p/qLThrI_Cw9

【讨论】:

  • 完美,这正是我想要的。
【解决方案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);
  }
});

【讨论】:

  • 我明白你的意思 - 是的,这绝对是我没有考虑过的一个选项。
  • 啊,是的——这也是我没有考虑到的。
  • 为什么响应看起来像这样{"Msg":"Received: Test"}。由于“Msg”和“Test”都是参数,并且参数被连接到现有字符串"Received: " + args.Msg,我希望“Msg”和“Test”都出现在响应中收到之后,更像是{Received : 消息 : 测试 }
  • @Leahcim - 因为我们做了response.Msg = "Received: " + args.Msg,它变成了字符串"Received: test"
  • @dave 所以“Msg”是键,“Test”是值,您可以通过 response.Msg? 访问该值?我看不到 "Msg": 在 Received 结果之前是如何结束的?
猜你喜欢
  • 2020-03-23
  • 1970-01-01
  • 2021-01-28
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 2011-11-12
相关资源
最近更新 更多