fix(docs): 豁免 /docs/json 信封包裹,修复 Swagger UI 无法加载规范
This commit is contained in:
1 parent
ab90c5b4c4
commit
10de5582c7
2 files changed
+17
-5
No files matched your search
+8
-5
@@ -31,6 +31,7 @@ import { redocHtml } from './openapi/ui.js'
|
|||||||
|
|
||||||
// 默认端口 16688:本机 TCP 动态端口范围是 1024~15000(出站连接会随机占用低端端口),避开该区间
|
// 默认端口 16688:本机 TCP 动态端口范围是 1024~15000(出站连接会随机占用低端端口),避开该区间
|
||||||
const PORT = process.env.PORT || 16688
|
const PORT = process.env.PORT || 16688
|
||||||
|
const DOCS_PREFIX = '/docs'
|
||||||
const API_TITLE = 'MusicServer 聚合音乐 API'
|
const API_TITLE = 'MusicServer 聚合音乐 API'
|
||||||
const API_DESCRIPTION = '多平台音乐聚合接口:搜索、播放地址、歌词、封面、榜单、歌单。' +
|
const API_DESCRIPTION = '多平台音乐聚合接口:搜索、播放地址、歌词、封面、榜单、歌单。' +
|
||||||
'支持 kw(酷我)/ kg(酷狗)/ tx(QQ音乐)/ wy(网易云)/ mg(咪咕)平台。'
|
'支持 kw(酷我)/ kg(酷狗)/ tx(QQ音乐)/ wy(网易云)/ mg(咪咕)平台。'
|
||||||
@@ -74,10 +75,13 @@ fastify.addHook('onResponse', async(request, reply) => {
|
|||||||
console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} → ${reply.statusCode} (${Math.round(reply.elapsedTime)}ms)`)
|
console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} → ${reply.statusCode} (${Math.round(reply.elapsedTime)}ms)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 成功响应统一包成 { code: 0, data };文档页/raw 路由与非 200 响应不包
|
// 成功响应统一包成 { code: 0, data };文档页/raw 路由与非 200 响应不包。
|
||||||
|
// @fastify/swagger-ui 的规范端点 {DOCS_PREFIX}/json 由插件内部注册、无法加 raw 标记,
|
||||||
|
// 必须豁免:Swagger UI 强制从该地址加载规范,包了信封顶层就没有 openapi 字段,页面直接报错
|
||||||
fastify.addHook('preSerialization', async(request, reply, payload) => {
|
fastify.addHook('preSerialization', async(request, reply, payload) => {
|
||||||
if (reply.statusCode !== 200) return payload
|
if (reply.statusCode !== 200) return payload
|
||||||
if (request.routeOptions.config?.raw) return payload
|
if (request.routeOptions.config?.raw) return payload
|
||||||
|
if (request.routeOptions.url === `${DOCS_PREFIX}/json`) return payload
|
||||||
if (payload === null || typeof payload === 'object') return { code: 0, data: payload }
|
if (payload === null || typeof payload === 'object') return { code: 0, data: payload }
|
||||||
return payload
|
return payload
|
||||||
})
|
})
|
||||||
@@ -141,10 +145,9 @@ fastify.register(swagger, {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.register(swaggerUi, {
|
// 注意:插件初始化器会把规范地址强制为 {DOCS_PREFIX}/json(uiConfig 的 url 会被覆盖),
|
||||||
routePrefix: '/docs',
|
// 真正对外文档化的规范地址是下方带 raw 标记的 /openapi.json
|
||||||
url: '/openapi.json',
|
fastify.register(swaggerUi, { routePrefix: DOCS_PREFIX })
|
||||||
})
|
|
||||||
|
|
||||||
fastify.get('/openapi.json', {
|
fastify.get('/openapi.json', {
|
||||||
config: { raw: true },
|
config: { raw: true },
|
||||||
|
|||||||
@@ -305,6 +305,15 @@ await test('OpenAPI 规范 /openapi.json', async() => {
|
|||||||
return `${Object.keys(spec.paths).length} 个路径`
|
return `${Object.keys(spec.paths).length} 个路径`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test('Swagger UI 规范端点 /docs/json(不可包信封)', async() => {
|
||||||
|
const r = await getRaw('/docs/json')
|
||||||
|
if (r.status !== 200) throw new Error(`期望 200,实际 ${r.status}`)
|
||||||
|
const spec = JSON.parse(r.body)
|
||||||
|
// Swagger UI 强制从该地址加载规范:若被全局信封钩子包成 { code, data },顶层无 openapi 字段,文档页直接报错
|
||||||
|
if (!spec.openapi?.startsWith('3.')) throw new Error(`顶层缺少 openapi 字段(疑似被信封包裹): ${r.body.slice(0, 80)}`)
|
||||||
|
return spec.openapi
|
||||||
|
})
|
||||||
|
|
||||||
// 汇总
|
// 汇总
|
||||||
const passed = results.filter(r => r.ok).length
|
const passed = results.filter(r => r.ok).length
|
||||||
const failed = results.filter(r => !r.ok)
|
const failed = results.filter(r => !r.ok)
|
||||||
|
|||||||
Reference in new issue
Block a user