パラメータを URL ルート ハンドラ関数に動的に渡そうとしています。リフレクション パッケージを使用して、値のマップを URL から、たまたま匿名構造体である 1 つのパラメーターを持つ関数に変換できると考えました。ハンドラー関数に渡す構造体を作成するところまで行きましたが、構造体へのポインターになってしまいます。ハンドラー関数のシグネチャをポインターを期待するように変更すると、作成された構造体がポインターへのポインターになってしまうと思います。とにかく、これがコードです(パニックが続きます):
リンク: http://play.golang.org/p/vt_wNY1f08
package main
import (
"errors"
"fmt"
"net/http"
"reflect"
"strconv"
"github.com/gorilla/mux"
)
func mapToStruct(obj interface{}, mapping map[string]string) error {
dataStruct := reflect.Indirect(reflect.ValueOf(obj))
if dataStruct.Kind() != reflect.Struct {
return errors.New("expected a pointer to a struct")
}
for key, data := range mapping {
structField := dataStruct.FieldByName(key)
if !structField.CanSet() {
fmt.Println("Can't set")
continue
}
var v interface{}
switch structField.Type().Kind() {
case reflect.Slice:
v = data
case reflect.String:
v = string(data)
case reflect.Bool:
v = string(data) == "1"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
x, err := strconv.Atoi(string(data))
if err != nil {
return errors.New("arg " + key + " as int: " + err.Error())
}
v = x
case reflect.Int64:
x, err := strconv.ParseInt(string(data), 10, 64)
if err != nil {
return errors.New("arg " + key + " as int: " + err.Error())
}
v = x
case reflect.Float32, reflect.Float64:
x, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return errors.New("arg " + key + " as float64: " + err.Error())
}
v = x
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
x, err := strconv.ParseUint(string(data), 10, 64)
if err != nil {
return errors.New("arg " + key + " as int: " + err.Error())
}
v = x
default:
return errors.New("unsupported type in Scan: " + reflect.TypeOf(v).String())
}
structField.Set(reflect.ValueOf(v))
}
return nil
}
type RouteHandler struct {
Handler interface{}
}
func (h RouteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := reflect.TypeOf(h.Handler)
handlerArgs := reflect.New(t.In(0)).Interface()
if err := mapToStruct(handlerArgs, mux.Vars(req)); err != nil {
panic(fmt.Sprintf("Error converting params"))
}
f := reflect.ValueOf(h.Handler)
args := []reflect.Value{reflect.ValueOf(handlerArgs)}
f.Call(args)
fmt.Fprint(w, "Hello World")
}
type App struct {
Router mux.Router
}
func (app *App) Run(bind string, port int) {
bind_to := fmt.Sprintf("%s:%d", bind, port)
http.Handle("/", &app.Router)
http.ListenAndServe(bind_to, &app.Router)
}
func (app *App) Route(pat string, h interface{}) {
app.Router.Handle(pat, RouteHandler{Handler:h})
}
func home(args struct{Category string}) {
fmt.Println("home", args.Category)
}
func main() {
app := &App{}
app.Route("/products/{Category}", home)
app.Run("0.0.0.0", 8080)
}
パニック:
2013/03/28 18:48:43 http: panic serving 127.0.0.1:51204: reflect: Call using *struct { Category string } as type struct { Category string }
/usr/local/Cellar/go/1.0.3/src/pkg/net/http/server.go:589 (0x3fb66)
_func_004: buf.Write(debug.Stack())
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:1443 (0x11cdb)
panic: reflect·call(d->fn, d->args, d->siz);
/usr/local/Cellar/go/1.0.3/src/pkg/reflect/value.go:428 (0x484ba)
Value.call: panic("reflect: " + method + " using " + xt.String() + " as type " + targ.String())
/usr/local/Cellar/go/1.0.3/src/pkg/reflect/value.go:334 (0x47c3a)
Value.Call: return v.call("Call", in)
/Users/matt/Workspaces/Go/src/pants/pants.go:86 (0x2f36)
RouteHandler.ServeHTTP: f.Call(args)
/Users/matt/Workspaces/Go/src/pants/pants.go:1 (0x347c)
(*RouteHandler).ServeHTTP: package main
/Users/matt/Workspaces/Go/src/github.com/gorilla/mux/mux.go:86 (0x5a699)
com/gorilla/mux.(*Router).ServeHTTP: handler.ServeHTTP(w, req)
/usr/local/Cellar/go/1.0.3/src/pkg/net/http/server.go:669 (0x337b6)
(*conn).serve: handler.ServeHTTP(w, w.req)
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:271 (0xfde1)
goexit: runtime·goexit(void)