为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
拉风的咖菲猫 2023-02-14 15:27:50
我完全是 Golang 的菜鸟,非常感谢以下方面的任何帮助我有这段代码片段,运行良好var settings CloudSettingstype 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 settingssettings = 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变量,那么从那一点开始的整个代码将只是重复,我想避免
查看完整描述

2 回答

?
SMILET

TA贡献1796条经验 获得超4个赞

在第二个片段中,您有一个初始化为结构的接口,但传递了该接口的地址。该接口包含一个不能被覆盖的LocalSettingsorCloudSetting值,因此解码器创建一个map[string]interface{},将传递的接口的值设置为指向该值,然后解组数据。当您运行第二个代码段时,您不会初始化本地设置或云设置。

改变:

settings=&CloudSettings{}

或者

settings=&LocalSettings{}

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

它应该表现得像预期的那样


查看完整回答
反对 回复 2023-02-14
?
12345678_0001

TA贡献1802条经验 获得超5个赞

根据您的问题,我假设所有字段(即使是具有相同名称的字段)在 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

}

这应该够了吧


查看完整回答
反对 回复 2023-02-14
  • 2 回答
  • 0 关注
  • 79 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信