如何告诉客户端他们需要从 Go 服务器发送一个整数而不是一个字符串?

假设我在服务器上有以下 Go 结构


type account struct {

    Name    string

    Balance int

}

我想对传入的请求调用 json.Decode 以将其解析为帐户。


    var ac account

    err := json.NewDecoder(r.Body).Decode(&ac)

如果客户端发送以下请求:


{

    "name": "test@example.com", 

    "balance": "3"

}

Decode() 将返回以下错误:


json: cannot unmarshal string into Go value of type int

现在可以将其解析为“您为 Balance 发送了一个字符串,但您确实应该发送一个整数”,但这很棘手,因为您不知道字段名称。如果请求中有很多字段,它也会变得更加棘手 - 您不知道哪个解析失败。


对于请求中任意数量的整数字段,在 Go 中处理传入请求并返回错误消息“余额必须是字符串”的最佳方法是什么?


皈依舞
浏览 174回答 3
3回答

慕工程0101907

您可以为“余额”字段使用带有自定义解组算法的自定义类型。现在有两种可能:处理两种类型:package mainimport (    "encoding/json"    "fmt"    "strconv")type Int inttype account struct {    Name    string    Balance Int}func (i *Int) UnmarshalJSON(b []byte) (err error) {    var s string    err = json.Unmarshal(b, &s)    if err == nil {        var n int        n, err = strconv.Atoi(s)        if err != nil {            return        }        *i = Int(n)        return    }    var n int    err = json.Unmarshal(b, &n)    if err == nil {        *i = Int(n)    }    return}func main() {    for _, in := range [...]string{        `{"Name": "foo", "Balance": 42}`,        `{"Name": "foo", "Balance": "111"}`,    } {        var a account        err := json.Unmarshal([]byte(in), &a)        if err != nil {            fmt.Printf("Error decoding JSON: %v\n", err)        } else {            fmt.Printf("Decoded OK: %v\n", a)        }    }}游乐场链接。只处理一个数字类型,并通过一个合理的错误使其他任何事情失败:package mainimport (    "encoding/json"    "fmt")type Int inttype account struct {    Name    string    Balance Int}type FormatError struct {    Want   string    Got    string    Offset int64}func (fe *FormatError) Error() string {    return fmt.Sprintf("Invalid data format at %d: want: %s, got: %s",        fe.Offset, fe.Want, fe.Got)}func (i *Int) UnmarshalJSON(b []byte) (err error) {    var n int    err = json.Unmarshal(b, &n)    if err == nil {        *i = Int(n)        return    }    if ute, ok := err.(*json.UnmarshalTypeError); ok {        err = &FormatError{            Want:   "number",            Got:    ute.Value,            Offset: ute.Offset,        }    }    return}func main() {    for _, in := range [...]string{        `{"Name": "foo", "Balance": 42}`,        `{"Name": "foo", "Balance": "111"}`,    } {        var a account        err := json.Unmarshal([]byte(in), &a)        if err != nil {            fmt.Printf("Error decoding JSON: %#v\n", err)            fmt.Printf("Error decoding JSON: %v\n", err)        } else {            fmt.Printf("Decoded OK: %v\n", a)        }    }}游乐场链接。还有第三种可能性:为整个account类型编写自定义解组器,但它需要更多涉及的代码,因为您需要使用encoding/json.Decoder类型的方法实际迭代输入 JSON 数据 。看完你的对于请求中任意数量的整数字段,在 Go 中处理传入请求并返回错误消息“余额必须是字符串”的最佳方法是什么?更仔细,我承认有整个类型的定制解析器唯一明智的可能性,除非你是一个OK第三方软件包实现解析器通过JSON模式支持验证(我想我会看看这个第一次作为juju是一个相当已建立的产品)。

慕姐4208626

对此的解决方案可能是使用类型断言,通过使用映射将 JSON 数据解组为:type account struct {    Name    string    Balance int}var str = `{    "name": "test@example.com",     "balance": "3"}`func main() {    var testing = map[string]interface{}{}    err := json.Unmarshal([]byte(str), &testing)    if err != nil {        fmt.Println(err)    }    val, ok := testing["balance"]    if !ok {        fmt.Println("missing field balance")        return    }    nv, ok := val.(float64)    if !ok {        fmt.Println("balance should be a number")        return    }    fmt.Printf("%+v\n", nv)}见http://play.golang.org/p/iV7Qa1RrQZ使用这里的类型断言是float64因为它是 Go 的 JSON 解码器支持的默认数字类型。应该注意的是,这种使用interface{}可能不值得麻烦。该UnmarshalTypeError(https://golang.org/pkg/encoding/json/#UnmarshalFieldError)包含一个Offset领域,可能允许检索触发错误的JSON数据的内容。例如,您可以返回以下类型的消息:cannot unmarshal string into Go value of type int near `"balance": "3"`

眼眸繁星

似乎这里提供了一个实现来解决这个问题,仅在 Go 中。type account struct {    Name    string    Balance int `json:",string"`}在我看来,更正确和可持续的方法是让你用 JavaScript 之类的东西创建一个客户端库,然后将它发布到 NPM 注册表中供其他人使用(私有存储库的工作方式相同)。通过提供这个库,你可以以一种有意义的方式为消费者定制 API,并防止错误蔓延到你的主程序中。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go