【问题标题】:No client method with the name 'x' found with SignalR使用 SignalR 找不到名称为“x”的客户端方法
【发布时间】:2018-07-05 07:51:21
【问题描述】:

我有一个 ASP.NET Boilerplate v3.6.2 项目(.NET Core / Angular),我需要从后端方法调用客户端函数,所以我使用ASP.NET Core SignalR implementation

我按照官方文档,所以:

在后台

  1. 在我的模块中,我将依赖项添加到AbpAspNetCoreSignalRModule

    [DependsOn(typeof(AbpAspNetCoreSignalRModule))]
    public class MyModule : AbpModule
    

    并将this NuGet 包添加到模块的项目中。

  2. 然后我扩展了AbpCommonHub 类以利用SignalR Hub 的内置实现,添加一个用于发送消息的SendMessage() 方法:

    public interface IMySignalRHub :  ITransientDependency
    {
        Task SendMessage(string message);
    }
    
    public class MySignalRHub: AbpCommonHub, IMySignalRHub {
        protected IHubContext<MySignalRHub> _context;
        protected IOnlineClientManager onlineClientManager;
        protected IClientInfoProvider clientInfoProvider;
    
        public MySignalRHub(
            IHubContext<MySignalRHub> context, 
            IOnlineClientManager onlineClientManager,
            IClientInfoProvider clientInfoProvider)
        : base(onlineClientManager, clientInfoProvider) {
            AbpSession = NullAbpSession.Instance;
            _context = context;
        }
    
        public async Task SendMessage(string message) {
            await _context.Clients.All.SendAsync("getMessage", string.Format("User {0}: {1}", AbpSession.UserId, message));
        }
    }
    
  3. 我将“/signalr”网址的映射更改为MySignalRHub

    public class Startup {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
            [...]
            # if FEATURE_SIGNALR
                app.UseAppBuilder(ConfigureOwinServices);
            # elif FEATURE_SIGNALR_ASPNETCORE
                app.UseSignalR(routes => {
                    routes.MapHub<MySignalRHub>("/signalr");
                });
            # endif
            [...]
        }
    }
    
  4. 然后我使用集线器在服务实现中发送消息:

    public class MyAppService: AsyncCrudAppService<MyEntity, MyDto, int, PagedAndSortedResultRequestDto, MyCreateDto, MyDto>, IMyAppService {
    
        private readonly IMySignalRHub _hub;
    
        public MyAppService(IRepository<MyEntity> repository, IMySignalRHub hub) : base(repository) {
            _hub = hub;
        }
    
        public override Task<MyDto> Create(MyCreateDto input) {
            _hub.SendMessage("test string").Wait();
            [...]
        }
    }
    

在客户端

所有配置和包含都已从原始模板中到位。当我打开 Angular 应用程序时,我可以看到这个控制台日志:

DEBUG: Connected to SignalR server!
DEBUG: Registered to the SignalR server!

当我尝试调用将消息发送到客户端的后端服务时,我在控制台中收到以下警告:

Warning: No client method with the name 'getMessage' found.

我尝试了在official documentation 和互联网上找到的许多解决方案,但都没有奏效。我无法在客户端代码中定义“getMessage”处理程序。

我尝试过的一些无效示例:

实施 1:

// This point is reached
abp.event.on('getMessage', userNotification => {
    debugger; // Never reaches this point
});

实施 2:

// This point is reached
abp.event.on('abp.notifications.received', userNotification => {
    debugger; // Never reaches this point
});

实施 3:

// This is taken from the official documentation and triggers the error:
// ERROR TypeError: abp.signalr.startConnection is not a function
abp.signalr.startConnection('/signalr', function (connection) {
    connection.on('getMessage', function (message) {
        console.log('received message: ' + message);
    });
});

你有没有遇到过这种情况?你有 Angular 客户端中处理程序定义的简单工作示例吗?

更新

我尝试了这个替代解决方案,更改了SignalRAspNetCoreHelper 类(随基本模板一起提供的共享类)的实现:

