2017-04-08 88 views
1
package controllers 

import (
    "encoding/json" 
    "errors" 
    "io" 
    "io/ioutil" 
    "reflect" 
) 

func GetTypeFromReq(c *App, ty interface{}) (interface{}, error) { 
    //get the type we are going to marshall into 
    item := reflect.ValueOf(ty) 

    //define and set the error that we will be returning to null 
    var retErr error 
    retErr = nil 

    //extract the body from the request and defer closing of the body 
    body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576)) 
    defer c.Request.Body.Close() 

    //handle errors and unmarshal our data 
    if err != nil { 
     retErr = errors.New("Failed to Read body: " + err.Error()) 
    } else if err = json.Unmarshal(body, &item); err != nil { 
     retErr = errors.New("Unmarshal Failed: " + err.Error()) 
    } 

    return item, retErr 
} 

我想一個類型和請求傳遞到一個函數,那麼該函數內部和解組請求到一個變量,並將其返回。如何一般馬歇爾請求到JSON在golang

我認爲我的做法是錯誤的,因爲當我嘗試這樣做:

inter, err := GetTypeFromReq(&c, models.User{}) 
if err != nil { 
    revel.ERROR.Println(err.Error()) 
} 
user := inter.(models.User) 

我得到的錯誤「接口轉換:接口{}是reflect.Value,不models.User」

關於如何解決這個問題的任何提示?最後一行的

回答

1

下面是如何修改的功能,使其按預期工作:

func GetTypeFromReq(c *App, ty interface{}) (interface{}, error) { 
    // Allocate new value with same type as ty 
    v := reflect.New(reflect.TypeOf(ty)) 

    //define and set the error that we will be returning to null 
    var retErr error 
    retErr = nil 

    //extract the body from the request and defer closing of the body 
    body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576)) 
    defer c.Request.Body.Close() 

    //handle errors and unmarshal our data 
    if err != nil { 
    retErr = errors.New("Failed to Read body: " + err.Error()) 
    } else if err = json.Unmarshal(body, v.Interface()); err != nil { 
    retErr = errors.New("Unmarshal Failed: " + err.Error()) 
    } 

    // v holds a pointer, call Elem() to get the value. 
    return v.Elem().Interface(), retErr 
} 

注到Interface()的呼籲得到reflect.Value的電流值。

下面是避免反射並鍵入斷言的方法:

func GetFromReq(c *App, item interface{}) error { 
    //extract the body from the request and defer closing of the body 
    body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576)) 
    defer c.Request.Body.Close() 

    //handle errors and unmarshal our data 
    if err != nil { 
    retErr = errors.New("Failed to Read body: " + err.Error()) 
    } else if err = json.Unmarshal(body, item); err != nil { 
    retErr = errors.New("Unmarshal Failed: " + err.Error()) 
    } 
    return retErr 
} 

使用方法如下:

var user models.User 
err := GetFromReq(&c, &user) 
if err != nil { 
    revel.ERROR.Println(err.Error()) 
} 

使用JSON decoder以簡化代碼:

func GetFromReq(c *App, item interface{}) error { 
    defer c.Request.Body.Close() 
    return json.NewDecoder(io.LimitReader(c.Request.Body, 1048576)).Deocode(item) 
} 

如果c.Request*http.Requestc.Responsehttp.ResponseWriter,然後寫入功能:

func GetFromReq(c *App, item interface{}) error { 
    return json.NewDecoder(http.MaxBytesReaer(c.Response, c.Request.Body, 1048576)).Deocode(item) 
} 

沒有必要關閉請求主體在網/ HTTP服務器。使用MaxBytesReader而不是io.LimitReader來防止客戶端意外或惡意發送大量請求並浪費服務器資源。

+0

真的很棒的解釋,非常感謝你 –

+1

完成,woops。我以爲我做到了。 –

0

修改代碼:變化user := inter.(models.User)user := inter.Interface().(models.User),試試看!

0

「接口轉換:接口{}是reflect.Value,不models.User」

相當平直向前有關消息錯誤。你的itemreflect.Value它不是models.User

所以我覺得在你的代碼,你可以改變itemto models.User

但我認爲你是綁來創建將與所有類型的模型的工作,在這種情況下models.User{}功能。因爲它使用interface

你的方法是昂貴的。你可以在incoming request直接轉換是這樣的:

func GetTypeFromReq(c *App, ty models.User) (models.User, error) { 
    //get the type we are going to marshall into 
    var item models.User 

    //define and set the error that we will be returning to nil 
    var retErr error // this var if the value not define then it is nil. Because error is interface 

    //extract the body from the request and defer closing of the body 
    body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576)) 
    defer c.Request.Body.Close() 

    //handle errors and unmarshal our data 
    if err != nil { 
     retErr = errors.New("Failed to Read body: " + err.Error()) 
    } else if err = json.Unmarshal(body, &item); err != nil { 
     retErr = errors.New("Unmarshal Failed: " + err.Error()) 
    } 

    return item, retErr 
} 

如果您body具有相同的結構模型,它會給你的價值,如果不是那麼它是error

請注意,您需要使用interface時要小心。你可以在這個article看到一些指導。使用接口:

  • 當API的用戶需要提供實現細節時。
  • 當API有多個實現時,他們需要在內部進行維護。
  • 時可以改變API的部分已經確定,並要求脫鉤。

你的功能你models.User的值轉換爲interface,然後返回interface值。這就是爲什麼它很昂貴。