如何解决swagger-ui-express中Spec2的CORS跨域问题?
我来帮你拆解下这个问题——你遇到的CORS报错根本不是你的Express服务造成的,而是浏览器的同源策略限制:当Swagger UI在你的http://localhost:8080/api-docs页面里直接请求http://xxx.xxx.xxx.xxx:xxxxx/swagger.json时,两个地址的域名/端口不一样,浏览器会拦截这个跨域请求,而你在自己服务里加的cors()或者swaggerUi.cors()完全管不到这个场景,因为这些配置是针对你的服务收到的跨域请求,而不是前端页面发起的跨域请求。
下面给你两种可行的修复方案,根据你的实际情况选择:
方案一:在Spec2的后端服务配置CORS(如果你能控制该服务)
如果http://xxx.xxx.xxx.xxx:xxxxx这个服务是你自己维护的,直接在它的代码里配置CORS,允许你的Swagger UI页面所在的域名访问:
const express = require('express'); const cors = require('cors'); const app = express(); // 只允许你的Swagger UI域名访问,生产环境推荐这种方式 app.use(cors({ origin: 'http://localhost:8080' // 替换成你实际部署Swagger UI的域名 })); // 其他业务代码... app.listen(xxxxx, () => console.log(`Listening on port xxxxx!`))
如果是测试环境图省事,也可以临时用origin: '*'允许所有域名访问,但生产环境绝对不要这么做,会有安全风险。
方案二:用反向代理转发请求(如果你无法控制Spec2的后端)
如果那个外部服务你没法修改,那就把你的Express服务当成中间层,做反向代理:让Swagger UI请求你自己服务的接口,再由你的服务转发到目标地址,这样浏览器只和同域的服务通信,就不会触发CORS限制了。
步骤如下:
- 安装代理依赖:
npm install http-proxy-middleware
- 修改你的Express代码,添加代理配置并更新Swagger的URL:
const express = require('express'); const cors = require('cors'); const swaggerUi = require('swagger-ui-express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); // 配置代理:把/api-spec2开头的请求转发到目标服务 app.use('/api-spec2', createProxyMiddleware({ target: 'http://xxx.xxx.xxx.xxx:xxxxx', changeOrigin: true, // 必须开启,修改请求头的Host为目标服务的Host pathRewrite: { '^/api-spec2': '' // 去掉/api-spec2前缀,转发到目标服务的根路径 } })); // 更新Swagger配置,把Spec2的URL改成代理路径 var options = { explorer: true, swaggerOptions: { urls: [ { url: 'http://petstore.swagger.io/v2/swagger.json', name: 'Spec1' }, { url: '/api-spec2/swagger.json', name: 'Spec2' } // 这里用相对路径,避免硬编码域名 ] } } app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(null, options)); app.listen(8080, () => console.log(`Listening on port 8080!`))
这样修改后,Swagger UI会请求http://localhost:8080/api-spec2/swagger.json,这和页面域名同域,浏览器不会拦截,你的服务再把请求转发到目标地址,后端之间的请求不受同源策略限制,完美绕过CORS问题。
内容的提问来源于stack exchange,提问作者John Glabb

