使用zmb3/spotify获取用户播放曲目时遇错误46的问题排查
问题:Spotify OAuth后续请求报错(User not registered in the Developer Dashboard)
问题描述
首次获取用户当前播放曲目时正常,后续请求返回错误:
Error getting current playing song: spotify: couldn't decode error: (46) [User not registered in the Developer Dashboard
需要解决的疑问:
- 该错误的原因是什么?
- 是否无法复用其他用户的令牌?
- 有没有无需让用户重复登录即可持续获取其播放曲目的方案?
相关代码
package src import ( ctx "context" "encoding/json" "fmt" "log" "net/http" "strings" "github.com/zmb3/spotify/v2" spotifyauth "github.com/zmb3/spotify/v2/auth" "golang.org/x/oauth2" ) func CompleteAuth(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("state") auth := spotifyauth.New(spotifyauth.WithRedirectURL(Env.RedirectUrl), spotifyauth.WithScopes(spotifyauth.ScopeUserReadCurrentlyPlaying), spotifyauth.WithClientID(Env.ClientID), spotifyauth.WithClientSecret(Env.ClientSecret)) tok, err := auth.Token(ctx.Background(), userID, r) if err != nil { http.Error(w, "Couldn't get token", http.StatusForbidden) log.Fatal(err) } client := spotify.New(auth.Client(ctx.Background(), tok)) info, err := client.CurrentUser(r.Context()) if err != nil { http.Error(w, "Couldn't get user", http.StatusForbidden) log.Fatal(err) } db.Set(userID, tok.AccessToken, 0) w.Write([]byte(fmt.Sprintf("You are logged in as: %s\n", info.DisplayName))) } func IndexPage(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("Hello!. Use /auth to login with spotify and /current to get your current playing song")) } func CurrentPage(w http.ResponseWriter, r *http.Request) { userid := r.URL.Query().Get("user_id") if userid == "" { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("You need to provide a user_id")) return } token, _ := db.Get(userid).Result() auth := spotifyauth.New(spotifyauth.WithRedirectURL(Env.RedirectUrl), spotifyauth.WithScopes(spotifyauth.ScopeUserReadCurrentlyPlaying), spotifyauth.WithClientID(Env.ClientID)) fmt.Println(token) client := spotify.New(auth.Client(ctx.Background(), &oauth2.Token{AccessToken: token})) go RefreshToken(userid, client) user, err := client.PlayerCurrentlyPlaying(r.Context()) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Error getting current playing song: %s", err.Error()))) return } response := PlayingInfo{ Playing: user.Playing, Info: nil, } if user.Playing { artists := make([]string, len(user.Item.Artists)) for i, artist := range user.Item.Artists { artists[i] = artist.Name } response.Info = &PlayingResponse{ SongName: user.Item.Name, Artists: strings.Join(artists, ", "), Link: user.Item.ExternalURLs["spotify"], Duration: user.Progress, TotalDuration: user.Item.Duration, Thumb: user.Item.Album.Images[0].URL, } } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(response); err != nil { log.Println("Error encoding JSON:", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } func AuthSpotify(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("user_id") if userID == "" { http.Error(w, "Invalid Params use ?user_id=<user_id>", http.StatusBadRequest) return } auth := spotifyauth.New(spotifyauth.WithRedirectURL(Env.RedirectUrl), spotifyauth.WithScopes(spotifyauth.ScopeUserReadCurrentlyPlaying), spotifyauth.WithClientID(Env.ClientID)) url := auth.AuthURL(userID, oauth2.AccessTypeOffline) w.WriteHeader(http.StatusOK) w.Write([]byte(url)) } func RefreshToken(userId string, client *spotify.Client) string { token, _ := db.Get(userId).Result() if token == "" { log.Println("User not logged in") return "" } auth := spotifyauth.New(spotifyauth.WithRedirectURL(Env.RedirectUrl), spotifyauth.WithScopes(spotifyauth.ScopeUserReadCurrentlyPlaying), spotifyauth.WithClientID(Env.ClientID), spotifyauth.WithClientSecret(Env.ClientSecret)) c, err := spotify.New(auth.Client(ctx.Background(), &oauth2.Token{AccessToken: token})).Token() if err != nil { log.Println("Error refreshing token:", err) return "" } db.Set(userId, c.AccessToken, 0) return c.AccessToken }
解答
错误原因
- 令牌存储不完整:你现在只存储了
AccessToken,但Spotify OAuth的令牌体系需要RefreshToken才能实现自动刷新。首次请求时AccessToken未过期,能正常调用接口;过期后没有有效RefreshToken去获取新的AccessToken,使用失效的令牌请求就会触发这个错误。 - 刷新逻辑无效:
RefreshToken函数里只传入了AccessToken,没有RefreshToken,根本无法完成令牌刷新。而且你在CompleteAuth中拿到的完整tok对象包含RefreshToken,但没有存储,后续彻底失去了刷新凭证。
关于令牌复用
不存在“无法复用其他用户令牌”的问题,每个用户的令牌是独立且可复用的——只要你正确维护令牌的生命周期(存储完整令牌、及时刷新),就能持续使用该用户的令牌调用接口。
持续获取播放曲目的方案
1. 完整存储令牌对象
修改CompleteAuth函数,将整个oauth2.Token序列化为JSON后存储,而不是只存AccessToken:
// 替换原来的db.Set(userID, tok.AccessToken, 0) tokenJSON, err := json.Marshal(tok) if err != nil { http.Error(w, "Failed to serialize token", http.StatusInternalServerError) return } db.Set(userID, string(tokenJSON), 0)
2. 修复令牌读取与客户端创建逻辑
修改CurrentPage函数,从数据库取出完整令牌并反序列化,再创建客户端:
func CurrentPage(w http.ResponseWriter, r *http.Request) { userid := r.URL.Query().Get("user_id") if userid == "" { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("You need to provide a user_id")) return } tokenJSON, err := db.Get(userid).Result() if err != nil || tokenJSON == "" { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("User not logged in")) return } var tok oauth2.Token if err := json.Unmarshal([]byte(tokenJSON), &tok); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Failed to parse token")) return } // 可以把auth实例全局初始化,不用每次创建 auth := spotifyauth.New( spotifyauth.WithRedirectURL(Env.RedirectUrl), spotifyauth.WithScopes(spotifyauth.ScopeUserReadCurrentlyPlaying), spotifyauth.WithClientID(Env.ClientID), spotifyauth.WithClientSecret(Env.ClientSecret), ) client := spotify.New(auth.Client(ctx.Background(), &tok)) // 刷新令牌并更新存储 newTok, err := client.Token() if err == nil { newTokJSON, _ := json.Marshal(newTok) db.Set(userid, string(newTokJSON), 0) } user, err := client.PlayerCurrentlyPlaying(r.Context()) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Error getting current playing song: %s", err.Error()))) return } // 后续响应逻辑不变... }
3. 简化刷新逻辑
zmb3/spotify库的客户端会自动处理令牌刷新,只要你提供了完整的令牌(包含RefreshToken)和正确的auth配置(带ClientSecret),调用client.Token()就能自动刷新并返回新令牌,不需要自己重新创建客户端。
内容的提问来源于stack exchange,提问作者Mysterio007
相关产品推荐
相关产品推荐

