Spotify API访问令牌过期:如何重置以维持Flask应用运行
我在PythonAnywhere.com上部署了一个Python/Flask业余Web应用,用来实现Spotify原生不支持的功能。手动通过PythonAnywhere的“Web”标签页重启应用后,应用运行正常,但一段时间后,前端页面发起API调用时会返回“Internal Server Error”。
排查发现,约1小时后调用Spotify API会返回401错误,原因是Spotify访问令牌仅有效期1小时。我曾推测PythonAnywhere仅在重启应用时执行一次令牌刷新逻辑,因此令牌60分钟后过期。于是我尝试将获取访问令牌的逻辑封装为函数,在各API调用函数中调用,但未解决问题。我目前的猜想是Web服务器仅在重启时获取一次令牌,但不确定如何排查。
请问该猜想是否合理?或有其他排查方向,无需手动重启服务器即可维持应用正常运行?
相关代码
常量设置
AUTH_URL = 'https://accounts.spotify.com/api/token' BASE_URL = 'https://api.spotify.com/v1/'
获取访问令牌并返回请求头的代码
auth_response = requests.post(AUTH_URL, { 'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, }) def setHeaders(): try: auth_response.raise_for_status() except requests.exceptions.HTTPError as e: print("!!!Error setting auth_response. System detail:") print(e) raise # 转换为JSON格式 auth_response_data = auth_response.json() # 保存访问令牌 access_token = auth_response_data['access_token'] headers = { 'Authorization': 'Bearer {token}'.format(token=access_token) } return headers
调用Spotify API获取用户所有播放列表的函数
# API GET地址: https://api.spotify.com/v1/users/{user_id}/playlists def getRawPlaylistMetadata(username,offset_value=0): headers = setHeaders() r_list = [] result_limit = 50 # print(offset_value) params = {'offset': offset_value, 'limit': result_limit} r = requests.get(BASE_URL + 'users/' + username + '/playlists', headers=headers,params=params) response_code = str(r.status_code) sys.stderr.write("\nr response code=" + response_code + "\n\n") r = r.json() try: r_list = r['items'] except KeyError: print("获取Spotify用户所有播放列表元数据失败。") if len(r_list) < result_limit: # print(len(r_list)) return r_list return r_list + getRawPlaylistMetadata(username,offset_value + len(r_list))
你的猜想部分正确,但核心问题出在代码逻辑上,而非PythonAnywhere的服务器机制:
问题根源:
你在函数外部提前执行了auth_response = requests.post(...),这行代码只会在应用启动(重启)时运行一次,获取的令牌自然1小时后过期。而setHeaders()函数只是反复使用这个已经过期的auth_response对象,并没有重新请求新的令牌——这就是为什么封装成函数也没用的原因。验证方法:
- 在
setHeaders()函数内添加日志,打印当前access_token的获取时间或过期时间(Spotify返回的expires_in字段是秒数,默认3600),就能确认是否每次调用都在刷新令牌。 - 查看PythonAnywhere的服务器日志,检查调用Spotify API时返回的401错误是否伴随令牌过期的提示。
- 在
解决方案:
修改setHeaders()函数,将令牌请求逻辑移到函数内部,确保每次调用都重新获取(或判断是否需要刷新)令牌:def setHeaders(): try: # 将请求令牌的逻辑移到函数内部,每次调用都重新请求 auth_response = requests.post(AUTH_URL, { 'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, }) auth_response.raise_for_status() except requests.exceptions.HTTPError as e: print("!!!Error setting auth_response. System detail:") print(e) raise auth_response_data = auth_response.json() access_token = auth_response_data['access_token'] headers = { 'Authorization': 'Bearer {token}'.format(token=access_token) } return headers进一步优化:可以缓存令牌和过期时间,避免每次API调用都请求令牌(比如记录
expires_at = time.time() + auth_response_data['expires_in'],每次调用前检查当前时间是否超过expires_at,再决定是否刷新)。其他排查方向:
- 检查PythonAnywhere的应用进程是否被意外重启或休眠(免费版有休眠机制,长时间无访问会暂停进程,但重启后令牌会重新获取,和你的情况不符)。
- 确认
CLIENT_ID和CLIENT_SECRET是否正确,有没有在环境变量中配置错误(重启时能正常运行,说明配置没问题)。
内容的提问来源于stack exchange,提问作者slimPickens142