export class SignalRAspNetCoreHelper {
    static initSignalR(): void {

        var encryptedAuthToken = new UtilsService().getCookieValue(AppConsts.authorization.encrptedAuthTokenName);

        abp.signalr = {
            autoConnect: true,
            connect: undefined,
            hubs: undefined,
            qs: AppConsts.authorization.encrptedAuthTokenName + "=" + encodeURIComponent(encryptedAuthToken),
            remoteServiceBaseUrl: AppConsts.remoteServiceBaseUrl,
            startConnection: undefined,
            url: '/signalr'
        };

        jQuery.getScript(AppConsts.appBaseUrl + '/assets/abp/abp.signalr-client.js', () => {
            // ADDED THIS
            abp.signalr.startConnection(abp.signalr.url, function (connection) {
                connection.on('getMessage', function (message) { // Register for incoming messages
                    console.log('received message: ' + message);
                });
            });
        });
    }
}

现在在控制台中我可以看到两条消息:

Warning: No client method with the name 'getMessage' found.
SignalRAspNetCoreHelper.ts:22 received message: User 2: asd

所以它正在工作,但不完全。某处“getMessage”处理程序不可见。 使用 ASP.NET Boilerplate 在 Angular 中实现消息处理程序的正确方法是什么?

【问题讨论】:

    标签: c# typescript asp.net-core aspnetboilerplate asp.net-core-signalr


    【解决方案1】:

    您应该使用Clients.Others.SendAsyncClient.AllExcept.SendAsync 而不是Clients.All.SendAsync

    Clients.All.SendAsync 专为客户端想要发送和接收消息的场景(如聊天室)而设计。因此,所有连接的客户端都应该实现connection.on('getMessage', 以便接收通知。如果他们不这样做,他们会在收到他们刚刚推送的通知时发出警告No client method with name 'x' found。

    在您的场景中,我了解您有一种客户端推送通知,另一种接收它们(例如 GPS 设备将位置发送到跟踪应用程序)。在这种情况下,使用Clients.Others.SendAsyncClient.AllExcept.SendAsync 将确保推送客户端不会被广播回他们(或他们的同类)刚刚推送的通知。

    【讨论】:

    • @john。 tks 的建议和格式。您能否向我指出提出答案的正确方法?指向纠正问题的 API 并解释为什么不合适?
    【解决方案2】:

    设置autoConnect: false开始你自己的连接:

    abp.signalr = {
        autoConnect: false,
        // ...
    };
    

    更好...

    不要扩展AbpCommonHub。你会发现实时通知停止工作,你需要替换IRealTimeNotifier,因为SignalRRealTimeNotifier使用AbpCommonHub

    您有 Angular 客户端中处理程序定义的简单工作示例吗?

    在 Angular 中使用 ASPNet Boilerplate 实现消息处理程序的正确方法是什么?

    按照文档创建一个单独的集线器。

    【讨论】:

      【解决方案3】:

      我在使用包"@aspnet/signalr": "1.1.4" 的Angular 应用程序中遇到了同样的错误。

      这个问题的原因是我加入频道后没有调用订阅方法。

      public async getWorkSheetById(worksheetId: string): Promise < Worksheet > {
          const worksheet: Worksheet = await this._worksheetService.getWorksheet(worksheetId);
          this.displayWorksheet(worksheet);
          await this._worksheetEventsService.joinWorksheetChannel(this._loadedWorksheet.id);
          return worksheet;
      }
      

      所以,为了解决这个问题,我在加入 await this.subscribeToSendMethod(this._loadedWorksheet) 后调用了 subscribe 方法

      public subscribeToSendMethod(loadedWorksheet: Worksheet): Worksheet {
          let newWorksheet: Worksheet;
          this._hubConnection.on('Send', (groupId: string, payloadType: string, payload: string, senderUserId: string)=> {
              newWorksheet=this._worksheetHandler.handlePayload(payloadType, payload, loadedWorksheet);
              this.displayWorksheet(newWorksheet);
          }
          );
          return newWorksheet;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-30
        相关资源
        最近更新 更多