问题:GET /whiteboard/{board_id} 同时被 REST(返回 JSON)与 main.py 的 HTML 页面
注册,FastAPI 按注册顺序匹配到 REST,导致浏览器访问拿到 JSON 而非前端页面。
改为按职责分命名空间,避免冲突:
- HTML 页面:/wb/{id}(main.py)、/wb-admin(main.py,Basic Auth)
- 公开 REST:GET /api/wb/{id}(前端 init 拉取初始笔画)
- WS:/ws/wb/{id}(实时同步 + 心跳)
- 管理 REST:GET /api/admin/wb、DELETE /api/admin/wb/{id}(Basic Auth)
前端 whiteboard.js / whiteboard_admin.js、测试脚本、README 路径同步更新。
旧 /whiteboard/* 路径不再注册(404)。
53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
"""验证删除白板时,在线 WS 连接被服务端踢出(收到 error 帧并断连)。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import json
|
||
import urllib.request
|
||
|
||
import websockets
|
||
|
||
BASE_WS = "ws://127.0.0.1:6867/ws/wb"
|
||
BOARD = "kicktest"
|
||
AUTH = "Basic YTo2NjUxMTMxNQ==" # a:66511315
|
||
|
||
|
||
async def main() -> None:
|
||
async with websockets.connect(f"{BASE_WS}/{BOARD}") as a:
|
||
await a.send(json.dumps({"type": "hello", "client_id": "A"}))
|
||
init = json.loads(await asyncio.wait_for(a.recv(), timeout=2))
|
||
assert init["type"] == "init"
|
||
print("A connected, init received")
|
||
|
||
# 通过 REST 删除白板(带 Basic Auth)
|
||
req = urllib.request.Request(
|
||
f"http://127.0.0.1:6867/api/admin/wb/{BOARD}",
|
||
method="DELETE",
|
||
headers={"Authorization": AUTH},
|
||
)
|
||
with urllib.request.urlopen(req) as r:
|
||
print("delete response:", r.read().decode())
|
||
|
||
# A 应收到 error 帧随后连接关闭
|
||
try:
|
||
raw = await asyncio.wait_for(a.recv(), timeout=3)
|
||
msg = json.loads(raw)
|
||
print("A received:", msg)
|
||
assert msg["type"] == "error", "应收到 error 通知"
|
||
except (websockets.ConnectionClosed, asyncio.TimeoutError) as e:
|
||
print("连接关闭/超时:", e)
|
||
|
||
# 确认连接已断
|
||
try:
|
||
await asyncio.wait_for(a.recv(), timeout=2)
|
||
print("ERROR: 连接仍存活(应已断开)")
|
||
except websockets.ConnectionClosed:
|
||
print("连接已被服务端关闭 ✅")
|
||
except asyncio.TimeoutError:
|
||
print("连接未关闭但无消息(部分通过)")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|