Apollo 2.x客户端与服务器订阅配置:WebSocket握手错误400
解决Apollo 2.x订阅功能WebSocket握手400错误的方案
嘿,我之前在折腾Apollo 2.x订阅功能时,也碰到过一模一样的WebSocket握手400错误,咱们一步步来排查解决!
1. 先核对客户端与服务器的订阅端点路径是否一致
这是最常见的问题!你客户端连的是ws://localhost:3200/,但很多时候Apollo Server会把订阅端点挂在/graphql路径下,而不是根路径。
- 服务器端检查:看看你的
SubscriptionServer配置里的path参数,比如:SubscriptionServer.create( { schema, execute, subscribe }, { server: httpServer, path: '/graphql' } // 这里的path就是订阅端点路径 ); - 客户端修正:如果服务器路径是
/graphql,那客户端的WebSocket链接URI要改成ws://localhost:3200/graphql,而不是只写localhost:3200。
2. 确保服务器端正确整合HTTP服务器与SubscriptionServer
Apollo 2.x的订阅功能需要把SubscriptionServer附着在HTTP服务器实例上,而不是直接用Express的app.listen启动,否则WebSocket握手会失败。
正确的服务器启动示例:
import express from 'express'; import { ApolloServer } from 'apollo-server-express'; import { createServer } from 'http'; import { SubscriptionServer } from 'subscriptions-transport-ws'; import { execute, subscribe } from 'graphql'; import schema from './schema'; const app = express(); const httpServer = createServer(app); // 先创建HTTP服务器实例 // 挂载Apollo Server const apolloServer = new ApolloServer({ schema }); apolloServer.applyMiddleware({ app }); // 附着SubscriptionServer到HTTP服务器 SubscriptionServer.create( { schema, execute, subscribe }, { server: httpServer, path: '/graphql' } ); // 用HTTP服务器实例启动,而不是app.listen httpServer.listen(3200, () => { console.log(`HTTP服务运行在 http://localhost:3200${apolloServer.graphqlPath}`); console.log(`订阅端点就绪:ws://localhost:3200/graphql`); });
3. 检查CORS配置是否覆盖WebSocket请求
如果你的客户端和服务器端口不同(比如客户端跑在3000,服务器在3200),CORS配置可能会阻止WebSocket握手。
- 在Apollo Server里直接配置CORS:
const apolloServer = new ApolloServer({ schema, cors: { origin: 'http://localhost:3000', // 替换成你的客户端地址 credentials: true } }); - 或者在Express里提前配置CORS中间件,确保包含WebSocket的路径:
import cors from 'cors'; app.use(cors({ origin: 'http://localhost:3000', credentials: true }));
4. 排查网络拦截因素
- 关闭浏览器的代理插件(比如AdBlock、VPN),或者用无痕模式测试,避免插件拦截WebSocket请求。
- 检查本地防火墙是否阻止了3200端口的WebSocket连接,或者看看有没有其他程序占用了3200端口。
5. 客户端WebSocket链接的正确配置示例
确保你的Apollo Client正确整合了WebSocket链接,拆分HTTP和WS请求:
import React from "react"; import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { split } from 'apollo-link'; import { HttpLink } from 'apollo-link-http'; import { WebSocketLink } from 'apollo-link-ws'; import { getMainDefinition } from 'apollo-utilities'; // HTTP链接处理查询和变更 const httpLink = new HttpLink({ uri: 'http://localhost:3200/graphql' }); // WebSocket链接处理订阅 const wsLink = new WebSocketLink({ uri: 'ws://localhost:3200/graphql', // 必须和服务器路径一致 options: { reconnect: true, // 断开自动重连 connectionParams: { // 如果需要认证,在这里传token等参数 } } }); // 拆分链接:根据操作类型选择HTTP或WS const link = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink ); // 创建Apollo Client实例 const client = new ApolloClient({ link, cache: new InMemoryCache() });
先从路径匹配和服务器启动方式这两点入手排查,这两个是最容易出错的地方,大概率能解决你的400错误!
内容的提问来源于stack exchange,提问作者VikR
相关产品推荐
相关产品推荐

