Express中间件报错:TypeError: Converting circular structure to JSON
问题描述
使用express-session与genius-lyrics包编写initSongArray中间件时,尝试返回简单JSON(如{ num: 20 })会触发TypeError: Converting circular structure to JSON错误,移除res.status.json语句后代码运行正常。
代码示例
async function initSongArray(req, res, next){ const searches = await Client.songs.search(req.params.artist); let songNum = 0 while((searches[songNum].artist.name.includes('&')) && req.params.and === 0){ console.log("& detected") songNum++; } req.session.artist = searches[songNum].artist; req.session.songArray = await req.session.artist.songs({sort: 'popularity', perPage: 20, page: 1}); req.session.currPage = 2 console.log(req.session.songArray.length) res.status(200).json( { num: 20 } ) } app.get('/artist/:artist/:and', initSongArray)
报错栈
>C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express-session\index.js:598 var str = JSON.stringify(sess, function (key, val) { ^ TypeError: Converting circular structure to JSON --> starting at object with constructor 'Client' | property 'songs' -> object with constructor 'SongsClient' --- property 'client' closes the circle at JSON.stringify () at hash (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express-session\index.js:598:18) at isModified (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express-session\index.js:426:57) at shouldSave (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express-session\index.js:448:11) at ServerResponse.end (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express-session\index.js:334:11) at ServerResponse.send (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express\lib\response.js:232:10) at ServerResponse.json (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\node_modules\express\lib\response.js:278:15) at initSongArray (C:\Users\bgrie\Desktop\a\compsci.websites\typesiteNode\index.js:70:21) at processTicksAndRejections (node:internal/process/task_queues:96:5)
原因分析
报错根源不是返回的JSON数据,而是express-session序列化session对象时失败。你将genius-lyrics包的原生Artist对象直接存入req.session.artist,该对象内部存在循环引用:Client的songs属性指向SongsClient,而SongsClient的client属性又指向原Client,形成闭环。当调用res.json触发响应结束时,express-session会自动保存session到存储介质,此时需要将session对象序列化为JSON,循环引用结构无法被JSON.stringify处理,因此抛出错误。
解决方案
不要直接将genius-lyrics的原生对象存入session,仅提取可序列化的核心字段:
方案1:提取核心字段存入session
async function initSongArray(req, res, next){ const searches = await Client.songs.search(req.params.artist); let songNum = 0 while((searches[songNum].artist.name.includes('&')) && req.params.and === 0){ console.log("& detected") songNum++; } // 提取可序列化的artist基础信息,而非存入整个对象 const artistData = { id: searches[songNum].artist.id, name: searches[songNum].artist.name, url: searches[songNum].artist.url // 按需添加其他需要的字段 }; req.session.artist = artistData; // 直接通过原artist对象获取歌曲列表 req.session.songArray = await searches[songNum].artist.songs({sort: 'popularity', perPage: 20, page: 1}); req.session.currPage = 2 console.log(req.session.songArray.length) res.status(200).json( { num: req.session.songArray.length } ) }
方案2:手动过滤循环引用字段
如果需要保留更多字段,可通过JSON序列化时的过滤函数移除循环引用部分:
async function initSongArray(req, res, next){ const searches = await Client.songs.search(req.params.artist); let songNum = 0 while((searches[songNum].artist.name.includes('&')) && req.params.and === 0){ console.log("& detected") songNum++; } // 过滤掉导致循环引用的client属性,转为普通对象 const plainArtist = JSON.parse(JSON.stringify(searches[songNum].artist, (key, val) => { if (key === 'client') return undefined; return val; })); req.session.artist = plainArtist; req.session.songArray = await searches[songNum].artist.songs({sort: 'popularity', perPage: 20, page: 1}); req.session.currPage = 2 console.log(req.session.songArray.length) res.status(200).json( { num: 20 } ) }
处理后session对象不再包含循环引用,express-session可正常序列化,res.json也能正常执行。
内容的提问来源于stack exchange,提问作者ben
相关产品推荐
相关产品推荐

