5

I have this controller:

package web

import (
    "net/http"
)

func init() {

}

func (controller *Controller) Index(r *http.Request) (string, int) {
    return "Testing", http.StatusOK
}

With this handler:

type Application struct {
}

func (application *Application) Route(controller interface{}, route string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {

        var ptr reflect.Value
        var value reflect.Value
        var finalMethod reflect.Value

        value = reflect.ValueOf(controller)

        // if we start with a pointer, we need to get value pointed to
        // if we start with a value, we need to get a pointer to that value
        if value.Type().Kind() == reflect.Ptr {
            ptr = value
            value = ptr.Elem()
        } else {
            ptr = reflect.New(reflect.TypeOf(controller))
            temp := ptr.Elem()
            temp.Set(value)
        }

        // check for method on value
        method := value.MethodByName(route)
        if method.IsValid() {
            finalMethod = method
        }
        // check for method on pointer
        method = ptr.MethodByName(route)
        if method.IsValid() {
            finalMethod = method
        }

        methodInterface := finalMethod.Call([]reflect.Value{})[0].Interface()
        method_route := methodInterface.(func(r *http.Request) (string, int))
        body, code := method_route(r)
        switch code {
        case http.StatusOK:
            io.WriteString(w, body)
        case http.StatusSeeOther, http.StatusFound:
            http.Redirect(w, r, body, code)
        default:
            w.WriteHeader(code)
            io.WriteString(w, body)
        }
    }
}

And it is executed in this way:

controller := &web.Controller{}
application := &system.Application{}

http.HandleFunc("/", application.Route(controller, "Index"))

The problem is it compiled ok. It does not show any error, but when I go to the website, just by pointing at localhost, it shows:

2014/12/27 22:38:16 http: panic serving 127.0.0.1:58304: reflect: Call with too few input arguments
goroutine 20 [running]:
net/http.func·011()
    /usr/local/Cellar/go/1.3.3/libexec/src/pkg/net/http/server.go:1100 +0xb7

I cannot find any error, and it is more strange it compiles ok... I'm new in Go, so I've no idea what is going on...

Cito
  • 1,659
  • 3
  • 22
  • 49

2 Answers2

3

I have found the answer by reading this:

https://stackoverflow.com/a/20715067/1339973

So instead of trying to call the method:

    methodInterface := finalMethod.Call([]reflect.Value{})[0].Interface()
    method_route := methodInterface.(func(r *http.Request) (string, int))
    body, code := method_route(r)

I just get the interface I need, then convert it into a function and call it as such.

    methodInterface := finalMethod.Interface()
    method_route := methodInterface.(func(r *http.Request) (string, int))
    body, code := method_route(r)

Actually, that is kind of what I was already doing, but in the wrong way.

Community
  • 1
  • 1
Cito
  • 1,659
  • 3
  • 22
  • 49
0

As explained in "How to properly use .Call in reflect package, Golang?", and in reflect#Value.Call(), you need a slice with at least 1 element of the right type in it, if your function takes one parameter.

If you know the exact parameter type and value, you need to create it, and build your Call parameter:

 in := []reflect.Value{reflect.ValueOf(m)}

The exception "reflect: Call with too few input arguments" is called after checking the number of parameters expected by the function

NumIn returns a function type's input parameter count.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thanks a lot for your help. As you read in my question, I'm new. So I get, sort of, your answer. In the code you can see the argument it receives is a http request, so it will be different in each request. How can I build it in the call? Thanks again! – Cito Dec 29 '14 at 15:56
  • @Cito not sure, considering you *need* to pass the right list of parameters for each function `Call()`. Unless the function name is clue enough for you to guess the right value to pass, that could be an issue. – VonC Dec 29 '14 at 16:19
  • 1
    I've done it like this: `methodInterface := finalMethod.Call([]reflect.Value{reflect.ValueOf(r)})[0].Interface()` But now the error is: `interface conversion: interface is string, not func(*http.Request) (string, int)` – Cito Dec 29 '14 at 16:41
  • Thanks for your question. You pointed me in the right direction. By reading this question: http://stackoverflow.com/a/20715067/1339973 I've been able to fix it. – Cito Dec 29 '14 at 16:54