如何在golang中将请求一般地编组到JSON中

时间:2017-04-08 06:33:53

标签: json http generics go types

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
}

我正在尝试将一个类型和一个请求传递给一个函数,然后在该函数内部将请求转换为变量并返回它。

我认为我的方法是错误的,因为当我尝试这样做时:

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

我收到错误"界面转换:interface {}是reflect.Value,而不是models.User"

有关如何处理此事的任何提示?

3 个答案:

答案 0 :(得分:2)

以下是如何修改该功能以使其按预期工作:

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

无需关闭net / http服务器中的请求正文。使用MaxBytesReader代替io.LimitReader可防止客户端意外或恶意发送大量请求并浪费服务器资源。

答案 1 :(得分:0)

修改最后一行的代码:将user := inter.(models.User)更改为user := inter.Interface().(models.User),试一试!

答案 2 :(得分:0)

  

“界面转换:界面{}是reflect.Value,而不是models.User”

非常直接的消息错误。您的item reflect.Value不是models.User

因此我认为您可以在代码中更改item to 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值。这就是为什么它很贵。