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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 15:30:17