通过Lambda部署Swagger UI文档时出现语法错误的排查求助
问题分析与解决:Swagger UI在AWS Lambda+API Gateway下的静态资源加载错误
我尝试通过Swagger UI展示部署在AWS API Gateway上的自研API文档,开发了Lambda函数处理/swagger-simpler端点。已确认成功获取到Swagger JSON,但访问该端点时,swagger-ui-bundle.js出现错误:Uncaught SyntaxError: expected expression, got '<'。查看该JS文件内容时,发现它和/swagger-simpler端点返回的HTML完全一致。请问哪里操作有误?
原Lambda代码
/** @format */ import 'source-map-support/register' import express from 'express' import serverless from 'serverless-http' import swaggerUi from 'swagger-ui-express' import { Handler } from 'aws-lambda' import { APIGatewayClient, GetExportCommand } from '@aws-sdk/client-api-gateway' const app = express() const apiGateway = new APIGatewayClient({}) export const handler: Handler = async (event, context) => { const apiId = event.requestContext.apiId const stage = event.requestContext.stage console.debug('From request context', { apiId, stage }) let swaggerJson: swaggerUi.JsonObject try { swaggerJson = await getSwaggerJson(apiId, stage) } catch (e) { console.error('Failed to retreive Swagger JSON', e) throw new Error('Failed to retreive Swagger JSON') } console.debug('Got Swagger doc object', { swaggerJson }) app.use('/swagger-simpler', swaggerUi.serve, swaggerUi.setup(swaggerJson)) console.debug('here') const handler = serverless(app) console.debug('got handler', { handler }) const ret = await handler(event, context) console.debug('handler returned', { ret }) return ret } const getSwaggerJson = async ( restApiId: string, stageName: string ): Promise<swaggerUi.JsonObject> => { const params = { exportType: 'oas30', restApiId, stageName, accepts: 'application/json', } const res = await apiGateway.send(new GetExportCommand(params)) console.debug('GetExportCommand successful', { res }) let swaggerJson: string if (res.body) { swaggerJson = Buffer.from(res.body).toString() } else { throw new Error('Empty response body from GetExportCommand') } console.debug('Got Swagger JSON', { swaggerJson }) return JSON.parse(swaggerJson) }
错误原因
- 重复注册路由与Handler:每次Lambda触发时,都执行
app.use('/swagger-simpler', ...)和serverless(app),导致express路由被重复注册、serverless handler被重复创建。Lambda执行环境会复用,多次触发后路由逻辑混乱,静态资源请求无法正确匹配。 - API Gateway路径配置缺失:如果API Gateway只配置了
/swagger-simpler的GET方法,未包含/swagger-simpler/{proxy+}的资源路径,Swagger UI请求的静态资源(如/swagger-simpler/swagger-ui-bundle.js)会被API Gateway拒绝,返回默认HTML页面,浏览器将HTML当作JS解析就会出现语法错误。
解决方案
1. 调整Lambda代码:避免重复初始化
将路由注册和serverless handler的创建移到模块级别,仅在第一次冷启动时执行一次:
/** @format */ import 'source-map-support/register' import express from 'express' import serverless from 'serverless-http' import swaggerUi from 'swagger-ui-express' import { Handler } from 'aws-lambda' import { APIGatewayClient, GetExportCommand } from '@aws-sdk/client-api-gateway' const app = express() const apiGateway = new APIGatewayClient({}) // 缓存serverless handler,避免重复创建 let serverlessHandler: ReturnType<typeof serverless> // 初始化Swagger路由的函数,仅执行一次 const initSwaggerRoutes = async (apiId: string, stage: string) => { let swaggerJson: swaggerUi.JsonObject try { swaggerJson = await getSwaggerJson(apiId, stage) } catch (e) { console.error('获取Swagger JSON失败', e) throw new Error('获取Swagger JSON失败') } console.debug('获取到Swagger文档对象', { swaggerJson }) // 挂载Swagger UI路由(仅一次) app.use('/swagger-simpler', swaggerUi.serve, swaggerUi.setup(swaggerJson)) // 创建serverless handler serverlessHandler = serverless(app) } export const handler: Handler = async (event, context) => { const apiId = event.requestContext.apiId const stage = event.requestContext.stage console.debug('从请求上下文获取', { apiId, stage }) // 仅在第一次触发时初始化路由和handler if (!serverlessHandler) { await initSwaggerRoutes(apiId, stage) } const ret = await serverlessHandler(event, context) console.debug('handler返回结果', { ret }) return ret } const getSwaggerJson = async ( restApiId: string, stageName: string ): Promise<swaggerUi.JsonObject> => { const params = { exportType: 'oas30', restApiId, stageName, accepts: 'application/json', } const res = await apiGateway.send(new GetExportCommand(params)) console.debug('GetExportCommand执行成功', { res }) let swaggerJson: string if (res.body) { swaggerJson = Buffer.from(res.body).toString() } else { throw new Error('GetExportCommand返回空响应体') } console.debug('获取到Swagger JSON字符串', { swaggerJson }) return JSON.parse(swaggerJson) }
2. 配置API Gateway资源路径
在API Gateway中添加/swagger-simpler/{proxy+}的资源,并将其ANY方法关联到Lambda函数。这样所有/swagger-simpler下的子路径请求(包括静态资源)都会被转发到Lambda,由express正确处理。
内容的提问来源于stack exchange,提问作者Aurelia Peters
相关产品推荐
相关产品推荐

