猿问

Golang Json 解码验证不会引发接口错误

我完全是 Golang 的菜鸟,非常感谢以下方面的任何帮助


我有这段代码片段,运行良好


var settings CloudSettings


type CloudSettings struct {

...

    A1 *bool `json:"cloud.feature1,omitempty"`

...

}


err = json.NewDecoder(request.Body).Decode(&settings)


An attempt to send an invalid string would raise this error:


curl ... -d '{"cloud.feature1" : "Junk"}'


"message":"Error:strconv.ParseBool: parsing \"Junk\": invalid syntax Decoding request body."

现在,我们有一个单独的LocalSettings结构,同样的功能需要有条件地处理云/本地设置解码


于是,代码改为:


var settings interface{} = CloudSettings{}


// If the request header says local settings

settings = LocalSettings{}


/* After this change Decode() no longer raises any error for invalid strings and accepts anything */

err = json.NewDecoder(request.Body).Decode(&settings)

所以问题是为什么我会看到这种行为,我将如何解决这个问题?


如果我有 2 个单独的settings变量,那么从那一点开始的整个代码将只是重复,我想避免


拉风的咖菲猫
浏览 109回答 2
2回答

SMILET

在第二个片段中,您有一个初始化为结构的接口,但传递了该接口的地址。该接口包含一个不能被覆盖的LocalSettingsorCloudSetting值,因此解码器创建一个map[string]interface{},将传递的接口的值设置为指向该值,然后解组数据。当您运行第二个代码段时,您不会初始化本地设置或云设置。改变:settings=&CloudSettings{}或者settings=&LocalSettings{}和err = json.NewDecoder(request.Body).Decode(settings)它应该表现得像预期的那样

12345678_0001

根据您的问题,我假设所有字段(即使是具有相同名称的字段)在 JSON 标记中都有一个cloud.或前缀。local.如果是这种情况,您可以简单地将两个选项嵌入到一个类型中:type Wrapper struct {    *CloudSettings    *LocalSettings}然后解组到这个包装器类型中。JSON 标签将确保填充正确设置类型的正确字段:wrapper := &Wrapper{}if err := json.NewDecoder(request.Body).Decode(&wrapper); err != nil {    // handle}// now to work out which settings were passed:if wrapper.CloudSettings == nil {    fmt.Println("Local settings provided!")    // use wrapper.CloudSettings} else {    fmt.Println("Cloud settings provided!")    // use wrapper.LocalSettings}您提到我们希望看到基于标头值加载的本地设置。您可以简单地解组有效负载,然后检查标头是否与加载的设置类型匹配。如果标头指定本地设置,但有效负载包含云设置,则只需返回错误响应。不过,我在这里假设您的 JSON 标签对于两种设置类型都是不同的。这并不总是适用,所以如果我的假设不正确,并且某些字段共享相同的 JSON 标签,那么自定义 Unmarshal 函数将是可行的方法:func (w *Wrapper) UnmarshalJSON(data []byte) error {    // say we want to prioritise Local over cloud:    l := LocalSettings{}    if err := json.Unmarshal(data, &l); err == nil {        // we could unmarshal into local without a hitch?        w.CloudSettings = nil // ensure this is blanked out        w.LocalSettings = &l // set local        return nil    }    // we should use cloud settings    c := CloudSettings{}    if err := json.Unmarshal(data, &c); err != nil {        return err    }    w.LocalSettings = nil    w.CloudSettings = &c    return nil}这样,任何冲突都会得到处理,我们可以控制哪些设置优先。同样,无论 JSON 解组的结果如何,您都可以简单地交叉检查标头值 + 填充了哪种设置类型,然后从那里获取它。最后,如果两种设置类型之间存在相当大的重叠,您也可以将有效负载解组为两种类型,并在包装类型中填充两个字段:func (w *Wrapper) UnmarshalJSON(data []byte) error {    *w = Wrapper{} // make sure we start with a clean slate    l := LocalSettings{}    var localFail err    if err := json.Unmarshal(data, &l); err == nil {        w.LocalSettings = &l // set local    } else {        localFail = err    }    c := CloudSettings{}    if err := json.Unmarshal(data, &c); err == nil {        w.CloudSettings = &c    } else if localFail != nil { // both unmarshal calls failed        return err // maybe wrap/return custom error    }    return nil // one or more unmarshals were successful}这应该够了吧
随时随地看视频慕课网APP

相关分类

Go
我要回答