GraphQL订阅本地正常,部署Heroku后WebSocket连接失败
看起来你本地测试订阅功能完全正常,但一部署到Heroku就卡壳了——这种情况大多是Heroku的环境特性和WebSocket的配置细节没匹配上。我帮你梳理几个关键调整点,按顺序试试:
1. 确保端口使用Heroku的动态分配端口
Heroku不会用你本地的固定端口,而是通过process.env.PORT提供动态端口。检查你的config.port是否正确读取这个环境变量:
// 可以直接在代码里修改监听端口 const port = process.env.PORT || 3000; server.listen(port, () => { ... });
2. 修正CORS配置,允许WebSocket握手请求
WebSocket的连接握手是GET请求,并且需要Upgrade和Connection头,你当前的CORS配置有两个明显问题:
- Express的CORS中间件没允许WebSocket所需的特殊头
- Apollo Server的cors配置只开放了POST方法
先更新Express的CORS中间件:
api.use(function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', '*'); // 加入GET和OPTIONS,支持WebSocket握手预检 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // 加入WebSocket需要的Upgrade、Connection头 res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Authorization, Upgrade, Connection'); // OPTIONS请求直接返回200,避免预检失败 if (req.method === 'OPTIONS') { return res.sendStatus(200); } next(); });
然后修正Apollo Server的applyMiddleware配置,放开methods和头:
graphQLServer.applyMiddleware({ app: api, cors: { origin: true, credentials: true, // 加入GET和OPTIONS methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: [ 'X-Requested-With', 'X-HTTP-Method-Override', 'Content-Type', 'Accept', 'Authorization', 'Access-Control-Allow-Origin', 'Upgrade', // 新增WebSocket所需头 'Connection' // 新增WebSocket所需头 ], }, playground: { settings: { 'editor.theme': 'light', }, }, });
3. 避免bodyParser解析WebSocket请求
bodyParser只需要处理POST请求的body,WebSocket的握手是GET请求,不要被bodyParser干扰。把bodyParser的应用改成只针对POST路由:
// 只对POST请求解析body api.post('*', bodyParser.urlencoded({ extended: false })); api.post('*', bodyParser.json());
4. 显式配置Apollo Server的订阅路径
有时候Playground会因为路径匹配问题连不上,显式指定订阅路径和你的GraphQL端点完全一致:
const graphQLServer = new ApolloServer({ schema, uploads: false, subscriptions: { path: '/graphql', // 和你的端点路径保持一致 onConnect: () => { console.log('WebSocket连接成功'); }, }, });
5. 检查Helmet的安全头是否拦截了WebSocket
Helmet的Content-Security-Policy可能会阻止WebSocket连接,调整配置允许wss协议:
api.use(helmet({ dnsPrefetchControl: false, frameguard: false, ieNoOpen: false, contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], // 允许你的Heroku域名的WebSocket连接 connectSrc: ["'self'", "wss://foodesk.herokuapp.com"], }, }, }));
6. 确认Procfile配置正确
确保你的Procfile指定的是web dyno,这样Heroku才会把HTTP/WebSocket请求路由过来:
web: node path/to/your/server/file.js
做完这些调整后,重新部署到Heroku,应该就能正常连接WebSocket订阅了。如果还是有问题,可以查看Heroku的日志(heroku logs --tail),看看有没有WebSocket相关的错误信息,能帮你进一步定位问题。
内容的提问来源于stack exchange,提问作者Fateh

