处理 WebSockets
Handle WebSocket connections in Edge Functions.
Edge Functions 支持托管 WebSocket 服务器,可以实现与浏览器客户端的双向通信。
🌐 Edge Functions supports hosting WebSocket servers that can facilitate bi-directional communications with browser clients.
这让你可以:
🌐 This allows you to:
- 构建实时应用,比如聊天或实时更新
- 为外部 API 创建 WebSocket 中继服务器
- 建立入站和出站的 WebSocket 连接
有关带会话保持和重放功能的生产就绪重连模式,请参见 带边缘函数的可恢复 WebSockets。
🌐 For a production-ready reconnect pattern with session persistence and replay, see Resumable WebSockets with Edge Functions.
创建 WebSocket 服务器 #
🌐 Creating WebSocket servers
这里有一些使用 Deno 和 Node.js API 设置 WebSocket 服务器的基本示例。
🌐 Here are some basic examples of setting up WebSocket servers using Deno and Node.js APIs.
export default { fetch: (req) => { const upgrade = req.headers.get('upgrade') || '' if (upgrade.toLowerCase() != 'websocket') { return Response.json( { error: "request isn't trying to upgrade to WebSocket." }, { status: 400 } ) } const { socket, response } = Deno.upgradeWebSocket(req) socket.onopen = () => console.log('socket opened') socket.onmessage = (e) => { console.log('socket message:', e.data) socket.send(new Date().toString()) } socket.onerror = (e) => console.log('socket errored:', e.message) socket.onclose = () => console.log('socket closed') return response },}出站 WebSockets #
🌐 Outbound WebSockets
你也可以从 Edge Function 建立一个到另一个服务器的出站 WebSocket 连接。
🌐 You can also establish an outbound WebSocket connection to another server from an Edge Function.
将它与传入的 WebSocket 服务器结合使用,就可以把 Edge Functions 当作 WebSocket 代理,例如作为 OpenAI Realtime API 的 中继服务器。
🌐 Combining it with incoming WebSocket servers, it's possible to use Edge Functions as a WebSocket proxy, for example as a relay server for the OpenAI Realtime API.
Local development warning
$CodeSample directive with external repos is not supported in local development because it relies on a GitHub API key. Please check the preview site to see the final UI.身份验证 #
🌐 Authentication
WebSocket 浏览器客户端没有发送自定义头的选项。所以,Edge Functions 将无法像平时那样通过授权头来验证 JWT。
🌐 WebSocket browser clients don't have the option to send custom headers. Because of this, Edge Functions won't be able to perform the usual authorization header check to verify the JWT.
在提供和部署函数时,你可以通过明确提供 --no-verify-jwt 来跳过默认的授权头检查。
🌐 You can skip the default authorization header checks by explicitly providing
--no-verify-jwt when serving and deploying functions.
要对发起 WebSocket 请求的用户进行身份验证,你可以通过 URL 查询参数或自定义协议传递 JWT。withSupabase 封装会验证请求头中的凭证,所以它不能对 WebSocket 客户端进行身份验证。你需要自己验证 JWT,如下所示。
🌐 To authenticate the user making WebSocket requests, you can pass the JWT in URL query
params or via a custom protocol. The withSupabase
wrapper validates credentials on request headers, so it can't authenticate WebSocket
clients. Verify the JWT yourself, as shown below.
import { createClient } from 'npm:@supabase/supabase-js@^2'const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)const supabase = createClient( Deno.env.get('SUPABASE_URL'), // If you want to use a different api key, change 'default' to your preferred key name SUPABASE_SECRET_KEYS['default'])export default { fetch: async (req) => { const upgrade = req.headers.get('upgrade') || '' if (upgrade.toLowerCase() != 'websocket') { return Response.json( { error: "request isn't trying to upgrade to WebSocket." }, { status: 400 } ) } // Please be aware query params may be logged in some logging systems. const url = new URL(req.url) const jwt = url.searchParams.get('jwt') if (!jwt) { console.error('Auth token not provided') return Response.json({ error: 'Auth token not provided' }, { status: 403 }) } const { error, data } = await supabase.auth.getUser(jwt) if (error) { console.error(error) return Response.json({ error: 'Invalid token provided' }, { status: 403 }) } if (!data.user) { console.error('user is not authenticated') return Response.json({ error: 'User is not authenticated' }, { status: 403 }) } const { socket, response } = Deno.upgradeWebSocket(req) socket.onopen = () => console.log('socket opened') socket.onmessage = (e) => { console.log('socket message:', e.data) socket.send(new Date().toString()) } socket.onerror = (e) => console.log('socket errored:', e.message) socket.onclose = () => console.log('socket closed') return response },}使用 WebSockets 时,请记住在 Deno.upgradeWebSocket(req) 返回响应后,HTTP 请求就被认为完成了。为了防止在套接字仍然打开时工作进程过早退出,可以保留一个未解决的 EdgeRuntime.waitUntil() promise,直到 socket.onclose 时才解决。
🌐 When using WebSockets, keep in mind that the HTTP request is considered complete after
Deno.upgradeWebSocket(req) returns the response. To prevent early worker retirement
while the socket is still open, keep an unresolved EdgeRuntime.waitUntil() promise
that resolves in socket.onclose.
在本地测试 WebSockets #
🌐 Testing WebSockets locally
在使用 Supabase CLI 本地测试 Edge Functions 时,请求完成后实例会自动终止。这会导致无法保持 WebSocket 连接打开。
🌐 When testing Edge Functions locally with Supabase CLI, the instances are terminated automatically after a request is completed. This will prevent keeping WebSocket connections open.
为了防止这种情况,你可以用以下设置更新 supabase/config.toml:
🌐 To prevent that, you can update the supabase/config.toml with the following settings:
[edge_runtime]policy = "per_worker"在使用 per_worker 策略运行时,函数在编辑后不会自动重载。你需要通过运行 supabase functions serve 手动重启它。
🌐 When running with per_worker policy, Function won't auto-reload on edits. You will
need to manually restart it by running supabase functions serve.