You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask Session跨请求丢失数据:Spotify令牌无法留存求助

Flask Session跨请求丢失Spotify令牌问题解决

问题现象

使用Flask搭建后端实现Spotify登录获取令牌,进而自动创建播放列表时,/callback函数中已将令牌数据存入session(打印显示数据符合预期),但调用/generate-playlist接口时,session中的令牌数据为空,导致播放列表创建失败。/generate-playlist的打印结果如下:

session in generate_playlist:  <FileSystemSession {'_permanent': True}>
token_info: no session was set

已排除的情况:前端无CORS错误,Spotify令牌本身有效,GPT-4调用正常,测试数据也无法跨请求留存,确定问题仅出在Flask Session的配置或使用上。

代码示例(app.py)

from flask import Flask, request, redirect, session, jsonify, render_template
from flask_session import Session
from flask_cors import CORS
from dotenv import load_dotenv
import spotipy
from spotipy.oauth2 import SpotifyOAuth, SpotifyOauthError
from openai import OpenAI
import json
import os

print('app.py running')
load_dotenv()

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['SESSION_TYPE'] = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app, resources={r"/*": {"origins": "http://localhost:3000", "allow_headers": "*", "methods": ["GET", "POST", "OPTIONS"]}}, supports_credentials=True)


SPOTIFY_CLIENT_ID = os.getenv('SPOTIFY_CLIENT_ID')
SPOTIFY_CLIENT_SECRET = os.getenv('SPOTIFY_CLIENT_SECRET')
SPOTIFY_REDIRECT_URI = os.getenv('SPOTIFY_REDIRECT_URI')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')

# Set up Spotify authentication
oauth = SpotifyOAuth(
    client_id=SPOTIFY_CLIENT_ID,
    client_secret=SPOTIFY_CLIENT_SECRET,
    redirect_uri=SPOTIFY_REDIRECT_URI,
    scope='user-read-private user-read-email user-read-playback-state user-modify-playback-state user-read-recently-played user-top-read playlist-modify-public'
)

client = OpenAI(
    api_key=os.environ['OPENAI_API_KEY'],  # this is also the default, it can be omitted
)


@app.route('/')
def home():
    return 'hello'

@app.route('/login')
def login():
    auth_url = oauth.get_authorize_url()
    return redirect(auth_url)

@app.route('/callback')
def callback():
    session.clear()
    code = request.args.get('code')
    if code is None:
        return redirect('http://localhost:3000?error=missing_code')

    try:
        token_info = oauth.get_access_token(code)
        Session["token_info"] = token_info
        print('cb session: ', Session)
        return redirect('http://localhost:3000')
    except SpotifyOauthError as e:
        print('spotify oauth error')
        return redirect(f'http://localhost:3000?error={str(e)}')

@app.route('/generate-playlist', methods=['POST', 'OPTIONS'])
def generate_playlist():
    print('session in generate_playlist early: ', session)
    if request.method == 'OPTIONS':
        return '', 204

    prompt = request.json['prompt']
    # Use the OpenAI API to generate a playlist based on the prompt
    print('creating openai')
    openai_response = client.chat.completions.create(
        model='gpt-4',
        messages=[
            {"role": "user", "content": f'Generate a playlist based on the following prompt and format into a JSON array of song name, song artist, and whether or not it is on spotify, example being [["One More Time", "Daft Punk", "Yes"], ["Aerodynamic", "Daft Punk", "Yes"]]: {prompt}'}
        ]
    )
    playlist_array = openai_response.choices[0].message.content.strip()
    print('gen text: ', playlist_array)
    playlist_data = json.loads(playlist_array)
    print('pl: ', playlist_data)

    print('session in generate_playlist: ', session)  # Add this line to check the session

    token_info = session.get("token_info", "no session was set")
    print('token_info:', token_info)
    if token_info is None:
        print('Missing token')
        return jsonify({'error': 'missing_token'}), 401

    try:
        sp = spotipy.Spotify(auth=token_info['access_token'])
        track_ids = []
        for track_info in playlist_data:
            track_name, artist_name, on_spotify = track_info
            if on_spotify.lower() == 'yes':
                results = sp.search(q=f'track:{track_name} artist:{artist_name}', type='track', limit=1)
                if results['tracks']['items']:
                    track_id = results['tracks']['items'][0]['id']
                    track_ids.append(track_id)

        # Create a new playlist on Spotify
        print('track ids: ', track_ids)
        user_id = sp.current_user()['id']
        playlist = sp.user_playlist_create(user_id, name=prompt, public=True)

        # Add the tracks to the playlist
        if track_ids:
            print('adding track ids')
            sp.user_playlist_add_tracks(user_id, playlist['id'], track_ids)

        playlist_response = {
            'name': playlist['name'],
            'url': playlist['external_urls']['spotify'],
            'tracks': [{'name': track_info[0], 'artist': track_info[1]} for track_info in playlist_data],
        }
        print('returning playlist data')
        return jsonify(playlist_response)
    except spotipy.SpotifyException as e:
        print('Spotify API error:', e)
        return jsonify({'error': 'spotify_api_error', 'message': str(e)}), 500
    except Exception as e:
        print('Error:', e)
        return jsonify({'error': 'internal_server_error', 'message': str(e)}), 500


if __name__ == "__main__":
    app.run(debug=True)

问题原因及解决办法

1. 核心错误:误用Session类而非session请求上下文对象

在/callback函数中,错误地使用Session["token_info"] = token_info存储数据,但Session是flask_session提供的类,并非当前请求的session实例。正确做法是使用从flask导入的session对象:

# 错误写法
Session["token_info"] = token_info
# 正确写法
session["token_info"] = token_info

同时,打印session的代码也需修正:

# 错误写法
print('cb session: ', Session)
# 正确写法
print('cb session: ', session)

2. 确保session持久化(可选)

若需要session在浏览器关闭后仍保留,可在存储token后设置session为持久化:

session["token_info"] = token_info
session.permanent = True  # 添加该行

3. 前端请求需携带Credentials

虽然已在CORS配置中设置supports_credentials=True,但前端发送请求时必须明确携带credentials,否则session标识(Cookie)不会被传递:

  • Fetch API示例:
    fetch('http://localhost:5000/generate-playlist', {
      method: 'POST',
      credentials: 'include',  // 必须添加
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ prompt: '你的提示词' })
    })
    
  • Axios示例:
    axios.post('http://localhost:5000/generate-playlist', { prompt: '你的提示词' }, {
      withCredentials: true  // 必须添加
    })
    

4. 验证session配置正确性

当前已设置SESSION_TYPE='filesystem',可补充以下配置明确session存储规则,避免潜在问题:

app.config['SESSION_FILE_DIR'] = './flask_session'  # 指定session文件存储目录
app.config['SESSION_FILE_THRESHOLD'] = 500  # 最大存储session数量
app.config['SESSION_PERMANENT'] = False  # 默认不持久化,按需调整

总结

最关键的错误是在/callback中误用Session类而非session请求上下文对象存储令牌,修正后即可解决session跨请求丢失的问题。同时需确保前端请求携带credentials,保证session标识能正确传递。

内容的提问来源于stack exchange,提问作者Nisus Imke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:53:17