Golang中对象创建失败的原因有哪些

Golang中对象创建失败的原因有哪些 我正在尝试为一个开源产品实现单点登录功能,但一直遇到一个阻碍我的错误。我想知道是否有人能告诉我,这是否是Go语言中需要我注意的某些特性(我对这门语言没有经验)。

image

我通过令牌传递了上述字段,然后对象如下所示。每次测试单点登录功能时,这个对象似乎都是空的。是否每个字段都必须存在,对象才能被正确创建?

type User struct {
	ExternalID     string                 `json:"sub"`
	Name           string                 `json:"name"`
	Email          string                 `json:"email"`
	EmailVerified  bool                   `json:"email_verified"`
	UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

非常欢迎任何建议或解释。


更多关于Golang中对象创建失败的原因有哪些的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

您检查过 UnmarshalJSON() 方法是否返回错误吗?

更多关于Golang中对象创建失败的原因有哪些的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


(我对这门语言没有经验)

正如 @mje 所说,最好展示一下你目前尝试过的内容。

Jake_Murphy:

建议

在不确切知道您具体在做什么的情况下,很难判断。您能发布一个小的示例代码吗?

Jake_Murphy:

解释

Go 的 JSON 解码器会忽略 JSON 对象中那些在您的目标结构体中没有对应字段的字段。如果 JSON 对象中不存在某个字段,它也会让结构体字段保持初始化为零值。根据您发布的示例数据和结构体代码,您结构体的前四个字段应该会被填充。

默认情况下,json.Unmarshal() 会递归地进行反序列化;如果某个字段定义了 UnmarshalJSON() 方法,它会自动调用该方法。

https://pkg.go.dev/encoding/json#Unmarshal https://pkg.go.dev/encoding/json#Unmarshaler

这个示例展示了 MarshalJSON() 的工作原理,但对于 Unmarshal 来说概念是相同的。

在你的情况下,需要 UnmarshalJSON() 方法,因为你需要为 EmailVerified 字段同时支持布尔值和字符串类型。

大家好,感谢如此快速的回复。

我受限于必须使用一个企业级 Kubernetes 环境,该环境有很多限制,我无法在本地复现,因此我没有机会像往常那样逐步调试代码。

我想再问一个 Go 问题,这可能有助于我更好地理解代码为何有效或无效。 Go 是否有内置的机制会自动使用这个 UnmarshalJSON 方法,还是应该在某个地方显式调用它?

image

如果它应该在某个地方被显式调用,我可能需要在 Git 仓库上提交一个错误报告。

我正在使用的开源产品是Chirpstack应用服务器。我正尝试让OIDC与Azure提供商协同工作。

这是oidc.go中的解组方法:

image

OIDC登录的实现

// OpenIDConnectLogin performs an OpenID Connect login.
func (a *InternalAPI) OpenIDConnectLogin(ctx context.Context, req *pb.OpenIDConnectLoginRequest) (*pb.OpenIDConnectLoginResponse, error) {
	oidcUser, err := oidc.GetUser(ctx, req.Code, req.State)
	if err != nil {
		return nil, helpers.ErrToRPCError(err)
	}

	if !oidcUser.EmailVerified {
		return nil, grpc.Errorf(codes.FailedPrecondition, "email address must be verified before you can login")
	}

	var user storage.User

	// try to get the user by external ID.
	user, err = storage.GetUserByExternalID(ctx, storage.DB(), oidcUser.ExternalID)
	if err != nil {
		if err == storage.ErrDoesNotExist {
			// try to get the user by email and set the external id.
			user, err = storage.GetUserByEmail(ctx, storage.DB(), oidcUser.Email)
			if err != nil {
				// we did not find the user by external_id or email and registration is enabled.
				if err == storage.ErrDoesNotExist && registrationEnabled {
					user, err = a.createAndProvisionUser(ctx, oidcUser)
					if err != nil {
						return nil, helpers.ErrToRPCError(err)
					}
					// fetch user again because the provisioning callback url may have updated the user.
					user, err = storage.GetUser(ctx, storage.DB(), user.ID)
					if err != nil {
						return nil, helpers.ErrToRPCError(err)
					}
				} else {
					return nil, helpers.ErrToRPCError(err)
				}
			}
			user.ExternalID = &oidcUser.ExternalID
		} else {
			return nil, helpers.ErrToRPCError(err)
		}
	}

产生错误的getUser方法。

func GetUser(ctx context.Context, db sqlx.Queryer, id int64) (User, error) {
	var user User

	err := sqlx.Get(db, &user, `
		select
			*
		from
			"user"
		where
			id = $1
	`, id)
	if err != nil {
		if err == sql.ErrNoRows {
			return user, ErrDoesNotExist
		}
		return user, errors.Wrap(err, "select error")
	}

	return user, nil
}

从你提供的代码和错误信息来看,这确实是Go语言JSON反序列化的一个常见问题。主要原因是JSON字段类型与Go结构体字段类型不匹配。

在你的情况下,email_verified字段在JSON中是字符串类型("true""false"),但Go结构体中定义为bool类型。Go的JSON包无法自动将字符串转换为布尔值。

以下是修复方案:

type User struct {
    ExternalID     string                 `json:"sub"`
    Name           string                 `json:"name"`
    Email          string                 `json:"email"`
    EmailVerified  bool                   `json:"email_verified,string"`  // 添加,string标签
    UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

或者,如果JSON中的email_verified字段可能缺失或为null,可以使用指针类型:

type User struct {
    ExternalID     string                 `json:"sub"`
    Name           string                 `json:"name"`
    Email          string                 `json:"email"`
    EmailVerified  *bool                  `json:"email_verified,omitempty"`
    UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

如果JSON中的布尔值确实是字符串格式,但你不能修改结构体标签,可以实现自定义的JSON反序列化:

type User struct {
    ExternalID     string                 `json:"sub"`
    Name           string                 `json:"name"`
    Email          string                 `json:"email"`
    EmailVerified  bool                   `json:"email_verified"`
    UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

func (u *User) UnmarshalJSON(data []byte) error {
    type Alias User
    aux := &struct {
        EmailVerified string `json:"email_verified"`
        *Alias
    }{
        Alias: (*Alias)(u),
    }
    
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    
    // 手动处理字符串到布尔值的转换
    if aux.EmailVerified == "true" {
        u.EmailVerified = true
    } else {
        u.EmailVerified = false
    }
    
    return nil
}

关于字段是否必须存在的问题:默认情况下,JSON反序列化时缺失的字段会被设置为零值(空字符串、false、0等)。如果你需要验证必需字段,可以在反序列化后手动检查:

var user User
if err := json.Unmarshal(data, &user); err != nil {
    // 处理错误
}

// 检查必需字段
if user.ExternalID == "" || user.Email == "" {
    return errors.New("required fields missing")
}

或者使用required标签配合验证库:

import "github.com/go-playground/validator/v10"

type User struct {
    ExternalID     string                 `json:"sub" validate:"required"`
    Name           string                 `json:"name"`
    Email          string                 `json:"email" validate:"required,email"`
    EmailVerified  bool                   `json:"email_verified,string"`
    UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

func main() {
    validate := validator.New()
    var user User
    
    if err := json.Unmarshal(data, &user); err != nil {
        // 处理错误
    }
    
    if err := validate.Struct(user); err != nil {
        // 验证失败
    }
}

错误信息中的json: cannot unmarshal string into Go struct field User.email_verified of type bool明确指出了问题所在:JSON中的字符串无法反序列化为Go的布尔类型。

回到顶部