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

如何获取PureScript应用当前基础URL以动态构建WebSocket连接?

嘿,这个动态生成WebSocket连接地址的需求我之前也碰到过,在PureScript里有两种靠谱的实现方式,我给你详细讲讲:

方法1:通过浏览器Location API动态拼接

这种方式完全在前端处理,不需要后端改动,核心是利用浏览器的window.location对象获取当前页面的服务信息,再转换成WebSocket协议的地址。

代码实现

import Web.HTML (window)
import Web.HTML.Location (Location, hostname, port, protocol)
import Web.HTML.Window (location)
import Effect (Effect)
import Data.String (replace)

-- 动态构建WebSocket连接地址
buildWsUrl :: Effect String
buildWsUrl = do
  -- 获取当前窗口对象
  win <- window
  -- 获取当前页面的location信息
  loc <- location win
  -- 提取协议、主机名、端口
  currentProto <- protocol loc
  currentHost <- hostname loc
  currentPort <- port loc
  
  -- 将http/https替换为ws/wss(自动适配加密连接)
  let wsProtocol = replace "http" "ws" currentProto
      -- 处理默认端口(80/443时浏览器不会显示端口,所以空字符串就不用加冒号)
      baseUrl = wsProtocol <> "//" <> currentHost <> (if currentPort == "" then "" else ":" <> currentPort)
  
  -- 拼接固定的WebSocket路径
  pure $ baseUrl <> "/ws/blah"

使用示例

你可以在创建WebSocket连接的时候调用这个函数:

import Web.WebSocket.WebSocket (newWebSocket)
import Effect.Class.Console (log)

initWebSocket :: Effect Unit
initWebSocket = do
  wsUrl <- buildWsUrl
  ws <- newWebSocket wsUrl
  log $"WebSocket连接地址:{wsUrl}"
  -- 这里可以添加onopen、onmessage等事件监听逻辑

方法2:后端注入全局变量(更推荐)

如果你的Suave后端可以控制前端页面的渲染,这种方式更可靠——后端直接根据当前运行环境生成正确的WebSocket地址,注入到页面的全局变量中,前端直接读取使用即可,避免前端拼接时可能出现的协议/端口错误。

后端(Suave)改动

在返回前端页面的模板中,添加一个全局变量:

open Suave
open Suave.Html

let getWsBaseUrl (ctx: HttpContext) =
  -- 根据当前环境配置生成WebSocket地址
  let host = ctx.request.host
  let port = ctx.request.port |> Option.map string |> Option.defaultValue ""
  let portSuffix = if port = "" then "" else ":" + port
  $"ws://{host}{portSuffix}/ws/blah"

let indexHandler (ctx: HttpContext) =
  let wsBaseUrl = getWsBaseUrl ctx
  html [
    head []
    body [
      script [src "/output/bundle.js"] []
      -- 注入全局变量
      script [] [ text $"window.wsBaseUrl = \"{wsBaseUrl}\";" ]
    ]
  ]
  |> OK
  |> ctx |> run

前端(PureScript)读取

直接读取全局变量来创建WebSocket连接:

import Effect (Effect)
import Web.HTML (window)
import Data.Nullable (toMaybe)
import Web.WebSocket.WebSocket (newWebSocket)
import Effect.Class.Console (log)

initWebSocket :: Effect Unit
initWebSocket = do
  win <- window
  -- 读取后端注入的全局变量
  let wsUrl = toMaybe (win ## wsBaseUrl)
  case wsUrl of
    Just url -> do
      ws <- newWebSocket url
      log $"已连接到WebSocket:{url}"
      -- 添加WebSocket事件监听
    Nothing -> log "错误:未找到WebSocket连接地址"

注意事项

  • 如果你的应用部署在HTTPS环境下,方法1中的replace "http" "ws"会自动把https:转换成wss:,刚好适配加密的WebSocket连接,无需额外处理。
  • 方法2的优势在于:当后端部署在反向代理后(比如Nginx),前端自己拼接可能会拿到代理的地址而非真实后端地址,后端注入的方式可以避免这个问题。

内容的提问来源于stack exchange,提问作者Sandoichi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:42:39