doudunyi3796 2013-03-28 22:55
浏览 65
已采纳

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

I'm trying to dynamically pass parameters to a URL route handler function. I thought it would be possible to use the reflection package to convert a map of values from the URL to the function that has one parameter that happens to be an anonymous struct. I've gotten as far as creating the struct to pass to the handler function but it ends up being a pointer to the struct. If I change the handler function's signature to expect a pointer the created struct ends up being a pointer to a pointer, I think. At any rate, here's the code (the panic follows):

Link: 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)
}

Panic:

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)
  • 写回答

2条回答 默认 最新

  • dongsan6889 2013-03-28 23:14
    关注

    Call Elem() on your reflect.Value object.

    Quoting from The Laws of Reflection article:

    To get to what p points to, we call the Elem method of Value, which indirects through the pointer

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作