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

通过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)
}

错误原因

  1. 重复注册路由与Handler:每次Lambda触发时,都执行app.use('/swagger-simpler', ...)和serverless(app),导致express路由被重复注册、serverless handler被重复创建。Lambda执行环境会复用,多次触发后路由逻辑混乱,静态资源请求无法正确匹配。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:56:03