如何在RAZOR的OnPost方法中通过SignalR向单个客户端推送状态更新
Razor页面中OnPost触发时向单个客户端推送SignalR进度更新
核心思路是利用SignalR的ConnectionId(每个客户端连接的唯一标识),在发起请求时将该ID传给服务器端的OnPost方法,之后服务器仅向此ConnectionId对应的客户端推送进度。
1. SignalR Hub 改造
创建或修改Hub类,可封装定向推送逻辑,也可直接在PageModel中通过IHubContext操作:
using Microsoft.AspNetCore.SignalR; public class ProgressHub : Hub { // 封装定向推送方法,方便复用 public async Task SendProgress(string connectionId, int progress, string message) { await Clients.Client(connectionId).SendAsync("ReceiveProgress", progress, message); } }
2. 注册SignalR服务(Program.cs/.NET 6+)
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddSignalR(); // 注册SignalR服务 var app = builder.Build(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapHub<ProgressHub>("/progressHub"); // 映射Hub端点 app.MapRazorPages(); app.Run();
3. Razor页面前端实现(Fetch/AJAX触发)
以下示例包含SignalR连接建立、进度监听,以及Fetch触发OnPost的逻辑:
@page @model IndexModel @{ ViewData["Title"] = "进度推送示例"; } <div> <button id="startTaskBtn">启动后台任务</button> <div id="progressContainer" style="margin-top:10px;"></div> </div> <!-- 引入SignalR客户端脚本(需提前通过LibMan安装@microsoft/signalr包) --> <script src="~/lib/signalr/dist/browser/signalr.js"></script> <script> // 初始化SignalR连接 const progressConnection = new signalR.HubConnectionBuilder() .withUrl("/progressHub") .build(); // 监听服务器推送的进度更新 progressConnection.on("ReceiveProgress", (progress, msg) => { document.getElementById("progressContainer").textContent = `当前进度:${progress}% | ${msg}`; }); // 启动连接 progressConnection.start().catch(err => console.error(err)); // 用Fetch触发OnPost并传递ConnectionId document.getElementById("startTaskBtn").addEventListener("click", async () => { const connId = progressConnection.connectionId; const csrfToken = document.querySelector('input[name="__RequestVerificationToken"]').value; const response = await fetch("/Index", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "RequestVerificationToken": csrfToken }, body: `connectionId=${encodeURIComponent(connId)}` }); const result = await response.text(); console.log("任务结果:", result); }); </script>
4. Razor页面后端(PageModel)实现
在OnPost方法中接收connectionId,并向指定客户端推送进度:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; public class IndexModel : PageModel { private readonly IHubContext<ProgressHub> _hubContext; public IndexModel(IHubContext<ProgressHub> hubContext) { _hubContext = hubContext; } public void OnGet() { } public async Task<IActionResult> OnPostAsync(string connectionId) { // 模拟耗时任务,分步推送进度 for (int progress = 0; progress <= 100; progress += 10) { await Task.Delay(500); // 模拟业务耗时操作 // 向指定客户端推送进度 await _hubContext.Clients.Client(connectionId) .SendAsync("ReceiveProgress", progress, $"执行阶段 {progress/10 + 1}"); } return new ContentResult { Content = "任务执行完成" }; } }
5. 表单提交适配
若需通过表单触发,可将connectionId存入隐藏域,提交时自动携带:
<form method="post" id="taskForm"> <input type="hidden" name="connectionId" id="connIdInput" /> <button type="submit">表单启动任务</button> </form> <script> // 连接启动后填充隐藏域 progressConnection.start().then(() => { document.getElementById("connIdInput").value = progressConnection.connectionId; }).catch(err => console.error(err)); // 阻止默认表单提交,用AJAX方式保留页面状态 document.getElementById("taskForm").addEventListener("submit", async (e) => { e.preventDefault(); const formData = new FormData(e.target); const csrfToken = document.querySelector('input[name="__RequestVerificationToken"]').value; const response = await fetch("/Index", { method: "POST", headers: { "RequestVerificationToken": csrfToken }, body: formData }); const result = await response.text(); console.log("任务结果:", result); }); </script>
注意事项
- 确保SignalR客户端脚本已正确引入(可通过LibMan从npm获取
@microsoft/signalr包) - 必须携带CSRF令牌(
__RequestVerificationToken),避免请求被拦截 - 若任务耗时极长,建议将任务移至后台服务(如
IHostedService),避免请求超时 - 客户端需保持SignalR连接活跃,若连接断开需重新建立
内容的提问来源于stack exchange,提问作者CsharpSqlGuy
相关产品推荐
相关产品推荐

