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

Blazor项目中SignalR无法推送支付回调数据求助

SignalR推送不触发客户端方法排查与解决

问题背景

支付服务结果已成功提交到Blazor项目的PaymentController,但通过SignalR实时推送数据到客户端时未生效。服务器端执行await _hubContext.Clients.All.SendAsync("ReceivePaymentData", "test test test test");时断点无异常、控制台无报错,但客户端的ReceivePaymentData方法始终未被触发。


排查与解决步骤

1. 先确认客户端真的连上了SignalR

  • 查看浏览器控制台是否输出SignalR bağlantısı başarıyla başlatıldı.,没输出说明连接未建立。
  • 打开浏览器开发者工具的Network标签,筛选WS类型,检查/paymentHub的连接状态是否为Open。
  • 临时注释客户端的AccessTokenProvider配置,测试基础连接是否正常——Token无效/过期会直接导致连接失败。

2. 检查方法名称的大小写匹配

SignalR的方法名称大小写敏感,必须确保服务器发送的ReceivePaymentData和客户端注册的方法名完全一致(包括每个字母的大小写)。

3. 排查中间件与端点配置

  • 确认UseCors中间件顺序正确:必须放在UseRouting之后、UseAuthorization之前(当前代码顺序没问题)。
  • 临时注释MapReverseProxy()配置测试:部分反向代理未正确配置WebSocket转发规则,会阻断SignalR连接。
  • 确认endpoints.MapHub<PaymentHub>("/paymentHub");已正确添加到端点配置中(当前代码配置正确)。

4. 验证WebSocket传输配置

客户端指定了仅使用WebSocket传输,需确认:

  • 服务器环境支持WebSocket:比如IIS需启用WebSocket功能,Linux下的Nginx/Apache需配置WebSocket转发规则。
  • HTTPS环境下证书有效:WebSocket在HTTPS模式下依赖正确的SSL配置,证书无效会导致连接失败。

5. 完善客户端连接生命周期管理

当前客户端注册方法的时机正确,但需添加组件销毁时的连接清理逻辑,避免残留连接导致的异常:

public async ValueTask DisposeAsync()
{
    if (hubConnection != null)
    {
        await hubConnection.DisposeAsync();
    }
}

6. 启用SignalR调试日志定位问题

在Program.cs中添加日志配置,查看服务器端SignalR的详细运行日志,确认消息是否真的发送、客户端是否收到:

builder.Logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Debug);
builder.Logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Debug);

相关代码

客户端Razor页面代码

protected override async Task OnInitializedAsync()
{
    hubConnection = new HubConnectionBuilder()
        .WithUrl(NavigationManager.ToAbsoluteUri("/paymentHub"), options =>
        {
            options.AccessTokenProvider = async () => await SessionStorageHelper.GetItemAsync<string>(StorageKey.Token);
            options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets; // WebSocket kullanımı
            options.SkipNegotiation = true; // WebSocket kullanmak için
        })
    .WithAutomaticReconnect()  // Bağlantı koparsa yeniden bağlanmayı dener
    .Build();

    hubConnection.On<string>("ReceivePaymentData", async (result) =>
        {
            Console.WriteLine(result);
            // isPaymentReceived = true;
            // PaymentResult = result;
            // await NotifyUser();
            await InvokeAsync(StateHasChanged); // UI güncellenir
        });

    try
    {
        await hubConnection.StartAsync();
        Console.WriteLine("SignalR bağlantısı başarıyla başlatıldı.");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"SignalR bağlantısı başlatılamadı: {ex.Message}");
    }
}

服务器控制器代码

[Route("api/[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
    private readonly IHubContext<PaymentHub> _hubContext;
    private readonly IConfiguration _configuration;

    public PaymentController(IHubContext<PaymentHub> hubContext, IConfiguration configuration)
    {
        _hubContext = hubContext;
        _configuration = configuration;
    }

    [HttpPost("callback")]
    [Consumes("application/x-www-form-urlencoded")]
    public async Task<IActionResult> PaymentCallback([FromForm] OdemeSonucResult sonuc)
    {
        Console.WriteLine("Ödeme sonucu alındı, SignalR ile istemciye gönderiliyor...");

        await _hubContext.Clients.All.SendAsync("ReceivePaymentData", "test test test test");
        return Ok();
    }
}

Hub代码

public class PaymentHub : Hub
{
}

Program.cs代码

using Blazor.Wasm.Client.Helpers;
using Blazor.Wasm.Server.Helper;
using Blazor.Wasm.Server.Hubs;
using DevExpress.XtraCharts;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.IO.Compression;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHttpContextAccessor();

builder.Services.AddControllersWithViews();

builder.Services.AddResponseCompression(opts =>
{
    opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
        new[] { "application/octet-stream" });
});

builder.Services.AddRazorPages();

builder.Services.AddSignalR(options =>
{
    options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
    options.HandshakeTimeout = TimeSpan.FromSeconds(30);
    options.MaximumParallelInvocationsPerClient = 1;
    options.EnableDetailedErrors = true;
})
    .AddJsonProtocol(options => {
        options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    });


builder.Services.TryAddScoped<AppSettingsUpdater>();

builder.Services.AddCors(options =>
{
    options.AddPolicy("Open", builder =>
    {
        builder
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .WithExposedHeaders("Access-Control-Allow-Origin")
            .SetPreflightMaxAge(TimeSpan.FromMinutes(10));
    });
});

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

builder.Services.Configure<PaymentAppSettings>(
    builder.Configuration.GetSection("PD"));

var app = builder.Build();

app.UseResponseCompression();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebAssemblyDebugging();
}

app.MapReverseProxy();

app.UseHttpsRedirection();

app.UseBlazorFrameworkFiles();

app.UseStaticFiles();

app.UseRouting();

app.UseCors("Open");

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapControllers();
    endpoints.MapHub<PaymentHub>("/paymentHub");
    endpoints.MapFallbackToFile("index.html");
});

app.Run();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:04:56