如何解码Blazor的SignalR请求响应,实现Python机器人服务请求
用Python对接Blazor的SignalR二进制请求方案
核心原理
Blazor Server依赖SignalR实现双向通信,你抓包看到的二进制请求是MessagePack序列化格式(SignalR默认的高效二进制序列化方式),只要解码这种格式并遵循SignalR的通信流程,就能用Python模拟请求。
步骤1:完成SignalR协商流程
SignalR通信的第一步是协商,用来获取连接凭证和支持的传输方式,对应你抓包的第一个JSON响应:
{"negotiateVersion":1,"connectionId":"mPtGjJ-copmHDi80CWjLew","connectionToken":"2tEI4GP4tq3gVE_dBzjGoQ","availableTransports":[{"transport":"ServerSentEvents","transferFormats":["Text"]},{"transport":"LongPolling","transferFormats":["Text","Binary"]}]}
用Python发起协商请求:
import requests # 替换为实际的Blazor服务器协商端点(通常是/_blazor/negotiate) negotiate_url = "https://your-blazor-server/_blazor/negotiate" resp = requests.post(negotiate_url) negotiate_info = resp.json() # 提取关键参数 connection_id = negotiate_info["connectionId"] connection_token = negotiate_info["connectionToken"]
步骤2:解码SignalR二进制请求
你抓包的第二个二进制内容是MessagePack格式的Invoke调用,包含要执行的方法(DispatchEventAsync)和点击事件参数。用Python的msgpack库解码:
import msgpack # 将抓包的二进制数据转为bytes类型(示例用你提供的内容转码) binary_req = b'\xc2\x19\xc0\xb2DispatchEventAsync\xda[{"eventHandlerId":654,"eventName":"click","eventFieldInfo":null}, {"detail":1,"screenX":713,"screenY":484,"clientX":675,"clientY":308,"offsetX":68,"offsetY":35,"pageX" :675,"pageY":508,"button":0,"buttons":0,"ctrlKey":false,"shiftKey":false,"altKey":false,"metaKey":false,"type":"click"}]' # 解码MessagePack数据 decoded_data = msgpack.unpackb(binary_req, raw=False) method_name = decoded_data[0] # 得到 "DispatchEventAsync" event_params = decoded_data[1] # 得到点击事件的参数数组 print(f"调用方法:{method_name}") print(f"事件参数:{event_params}")
步骤3:用Python模拟发送SignalR请求
选择抓包中支持Binary传输的LongPolling方式,构造MessagePack格式的请求发送:
# 发送消息的端点(替换为实际路径) send_url = f"https://your-blazor-server/_blazor/send?connectionId={connection_id}&connectionToken={connection_token}" # 构造要发送的MessagePack数据(格式为[方法名, 参数数组]) send_payload = msgpack.packb([ "DispatchEventAsync", [ {"eventHandlerId": 654, "eventName": "click", "eventFieldInfo": None}, {"detail": 1, "screenX": 713, "screenY": 484, "clientX": 675, "clientY": 308, "offsetX": 68, "offsetY": 35, "pageX": 675, "pageY": 508, "button": 0, "buttons": 0, "ctrlKey": False, "shiftKey": False, "altKey": False, "metaKey": False, "type": "click"} ] ]) # 发送请求,设置正确的Content-Type headers = {"Content-Type": "application/x-msgpack"} resp = requests.post(send_url, data=send_payload, headers=headers) print(f"请求状态码:{resp.status_code}")
关键注意事项
- 端点路径:Blazor Server的SignalR端点通常是
/_blazor/negotiate、/_blazor/connect、/_blazor/send,要以实际抓包的路径为准。 - 请求头模拟:服务器可能验证
User-Agent、Referer等头信息,直接复制浏览器抓包的请求头加到Python请求中,避免被拦截。 - eventHandlerId:这个ID是服务器为页面元素生成的临时标识,每次连接可能变化,需要在协商后通过抓包页面初始化的SignalR消息获取,或者模拟页面加载流程拿到。
- 连接生命周期:SignalR连接需要维护心跳,长时间不交互可能被断开,必要时要处理重连逻辑。
内容的提问来源于stack exchange,提问作者mohsen kazemi
相关产品推荐
相关产品推荐

