Golang Map of http.ResponseWriters - go

I'm trying to create a map that stores http.ResponseWriters so that I can write to them later, after a separate thread has done the relevant computation.
The map is defined in my main as follow:
jobs := make(map[uint32]http.ResponseWriter)
I then pass this map to a handle function like so:
r.HandleFunc("/api/{type}/{arg1}", func(w http.ResponseWriter, r *http.Request) {
typ, _ := strconv.Atoi(mux.Vars(r)["type"])
AddReqQueue(w, ReqQueue, typ, mux.Vars(r)["arg1"], jobs, ids)
}).Methods("get")
After that I process the reuqeuest and add it to a channel:
func AddReqQueue(w http.ResponseWriter, ReqQueue chan mssg.WorkReq, typ int, arg1 string, jobs map[uint32]http.ResponseWriter, ids []uint32) {
var id uint32
id, ids = ids[0], ids[1:] // get a free work id
jobs[id] = w
fmt.Println("Adding req to queue")
ReqQueue <- mssg.WorkReq{Type: uint8(typ), Arg1: arg1, WId: id}
}
Inside this function I have tested and am able to write data to the ReponseWriter, however later when I try to use the map in:
func SendResp(RespQueue chan mssg.WorkResp, jobs map[uint32]http.ResponseWriter) {
for {
resp := <-RespQueue
jobs[resp.WId].Header().Set("Content-Type", "text/plain")
_, err := jobs[resp.WId].Write(resp.Data) // ERROR IS COMING FROM HERE
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
}
}
}
It doesn't work. No matter what I pre-set the header too or try to write (even just a simple string I hardcoded) I get a err of
Conn.Write wrote more than the declared Content-Length
I know I'm accessing the right struct in the map, it just seems as if the ReponseWriter has gone out of context or been corrupted, also i know that the headers shouldn't really matter since it is the first time I am calling Write() and therefore it should create the headers for me.

#elithrar was correct. I wasn't aware that the http.ResponseWriter object became invalid after the handler exited. If I just force my handler to wait, it works fine.

Related

Mock/test basic http.get request

