2017-04-08 6 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 
} 

タイプとリクエストを関数に渡し、その関数内でリクエストを変数にアンマーシャリングして返します。golangのJSONに一般的にリクエストをマーシャリングする方法

私は私がこれを行うにしようとするので、私のアプローチは間違っていると仮定します。

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.Requestとですはhttp.ResponseWriterある、のように関数を記述します。net/HTTPサーバーにリクエストボディを閉鎖する必要はありません

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

。 io.LimitReaderの代わりにMaxBytesReaderを使用すると、クライアントが誤ってまたは悪意を持って大量の要求を送信したり、サーバーリソースを浪費することを防ぐことができます。

+0

本当にありがとう、ありがとうございます。 –

+1

完了しました。私は思った。 –

0

最後の行のコードを変更してください:からuser := inter.Interface().(models.User)に変更してみてください!

0

:かなりストレートフォワードメッセージエラーに関する

"インターフェース変換インタフェースは、{} reflect.Value、ないmodels.Userです"。 itemreflect.Valueであることはmodels.Userではありません。

ですので、あなたのコードではitemto models.Userを変更することができます。

しかし、あなたのモデルのすべてのタイプ(この場合はmodels.User{})で機能する関数を作成するとします。

interfaceを使用しているため、あなたのアプローチは高価です。あなたのbodyがあなたのモデルと同じ構造を持っているならば、それはerrorでない場合、それは、あなたの価値を与える

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 
} 

:あなたはこのような直接incoming requestを変換することができます。

interfaceを使用する場合は注意が必要です。このarticleにいくつかのガイドラインがあります。

  • APIのユーザーが実装の詳細を提供する必要がある場合。
  • APIに複数の実装がある場合は、内部的にメンテナンスする必要があります。
  • 変更可能なAPIの部分が識別され、デカップリングが必要な場合。

あなたの関数はinterfaceにごmodels.Userの値を変換し、その後interface値を返します。それが高価な理由です。

関連する問題