How to instantiate value of unknown type in Go?

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

How to instantiate value of unknown type in Go?

问题

我在golang中开发了一些服务器。我试图创建一些包装函数,以便将来能够帮助我。

我有以下内容:

1)我有一些DTO结构体,例如:

type Request struct {
    Field1   string `json:"field1"`
    Field2   string `json:"field2"`
}

type Response struct {
    Field1   string `json:"field1"`
    Field2   string `json:"field2"`
    Field3   string `json:"field3"`
}

2)我有一些函数,在控制器层中,按照约定,它们接收一个参数(指向结构体的指针)并返回一个结果(指向结构体的指针),例如:

func SomeHandler(request *Request) *Response{
    ...做一些事情
    return &Response{"first","second","third"}
}

我需要:

我需要编写一个包装函数,它接收以下参数:

  1. 'controller'函数的指针
  2. http.ResponseWriter
  3. *http.Request

这个包装函数必须:

  1. 确定'controller'函数的参数类型
  2. 确定'controller'函数的结果类型
  3. 从*http.Request的Body中实例化并填充参数值(从json解码)
  4. 使用在上一步中实例化的参数调用控制器函数
  5. 将上一步的结果写入http.ResponseWriter(以json格式编码)

包装函数必须能够正确处理任何类型的'controller'函数-这些函数的签名是不同的(参数类型不同,结果类型不同)

有人可以帮我实现这个包装函数吗?

英文:

I develop some server in golang. I try to create some wrapper-function, which can helps me in future.

What I have:

  1. I had some DTO structs, for example:

    type Request struct {
    Field1 string json:"field1"
    Field2 string json:"field2"
    }

    type Response struct {
    Field1 string json:"field1"
    Field2 string json:"field2"
    Field3 string json:"field3"
    }

  2. I had some functions, in controller layer, which (by conventions) receives 1 argument (pointer to struct) and returns 1 result (pointer to struct), for example:

    func SomeHandler(request *Request) *Response{
    ...do something
    return &Response{"first","second","third"}
    }

What I need:

I need to write wrapper function which receives as argument:
<ol>
<li>pointer to 'controller' function</li>
<li>http.ResponseWriter</li>
<li>*http.Request</li>
</ol>
This wrapper function must:
<ol>
<li>Determine type of argument of 'controller' function</li>
<li>Determine type of result of 'controller' function</li>
<li>Instantiate and fill argument value from Body of *http.Request (decode from json) </li>
<li>Call controller Function with instantiated on previous step argument</li>
<li>Write results of previous step into http.ResponseWriter (encoded as json)</li>
</ol>

Wrapper must work correct with any types of 'controller' functions - signatures of this functions is different (different argument type, different result type)

Can anybody help me with implementing of this wrapper?

答案1

得分: 2

你正在做一些有点奇怪的事情,但reflect可以提供你所需的所有信息。

func myFunction(a string, b int32) error {
    return nil
}

func myWrapper(mystery interface{}) {
    typ := reflect.TypeOf(mystery)

    // 在操作 In(i) 之前,先检查 typ.Kind;
    for i := 0; i < typ.NumIn(); i++ {
        fmt.Printf("Param %d: %v\n", i, typ.In(i))
    }
    for i := 0; i < typ.NumOut(); i++ {
        fmt.Printf("Result %d: %v\n", i, typ.Out(i))
    }
}

这将打印出:

Param 0: string
Param 1: int32
Result 0: error
英文:

What you're doing is a bit weird but reflect can provide all the info you need.

func myFunction(a string, b int32) error {
	return nil
}

func myWrapper(mystery interface{}) {
	typ := reflect.TypeOf(mystery)

    // Check typ.Kind before playing with In(i);
	for i := 0; i &lt; typ.NumIn(); i++ {
		fmt.Printf(&quot;Param %d: %v\n&quot;, i, typ.In(i))
	}
	for i := 0; i &lt; typ.NumOut(); i++ {
		fmt.Printf(&quot;Result %d: %v\n&quot;, i, typ.Out(i))
	}

}

This prints:

Param 0: string
Param 1: int32
Result 0: error

答案2

得分: 0

应该可以使用类似这样的代码(未经测试):

func wrapper(ctlr interface{}, w http.ResponseWriter, r *http.Request) error {

    tpe := reflect.TypeOf(ctlr)
    if tpe.Kind() != reflect.Func || tpe.NumIn() != 1 || tpe.NumOut() != 1 {
        // TODO: 处理错误的 ctlr 类型
    }

    // 1. 确定 'controller' 函数的参数类型
    argt := tpe.In(0)

    // 2. 确定 'controller' 函数的返回类型
    // rest := tpe.Out(0) // 这一步是不必要的,可以注释掉

    // 3. 从 *http.Request 的 Body 中实例化并填充参数值(从 json 解码)
    arg := reflect.Zero(argt)
    err := json.NewDecoder(r.Body).Decode(&arg)
    if err != nil {
        // TODO: 处理错误
    }

    // 4. 使用前一步实例化的参数调用 controller 函数
    resv := reflect.ValueOf(ctlr).Call([]reflect.Value{arg})[0]

    // 5. 将前一步的结果写入 http.ResponseWriter(以 json 编码)
    err = json.NewEncoder(w).Encode(resv.Interface())
    if err != nil {
        // TODO: 处理错误
    }

    return nil
}

请注意,第二步是不必要的。

英文:

Something like this should work (untested):

func wrapper(ctlr interface{}, w http.ResponseWriter, r *http.Request) error {

	tpe := reflect.TypeOf(ctlr)
	if tpe.Kind() != reflect.Func || tpe.NumIn() != 1 || tpe.NumOut() != 1 {
		// TODO: handle wrong ctlr type
	}

	// 1. Determine type of argument of &#39;controller&#39; function
	argt := tpe.In(0)

	// 2. Determine type of result of &#39;controller&#39; function
	// rest := tpe.Out(0) // commented out since it&#39;s useless

	// 3. Instantiate and fill argument value from Body of *http.Request (decode from json)
	arg := reflect.Zero(argt)
	err := json.NewDecoder(r.Body).Decode(&amp;arg)
	if err != nil {
		// TODO: handle err
	}

	// 4. Call controller Function with instantiated on previous step argument
	resv := reflect.ValueOf(ctlr).Call([]reflect.Value{arg})[0]

	// 5. Write results of previous step into http.ResponseWriter (encoded as json)
	err = json.NewEncoder(w).Encode(resv.Interface())
	if err != nil {
		// TODO: handle err
	}

	return nil
}

Note that the second step is unnecessary.

huangapple
  • 本文由 发表于 2017年1月19日 02:39:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/41727025.html
匿名

发表评论

匿名网友

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

确定