我正在尝试将SignalR用于Asp Net Core 2.1,以便从控制器方法发送消息,该方法从Angular中的测试按钮触发调用.
我期望的行为是当我单击按钮时,我的服务调用控制器方法,该方法发送测试消息.然后,我将简单地记录消息.
我期望的行为是当我单击按钮时,我的服务调用控制器方法,该方法发送测试消息.然后,我将简单地记录消息.
我想在服务中管理它,以避免所有组件中的代码重复.
我已经阅读了一些例子,如this question about using SignalR in a service(我已经使用了第二个解决方案)和this article和the official docs,但即使应用这些概念,它似乎也不起作用. (所以,或者我绝对是以错误的方式应用它们,或者仍然缺少某些东西,但我找不到…
客户端成功连接到消息中心,如果我单击该按钮,该方法将被点击但我没有收到任何消息,而是在Chrome控制台中收到此警告:
Warning: No client method with the name ‘SendAsync’ found.
发送消息很好,问题就在于接收它们……
问题是:我做错了什么?是后端还是Angular端的错误?
我与你分享我的所有代码(调用控制器方法的按钮和服务是不相关的,因为对服务的调用很顺利):
> Startup.cs
public void ConfigureServices(IServiceCollection services) { //... services.AddSignalR(); } //... public void Configure(IApplicationBuilder app,IHostingEnvironment env) { //... app.UseSignalR(routes => { //... routes.MapHub<MessageHub>("/messagehub"); //... }); }
> MessageHub.cs
public class MessageHub : Hub<ITypedHubClient> { public async Task SendMessage(string user,string message) { await Clients.All.SendAsync("ReceiveMessage",user,message); } } public interface ITypedHubClient { Task SendAsync(string title,string name,string message); }
> MessageController.cs
IHubContext<MessageHub,ITypedHubClient> _messageHubContext; public MessageController(IHubContext<MessageHub,ITypedHubClient> messageHubContext) { _messageHubContext = messageHubContext; } [HttpPost("Test")] public async Task<IActionResult> Test() { try { await _messageHubContext.Clients.All.SendAsync("ReceiveMessage","test","test"); return Ok(true); } catch (Exception e) { return BadRequest(e); } }
> communication.service.ts
@Injectable() export class CommunicationService { private _hubConnection: HubConnection | undefined; public async: any; message = ''; messages: string[] = []; private newmessage = new Subject<string>(); message$= this.newmessage.asObservable(); constructor() { this._hubConnection = new signalR.HubConnectionBuilder() .withUrl('/messagehub') //.configureLogging(signalR.LogLevel.Information) .configureLogging(signalR.LogLevel.Debug) .build(); this._hubConnection.start().catch(err => console.error(err.toString())); this._hubConnection.on('SendMessage',(user: any,message:any) => { const received = `Received: ${message}`; //this.messages.push(received); this.newmessage.next(received); console.log("got something new...",received); }); } clear() { this.newmessage.next(""); } public sendMessage(): void { const data = `Sent: ${this.message}`; if (this._hubConnection) { this._hubConnection.invoke('SendMessage','AAA',data); } this.messages.push(data); } }
解决方法
在signalr core 2.1中,您可以使用强类型集线器在接口中声明可以在客户端上调用哪些操作:
public class MessageHub : Hub<ITypedHubClient> { public async Task SendMessage(string title,string user,string message) { await Clients.All.SendMessageToClient(title,message); } } public interface ITypedHubClient { Task SendMessageToClient(string title,string message); }
在控制器中:
IHubContext<MessageHub,ITypedHubClient> _messageHubContext; public async Task<IActionResult> Test() { await _messageHubContext.Clients.All.SendMessageToClient("test","test"); return Ok("ok"); }
在客户端:
_hubConnection.on('SendMessageToClient',(title,message) => { const received = `title: ${title},name: ${user},message: ${message}`; console.log(received); });
如果你不使用强类型集线器,那么在客户端调用相同的方法它变成:
public class MessageHub : Hub { public async Task SendMessage(string title,string message) { await Clients.All.SendAsync("SendMessageToClient",title,message); } }
在这种情况下,您可以在客户端代理上使用SendAsync方法,它的第一个参数是您要调用的方法的名称.
更新:
当我们使用接口定义强类型集线器时,所有接口方法都必须返回一个Task.使用自定义方法,signalr生成调用SendCoreAsync的方法.这允许我们异步调用这些方法.
如果接口方法的返回类型不是Task,我们会收到错误:所有客户端代理方法都必须返回’System.Threading.Tasks.Task’