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

如何在ASP.NET Core Razor页面后端代码中调用前端JavaScript函数更新进度条及替代方案

How to Update Progress Bar from Backend in ASP.NET Core Razor Pages

First, let's clarify why your current approach won't work: ASP.NET Core Razor Pages uses HTTP, which is a stateless request-response protocol. When the OnGet method runs on the server, it generates the entire page and sends it to the client in one go. There's no way to invoke client-side JavaScript functions during the server-side execution because the connection closes once the response is sent.

Direct backend-to-frontend function calls aren't feasible here. Instead, we need a way to send progress updates from the server to the client while the long-running task executes. Below are two recommended solutions:


Solution 1: Use SignalR for Real-Time Updates

SignalR is an ASP.NET Core library that enables real-time bidirectional communication between server and client. This is ideal for progress tracking since the server can push updates to the client as soon as progress changes.

Step 1: Add SignalR to Your Project

Install the SignalR NuGet package via the CLI:

dotnet add package Microsoft.AspNetCore.SignalR

Step 2: Create a SignalR Hub

Add a new class (e.g., ProgressHub.cs) to your project:

using Microsoft.AspNetCore.SignalR;

public class ProgressHub : Hub
{
    // Send progress updates to connected clients
    public async Task SendProgressUpdate(int percent)
    {
        await Clients.Caller.SendAsync("ReceiveProgressUpdate", percent);
    }
}

Step 3: Configure SignalR in Program.cs

Update your startup code to register and map the SignalR hub:

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

// Configure pipeline
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapRazorPages();
app.MapHub<ProgressHub>("/progressHub"); // Map the hub endpoint

app.Run();

Step 4: Modify Backend to Send Progress via SignalR

Update Index.cshtml.cs to inject the hub context and send progress updates:

using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Mvc.RazorPages;

public class IndexModel : PageModel
{
    private readonly IHubContext<ProgressHub> _hubContext;

    public IndexModel(IHubContext<ProgressHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public async Task OnGet()
    {
        // Run the long task in the background (avoid async void!)
        _ = Task.Run(async () => await TestFunction());
    }

    public async Task TestFunction()
    {
        int loopCount = 1;
        while (loopCount <= 100)
        {
            // Send progress to all connected clients
            await _hubContext.Clients.All.SendAsync("ReceiveProgressUpdate", loopCount);
            
            loopCount++;
            await Task.Delay(50); // Simulate work (remove in production)
        }
    }
}

Step 5: Update Frontend to Listen for SignalR Updates

Modify Index.cshtml to connect to the hub and update the progress bar:

<style>
#Progress_Status { width: 50%; background-color: #ddd; }
#myprogressBar { width: 0%; height: 35px; background-color: #4CAF50; text-align: center; line-height: 32px; color: black; }
</style>
<div id="Progress_Status">
    <div id="myprogressBar">0%</div>
</div>

<!-- Include SignalR client library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/7.0.10/signalr.min.js"></script>
<script>
// Connect to the SignalR hub
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/progressHub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

// Listen for progress updates
connection.on("ReceiveProgressUpdate", function(percent) {
    const progressBar = document.getElementById("myprogressBar");
    progressBar.style.width = percent + '%';
    progressBar.textContent = percent + '%';
});

// Start the connection
async function start() {
    try {
        await connection.start();
        console.log("SignalR Connected.");
    } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
    }
}

// Reconnect if connection drops
connection.onclose(async () => {
    await start();
});

// Initialize connection on page load
start();
</script>

Solution 2: Use AJAX Polling (Simpler, Less Real-Time)

If you prefer to avoid adding SignalR, polling is a simpler alternative: the client periodically sends requests to the server to check current progress.

Step 1: Enable Session State

First, add session support in Program.cs:

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(5);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// Add after UseRouting and before UseAuthorization
app.UseSession();

Step 2: Modify Backend to Track Progress

Update Index.cshtml.cs to store progress in session and add a handler to retrieve it:

using Microsoft.AspNetCore.Mvc.RazorPages;

public class IndexModel : PageModel
{
    public async Task OnGet()
    {
        // Reset progress at task start
        HttpContext.Session.SetInt32("Progress", 0);
        
        // Start background task
        _ = Task.Run(async () => await TestFunction());
    }

    public async Task TestFunction()
    {
        int loopCount = 1;
        while (loopCount <= 100)
        {
            // Update session with current progress
            HttpContext.Session.SetInt32("Progress", loopCount);
            
            loopCount++;
            await Task.Delay(50); // Simulate work
        }
        // Mark task as complete
        HttpContext.Session.SetInt32("Progress", 100);
    }

    // Handler to return current progress as JSON
    public IActionResult OnGetGetProgress()
    {
        var progress = HttpContext.Session.GetInt32("Progress") ?? 0;
        return new JsonResult(progress);
    }
}

Step 3: Update Frontend to Poll for Progress

Modify Index.cshtml to periodically fetch progress and update the bar:

<style>
#Progress_Status { width: 50%; background-color: #ddd; }
#myprogressBar { width: 0%; height: 35px; background-color: #4CAF50; text-align: center; line-height: 32px; color: black; }
</style>
<div id="Progress_Status">
    <div id="myprogressBar">0%</div>
</div>

<script>
function updateProgress(percent) {
    const progressBar = document.getElementById("myprogressBar");
    progressBar.style.width = percent + '%';
    progressBar.textContent = percent + '%';
}

function pollProgress() {
    fetch('/Index?handler=GetProgress')
        .then(response => response.json())
        .then(percent => {
            updateProgress(percent);
            // Stop polling when task is complete
            if (percent < 100) {
                setTimeout(pollProgress, 100); // Poll every 100ms
            }
        })
        .catch(error => console.error('Error fetching progress:', error));
}

// Start polling on page load
window.onload = pollProgress;
</script>

Which Solution Should You Choose?

  • SignalR: Best for real-time updates, low latency, and scenarios where you need instant feedback. Ideal for long-running tasks with frequent progress changes.
  • Polling: Simpler to implement with no additional dependencies. Good for less frequent updates or when you want to avoid SignalR complexity.

Remember to handle edge cases like task cancellation, session expiration, and multi-user scenarios (session-based progress works per user, while SignalR can target specific clients if needed).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:44:09