如何在控制器 (SignalR) 上调用集线器?

How can I call my hub on my controller (SignalR)?

每次 post 编辑并添加到数据库时,我都希望它显示在我的 Index.cshtml 上。现在我正在做如下所示。但是这个选项不起作用:

 [HttpPost]
    public async Task<IActionResult> PostIgnicoes([FromBody] Ignicoes ignicao)
    {
        **var hub = new MyHub();**
        if (ignicao.Latitude == null)
       {
        return BadRequest(ModelState);
    }
    else
    {
        if (ignicao.Longitude == null)
        {
            return BadRequest(ModelState);
        }
        else
        {
            //if(ignicao.Estado == null)
            //{
            //    return BadRequest(ModelState);
            //}

        }
    }

    _context.Ignicoes.Add(ignicao);


    **await hub.PostMarker();**
    await _context.SaveChangesAsync();


    return CreatedAtAction("GetIgnicoes", new { id = ignicao.Id }, ignicao);
}

但每次我 post 它 return 错误 500,这意味着我不能 post 任何东西。为什么会这样?

在 SignalR 中,集线器基本上是客户端可以调用的“方法”的集合。所以这是客户端使用的界面,就像控制器用于 Web API.

如果您想在客户端调用方法,那么使用集线器不是正确的方法。相反,您想使用 IHubContext。您需要将其注入您的控制器。

public class HomeController : Controller
{
    private readonly IHubContext<MyHub> _hubContext;

    public HomeController(IHubContext<MyHub> hubContext)
    {
        _hubContext = hubContext;
    }

    [HttpPost]
    public async Task<IActionResult> PostIgnicoes([FromBody] Ignicoes ignicao)
    {
        //…

        await _hubContext.Clients.All.SendAsync("PostMarker");
        await _context.SaveChangesAsync();

        //…
    }
}

有关详细信息,请查看有关 sending messages from outside a hub. You can also this with use strongly typed hubs 的文档以避免必须通过将名称作为字符串传递来调用未类型化的方法。

这就是我理解 SignalR 的工作方式。可能还有更多内容,但构建类似于我在下面解释的内容使我能够在我正在构建的应用程序中构建实时通知。

枢纽

Hub 不需要定义任何方法来启用服务器发送的事件。它主要是您连接的其他 SignalR 组件的挂钩。那么,让我们创建一个几乎是空的 Hub,我将其称为 MapHub:

using Microsoft.AspNetCore.SignalR;

namespace YourApplicationNamespace
{
    public class MapHub : Hub
    {
    }
}

就是这样。看起来很奇怪,但让我们继续。

IHubContext<T>

这实际上是您用来向注册客户发送通知的方式。您将在您的 Startup(或配置 DI 容器的任何地方)中注册它。当您使用类似于以下内容时,这是为您完成的:

app.UseEndpoints(endpoints => {
    endpoints.MapHub<MapHub>("/notify"); 
});

"/notify" 是我要用于此集线器的端点。 SignalR 将使用它来注册客户端。

然后,更新您的控制器,并添加 IHubContext<MapHub> 作为依赖项:

public class MapsController : Controller
{
    private readonly IHubContext<MapHub> hubContext;

    public MapsController(IHubContext<MapHub> hubContext)
    {
        this.hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext));
    }

    // ... the rest of your code

}

完成后,您将修改您的操作。

通知客户端获取数据

而不是调用

**await hub.PostMarker();**

你会做这样的事情:

[HttpPost]
public async Task<IActionResult> PostIgnicoes([FromBody] Ignicoes ignicao)
{
    if (ignicao.Latitude == null)
    {
        return BadRequest(ModelState);
    }
    else
    {
        if (ignicao.Longitude == null)
        {
            return BadRequest(ModelState);
        }
    }

    _context.Ignicoes.Add(ignicao);

    await _context.SaveChangesAsync();

    // THIS IS THE NEW SIGNALR CODE
    await hubContext.Clients.All.SendAsync("getMarkers");

    return CreatedAtAction("GetIgnicoes", new { id = ignicao.Id }, ignicao);
}

请注意,您不直接使用集线器 - class 主要用于通过启动中的 .MapHub 扩展和 JavaScript 为您连接的位我们还没有讨论过的代码。

Clients.All.SendAsync("getMarkers") 告诉 SignalR 向所有注册客户端发送通知并触发事件 getMarkers(定义如下)。

客户端设置

我假设您已经在页面上下载并加载了 SignalR 客户端脚本。下面的代码是我设置连接的方式:

try {
    const connection = new signalR.HubConnectionBuilder()
        .withUrl('/notify')
        .withAutomaticReconnect()
        .configureLogging(signalR.LogLevel.Warning)
        .build();

    connection.on('getMarkers', function () {
        // ADD CODE HERE TO REQUEST NEW MARKERS
    });

    connection.start().then(function () {
        console.log("Hub connected");
    });
}
catch (err) {
    console.error(err);
}

这或多或少来自 Microsoft Docs 上的 SignalR 教程。关键部分是

    connection.on('getMarkers', function () {
        // ADD CODE HERE
    });

这就是您为客户端设置 "listening" 端点的方式,这是 hubContext.Clients.All.SendAsync("getMarkers") 调用的端点(在我的示例中)。您在 // ADD CODE HERE 做什么由您决定。我的实例使用 jQuery 的 $.load 函数重新加载我页面上的边栏。