I am leaning to write unit tests and I was wondering the correct way to unit test a basic http.get request.
I found an API online that returns fake data and wrote a basic program that gets some user data and prints out an ID:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type UserData struct {
Meta interface{} `json:"meta"`
Data struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Gender string `json:"gender"`
Status string `json:"status"`
} `json:"data"`
}
func main() {
resp := sendRequest()
body := readBody(resp)
id := unmarshallData(body)
fmt.Println(id)
}
func sendRequest() *http.Response {
resp, err := http.Get("https://gorest.co.in/public/v1/users/1841")
if err != nil {
log.Fatalln(err)
}
return resp
}
func readBody(resp *http.Response) []byte {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
return body
}
func unmarshallData(body []byte) int {
var userData UserData
json.Unmarshal(body, &userData)
return userData.Data.ID
}
This works and prints out 1841. I then wanted to write some tests that validate that the code is behaving as expected, e.g. that it correctly fails if an error is returned, that the data returned can be unmarshalled. I have been reading online and looking at examples but they are all far more complex that what I feel I am trying to achieve.
I have started with the following test that ensures that the data passed to the unmarshallData function can be unmarshalled:
package main
import (
"testing"
)
func Test_unmarshallData(t *testing.T) {
type args struct {
body []byte
}
tests := []struct {
name string
args args
want int
}{
{name: "Unmarshall", args: struct{ body []byte }{body: []byte("{\"meta\":null,\"data\":{\"id\":1841,\"name\":\"Piya\",\"email\":\"priya#gmai.com\",\"gender\":\"female\",\"status\":\"active\"}}")}, want: 1841},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := unmarshallData(tt.args.body); got != tt.want {
t.Errorf("unmarshallData() = %v, want %v", got, tt.want)
}
})
}
}
Any advise on where to go from here would be appreciated.
before moving on to the testing, your code has a serious flow, which will become a problem if you don't take care about it in your future programming tasks.
https://pkg.go.dev/net/http See the second example
The client must close the response body when finished with it
Let's fix that now (we will have to come back on this subject later), two possibilities.
1/ within main, use defer to Close that resource after you have drained it;
func main() {
resp := sendRequest()
defer body.Close()
body := readBody(resp)
id := unmarshallData(body)
fmt.Println(id)
}
2/ Do that within readBody
func readBody(resp *http.Response) []byte {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
return body
}
Using a defer is the expected manner to close the resource. It helps the reader to identify the lifetime span of the resource and improve readability.
Notes : I will not be using much of the table test driven pattern, but you should, like you did in your OP.
Moving on to the testing part.
Tests can be written under the same package or its fellow version with a trailing _test, such as [package target]_test. This has implications in two ways.
Using a separate package, they will be ignored in the final build. Which will help to produce smaller binaries.
Using a separate package, you test the API in a black box manner, you can access only the identifiers it explicitly exposes.
Your current tests are white boxed, meaning you can access any declaration of main, public or not.
About sendRequest, writing a test around this is not very interesting because it does too little, and your tests should not be written to test the std library.
But for the sake of the demonstration, and for good reasons we might want to not rely on external resources to execute our tests.
In order to achieve that we must make the global dependencies consumed within it, an injected dependency. So that later on, it is possible to replace the one thing it depends on to react, the http.Get method.
func sendRequest(client interface{Get() (*http.Response, error)}) *http.Response {
resp, err := client.Get("https://gorest.co.in/public/v1/users/1841")
if err != nil {
log.Fatalln(err)
}
return resp
}
Here i use an inlined interface declaration interface{Get() (*http.Response, error)}.
Now we can add a new test which injects a piece of code that will return exactly the values that will trigger the behavior we want to test within our code.
type fakeGetter struct {
resp *http.Response
err error
}
func (f fakeGetter) Get(u string) (*http.Response, error) {
return f.resp, f.err
}
func TestSendRequestReturnsNilResponseOnError(t *testing.T) {
c := fakeGetter{
err: fmt.Errorf("whatever error will do"),
}
resp := sendRequest(c)
if resp != nil {
t.Fatal("it should return a nil response when an error arises")
}
}
Now run this test and see the result. It is not conclusive because your function contains a call to log.Fatal, which in turns executes an os.Exit; We cannot test that.
If we try to change that, we might think we might call for panic instead because we can recover.
I don't recommend doing that, in my opinion, this is smelly and bad, but it exists, so we might consider. This is also the least possible change to the function signature. Returning an error would break even more the current signatures. I want to minimize this for that demonstration. But, as a rule of thumb, return an error and always check them.
In the sendRequest function, replace this call log.Fatalln(err) with panic(err) and update the test to capture the panic.
func TestSendRequestReturnsNilResponseOnError(t *testing.T) {
var hasPanicked bool
defer func() {
_ = recover() // if you capture the output value or recover, you get the error gave to the panic call. We have no use of it.
hasPanicked = true
}()
c := fakeGetter{
err: fmt.Errorf("whatever error will do"),
}
resp := sendRequest(c)
if resp != nil {
t.Fatal("it should return a nil response when an error arises")
}
if !hasPanicked {
t.Fatal("it should have panicked")
}
}
We can now move on to the other execution path, the non error return.
For that we forge the desired *http.Response instance we want to pass into our function, we will then check its properties to figure out if what the function does is inline with what we expect.
We will consider we want to ensure it is returned unmodified : /
Below test only sets two properties, and I will do it to demonstrate how to set the Body with a NopCloser and strings.NewReader as it is often needed later on using the Go language;
I also use reflect.DeepEqual as brute force equality checker, usually you can be more fine grained and get better tests. DeepEqual does the job in this case but it introduces complexity that does not justify systematic use of it.
func TestSendRequestReturnsUnmodifiedResponse(t *testing.T) {
c := fakeGetter{
err: nil,
resp: &http.Response{
Status: http.StatusOK,
Body: ioutil.NopCloser(strings.NewReader("some text")),
},
}
resp := sendRequest(c)
if !reflect.DeepEqual(resp, c.resp) {
t.Fatal("the response should not have been modified")
}
}
At that point you may have figured that this small function sendRequest is not good, if you did not I ensure you it is not. It does too little, it merely wraps the http.Get method and its testing is of little interest for the survival of the business logic.
Moving on to readBody function.
All remarks that applied for sendRequest apply here too.
it does too little
it os.Exits
One thing does not apply. As the call to ioutil.ReadAll does not rely on external resources, there is no point in attempting to inject that dependency. We can test around.
Though, for the sake of the demonstration, it is the time to talk about the missing call to defer resp.Body.Close().
Let us assume we go for the second proposition made in introduction and test for that.
The http.Response struct adequately exposes its Body recipient as an interface.
To ensure the code calls for the `Close, we can write a stub for it.
That stub will record if that call was made, the test can then check for that and trigger an error if it was not.
type closeCallRecorder struct {
hasClosed bool
}
func (c *closeCallRecorder) Close() error {
c.hasClosed = true
return nil
}
func (c *closeCallRecorder) Read(p []byte) (int, error) {
return 0, nil
}
func TestReadBodyCallsClose(t *testing.T) {
body := &closeCallRecorder{}
res := &http.Response{
Body: body,
}
_ = readBody(res)
if !body.hasClosed {
t.Fatal("the response body was not closed")
}
}
Similarly, and for the sake of the demonstration, we might want to test if the function has called for Read.
type readCallRecorder struct {
hasRead bool
}
func (c *readCallRecorder) Read(p []byte) (int, error) {
c.hasRead = true
return 0, nil
}
func TestReadBodyHasReadAnything(t *testing.T) {
body := &readCallRecorder{}
res := &http.Response{
Body: ioutil.NopCloser(body),
}
_ = readBody(res)
if !body.hasRead {
t.Fatal("the response body was not read")
}
}
We an also verify the body was not modified in betwen,
func TestReadBodyDidNotModifyTheResponse(t *testing.T) {
want := "this"
res := &http.Response{
Body: ioutil.NopCloser(strings.NewReader(want)),
}
resp := readBody(res)
if got := string(resp); want != got {
t.Fatal("invalid response, wanted=%q got %q", want, got)
}
}
We have almost done, lets move one to the unmarshallData function.
You have already wrote a test about it. It is okish, though, i would write it this way to make it leaner:
type UserData struct {
Meta interface{} `json:"meta"`
Data Data `json:"data"`
}
type Data struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Gender string `json:"gender"`
Status string `json:"status"`
}
func Test_unmarshallData(t *testing.T) {
type args struct {
body []byte
}
tests := []UserData{
UserData{Data: Data{ID: 1841}},
}
for _, u := range tests {
want := u.ID
b, _ := json.Marshal(u)
t.Run("Unmarshal", func(t *testing.T) {
if got := unmarshallData(b); got != want {
t.Errorf("unmarshallData() = %v, want %v", got, want)
}
})
}
}
Then, the usual apply :
don't log.Fatal
what are you testing ? the marshaller ?
Finally, now that we have gathered all those pieces, we can refactor to write a more sensible function and re use all those pieces to help us testing such code.
I won't do it, but here is a starter, which still panics, and I still don't recommend, but the previous demonstration has shown everything needed to test a version of it that returns an error.
type userFetcher struct {
Requester interface {
Get(u string) (*http.Response, error)
}
}
func (u userFetcher) Fetch() int {
resp, err := u.Requester.Get("https://gorest.co.in/public/v1/users/1841") // it does not really matter that this string is static, using the requester we can mock the response, its body and the error.
if err != nil {
panic(err)
}
defer resp.Body.Close() //always.
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
var userData UserData
err = json.Unmarshal(body, &userData)
if err != nil {
panic(err)
}
return userData.Data.ID
}

Tracing golang http request

I want to trace complete execution of an HTTP request in golang. For any non trivial operation the request will eventually call many different functions. I would like to have logs from the entire execution stack tagged with a unique request id. e.g.
http.Handle("/my-request", myrequesthandler)
func myrequestHandler(w http.ResponseWriter, r *http.Request) {
//debug print1
log.Printf("....")
myfunc()
}
func myfunc() {
//debug print2
log.Printf("....")
}
Here I need a way to identify print1 and print2 as part of same request. It looks like zerolog does have something like this, as described here. Like so:
....
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hlog.FromRequest(r).Info().
Msg("Something happened")
}))
http.Handle("/", h)
But if I understand it right, it will involve passing request object to each and every function. Is this the idiomatic way to solve this problem? What are the alternatives?
Set a unique id on a context.Context as soon as the request is received, and pass that context down the call stack. This is what contexts are for.
[Context] carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Example:
// You could place this in a separate helper package to improve encapsulation
type ctxKey struct{}
func myRequestHandler(w http.ResponseWriter, r *http.Request) {
uniqueID := // generate a unique identifier
// create a context with the http.Request context as parent
ctx := context.WithValue(r.Context(), ctxKey{}, uniqueID)
foo(ctx, ...)
bar(ctx, ...)
}
func foo(ctx context.Context, ...) {
uniqueID := ctx.Value(ctxKey{})
// do something with the unique id
baz(ctx, ...)
}
In particular:
Create the context with *http.Request.Context() as parent. This way if the request is canceled, e.g. due to client disconnection, cancellation will propagate to your sub-context
Consider setting the context value using an unexported struct as key. Unexported structs defined in your package will never conflict with other keys. If you use strings as keys instead, any package could in theory use the same key and overwrite your value (or you could overwrite others' values). YMMV.
Pass your context as the first argument of any function in your call stack, as the package documentation recommends.
For tracing and logging across applications, you might want to look into opentracing. Propagation of tracers is still done with Contexts as outlined above.
you can use context.Context and set request id on it via middleware function.
example:
type requestIDKey struct{}
func requestIDSetter(next http.HandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
// use provided request id from incoming request if any
reqID := r.Header.Get("X-Request-ID")
if reqID == "" {
// or use some generated string
reqID = uuid.New().String()
}
ctx := context.WithValue(r.Context(), requestIDKey{}, reqID)
next(rw, r.WithContext(ctx))
}
}
then you need to modify your logger to accept context.Context
example:
func printfWithContext(ctx context.Context, format string, v ...interface{}) {
reqID := ctx.Value(requestIDKey{}).(string)
log.Printf(reqID+": "+format, v...)
}
and finally apply to your code
http.HandleFunc("/my-request", requestIDSetter(myrequestHandler))
func myrequestHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
//debug print1
printfWithContext(ctx, "....1")
myfunc(ctx)
}
func myfunc(ctx context.Context) {
//debug print2
printfWithContext(ctx, "....2")
}

Is it a good practice to use use the same httptest server for multiple methods

I am trying to test some golang code and I have a method that calls several other methods from its body. All these methods perform some kind of operations using an elastic search client. I wanted to know whether it will be a good practice if I used a test server for testing this method that will write different responses depending upon the request method and path it received from the request that is made when the methods inside the body execute and make calls to the elasticsearch client that sends the requests to my test server?
Update:
I am testing an elasticsearch middleware. It implements a reindex service like this
type reindexService interface {
reindex(ctx context.Context, index string, mappings, settings map[string]interface{}, includes, excludes, types []string) error
mappingsOf(ctx context.Context, index string) (map[string]interface{}, error)
settingsOf(ctx context.Context, index string) (map[string]interface{}, error)
aliasesOf(ctx context.Context, index string) ([]string, error)
createIndex(ctx context.Context, name string, body map[string]interface{}) error
deleteIndex(ctx context.Context, name string) error
setAlias(ctx context.Context, index string, aliases ...string) error
getIndicesByAlias(ctx context.Context, alias string) ([]string, error)
}
I can easily test all the methods using this pattern. Creating a simple elastic search client using a httptest server url and making requests to that server
var createIndexTests = []struct {
setup *ServerSetup
index string
err string
}{
{
&ServerSetup{
Method: "PUT",
Path: "/test",
Body: `null`,
Response: `{"acknowledged": true, "shards_acknowledged": true, "index": "test"}`,
},
"test",
"",
},
// More test cases here
}
func TestCreateIndex(t *testing.T) {
for _, tt := range createIndexTests {
t.Run("Should successfully create index with a valid setup", func(t *testing.T) {
ctx := context.Background()
ts := buildTestServer(t, tt.setup)
defer ts.Close()
es, _ := newTestClient(ts.URL)
err := es.createIndex(ctx, tt.index, nil)
if !compareErrs(tt.err, err) {
t.Fatalf("Index creation should have failed with error: %v got: %v instead\n", tt.err, err)
}
})
}
}
But in case of reindex method this approach poses a problem since reindex makes calls to all the other methods inside its body. reindex looks something like this:
func (es *elasticsearch) reindex(ctx context.Context, indexName string, mappings, settings map[string]interface{}, includes, excludes, types []string) error {
var err error
// Some preflight checks
// If mappings are not passed, we fetch the mappings of the old index.
if mappings == nil {
mappings, err = es.mappingsOf(ctx, indexName)
// handle err
}
// If settings are not passed, we fetch the settings of the old index.
if settings == nil {
settings, err = es.settingsOf(ctx, indexName)
// handle err
}
// Setup the destination index prior to running the _reindex action.
body := make(map[string]interface{})
body["mappings"] = mappings
body["settings"] = settings
newIndexName, err := reindexedName(indexName)
// handle err
err = es.createIndex(ctx, newIndexName, body)
// handle err
// Some additional operations
// Reindex action.
_, err = es.client.Reindex().
Body(reindexBody).
Do(ctx)
// handle err
// Fetch all the aliases of old index
aliases, err := es.aliasesOf(ctx, indexName)
// handle err
aliases = append(aliases, indexName)
// Delete old index
err = es.deleteIndex(ctx, indexName)
// handle err
// Set aliases of old index to the new index.
err = es.setAlias(ctx, newIndexName, aliases...)
// handle err
return nil
}
For testing the reindex method I have tried mocking and DI but that turns out to be hard since the methods are defined on a struct instead of passing an interface as an argument to them. (So now I want to keep the implementation same since it would require making changes to all the plugin implementations and I want to avoid that)
I wanted to know whether I can use a modified version of my build server funtion (the one I am using is given below) to return responses for different methods for the reindex service which will write the appropriate responses based on the HTTP method and the request path that is used by that method?
type ServerSetup struct {
Method, Path, Body, Response string
HTTPStatus int
}
// This function is a modified version of: https://github.com/github/vulcanizer/blob/master/es_test.go
func buildTestServer(t *testing.T, setup *ServerSetup) *httptest.Server {
handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestBytes, _ := ioutil.ReadAll(r.Body)
requestBody := string(requestBytes)
matched := false
if r.Method == setup.Method && r.URL.EscapedPath() == setup.Path && requestBody == setup.Body {
matched = true
if setup.HTTPStatus == 0 {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(setup.HTTPStatus)
}
_, err := w.Write([]byte(setup.Response))
if err != nil {
t.Fatalf("Unable to write test server response: %v", err)
}
}
// TODO: remove before pushing
/*if !reflect.DeepEqual(r.URL.EscapedPath(), setup.Path) {
t.Fatalf("wanted: %s got: %s\n", setup.Path, r.URL.EscapedPath())
}*/
if !matched {
t.Fatalf("No requests matched setup. Got method %s, Path %s, body %s\n", r.Method, r.URL.EscapedPath(), requestBody)
}
})
return httptest.NewServer(handlerFunc)
}
Something like this function but it takes a map of request methods and past mapped to appropriate responses and writes them to the writer?

How do I add data to an interface that is taken as an argument in Go?

I'm just picking up go, so apologies if my terminology isn't precise. My end goal is to add the name of a cachebusted CSS file to the layout template of my Go application. The CSS file built on the fly when the application starts up so can't be hardcoded. In my template file I have this:
//More html here
<link href="{{.CSSFile}}" rel="stylesheet">
//more html here
I have a Render method on a View type like shown below. It takes data interface{} as an argument and then runs ExecuteTemplate. It is called by every controller in one way or another that sends the data argument and exposes information. I know how to add it as data from the controller that then calls the Render method, but I obviously don't want to add the CSS file in every single controller action, so it makes the most sense to add it in the Render function one time and have it added to the data that gets passed to ExecuteTemplate. My issue is how do I add this information to the data already being passed to Render and then pass that whole of information to ExecuteTemplate. What I have below works for the CSS file, but it obviously doesn't send along the data that was passed to the original Render argument.
type View struct {
Template *template.Template
Layout string
}
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Data struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
}
func (d *Data) AddCSSFile() {
ss, _ := filepath.Glob("./assets/site-*.css")
fp := strings.Join(ss, "")
_, d.CSSFile = filepath.Split(fp)
}
I've created a gist which, not entirely complete, is a little more fleshed out of what I'm trying to do:
https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580
Not sure I understand exactly what you're asking but if what you want is to combine the data interface{} argument with the d := Data{} value inside Render, then you could do something like this...
// ...
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
p := Page{Data:data}
p.AddCSSFile()
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, p)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}
type Page struct {
Alerts []Alert
Yield interface{}
CSSFile interface{}
Data interface{}
}
func (p *Page) AddCSSFile() {
// ...
}
Edit: Or you could also just initialize an anonymous struct value and pass it to ExecuteTemplate without having to change you existing Data type.
// ...
err := v.Template.ExecuteTemplate(w, v.Layout, struct{
Data
Args interface{}
}{Data:d, Args:data})
// ...
Edit2: So if I understand your comment correctly the data interface{} argument passed to the Render method could in some or all instances be or contain a value of a type that matches one of the Data fields' types, in which case you would like to set that value to that field so as to pass it together to the ExecuteTemplate method. At least one solution to that, as you've already found out, is to use type assertion. Below is a slightly modified version of your example from the comment in the context of your original example from the question.
func (v *View) Render(w http.ResponseWriter, data interface{}) error {
d := Data{}
d.AddCSSFile()
if alerts, ok := data.([]Alert); ok {
d.Alerts = alerts
}
w.Header().Set("Content-Type", "text/html")
err := v.Template.ExecuteTemplate(w, v.Layout, d)
if err != nil {
log.Println(err)
fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
}
return nil
}

Reflection error on GoLang - Too few arguments

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...
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.
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.

Resources