如何传递一个动态创建的结构作为非指针对象在Go



我试图动态地将参数传递给URL路由处理程序函数。我认为可以使用反射包将值的映射从URL转换为具有一个参数的函数,该参数恰好是匿名结构体。我已经得到了远创建结构传递给处理程序函数,但它最终是一个指针的结构。如果我改变处理函数的签名,期望一个指针,创建的结构最终是指针指向指针,我认为。无论如何,下面是代码(接下来是恐慌):

链接: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)

调用Elem()。值对象。

引自The Laws of Reflection文章:

为了得到p所指向的内容,我们调用Value的Elem方法,该方法通过指针 进行间接指向。

注意,reflect.New()创建一个指向您传递给它的类型的值的指针。下面这行:

handlerArgs := reflect.New(t.In(0)).Interface()

handlerArgs将是指向t.In(0)类型结构体的指针。您需要对该指针解引用,以获得适合传递给函数的值。

我的建议如下:

  1. 使hanlerArgs a *reflect.Value为实际结构值:

    handlerArgs := reflect.New(t.In(0)).Elem()
    
  2. 使mapToStruct采用这样的*reflect.Value而不是interface{}持有指向结构体的指针(毕竟它是您的反射代码的助手)。

  3. 直接使用handlerArgs作为Call()调用的函数参数之一

相关内容

  • 没有找到相关文章

最新更新