I need some help knowing if they are doing something wrong with signalR. Let go:
My application is mvc. I followed the example of the tutorials and did so:
I created a RealTime class:
public class RealTime : Hub
{
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
In StartUp:
public void ConfigureServices(IServiceCollection services)
{
.
.
.
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
{
.
.
.
app.UseSignalR(routes =>
{
routes.MapHub<RealTime>("/realtime");
});
}
So, I went to my controller and injected IHubContext:
public class MyController : Controller
{
private readonly IHubContext<RealTime> _hub;
public MyController
(
IHubContext<RealTime> hub
)
{
_hub = hub;
}
And at some point I use:
await _hub.Clients.All.SendAsync("ReceiveMessage", "Hello Word!");
On my front end I did the following:
var connection = new signalR.HubConnectionBuilder().withUrl("/realtime").build();
connection.on("ReceiveMessage", function (message) {
Swal.fire(
'The Internet?',
message,
'question'
);
});
connection.start().then(function () {
//
}).catch(function (err) {
//
});
But nothing happens. There is no error in either the back en or the front end. But nothing happens!
Can anyone tell me see I did something wrong?
First thing I noticed was that you injected your hub in a Controller, try injecting it in the service/manager that gets called in that Controller.
The second thing was that you better get used with SendCoreAsync() instead of SendAsync when you aren't in the class that implements Hub.
The third thing i can suggest you to do is to put .configureLogging(signalR.LogLevel.Information) and then .build();
Also, put this to not get an error: import * as signalR from "#aspnet/signalr";
Also start the connection and display a message to know if the connection is established
connection.start().then(function () {
console.log("signalR connected")
}).catch(function (err) {
console.log(err)
});
And I would put the connection.on only after starting the connection
connection.on("ReceiveMessage", (message) =>{
Swal.fire(
'The Internet?',
message,
'question'
);
});
Hope I helped you !
thanks for the help. I found the solution, although ugly it works.
I put before opening the connection the following snippet:
Object.defineProperty(WebSocket, 'OPEN', { value: 1, });
So my js was like this:
var connection = new signalR.HubConnectionBuilder()
.withUrl("/realtime")
.configureLogging(signalR.LogLevel.Debug)
.build();
Object.defineProperty(WebSocket, 'OPEN', { value: 1, });
connection.start().then(function () {
console.log("signalR connected")
}).catch(function (err) {
return console.error(err)
});
connection.on("ReceiveMessage", function (message) {
Swal.fire(
'The Internet?',
message,
'question'
);
});
Related
I read a lot here on stackoverflow before post this, and the only that help, but not really, was
SignalR call from controller
https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio
I thought that that error they say was my error, but didnt work. So When the client send a message, this should trigger the hub, but didnt... At least did show up
My controller is like...
//Constructor
private readonly IHubContext<ChatHub> chatHub;
public UserController(IHubContext<ChatHub> hubContext)
{
this.chatHub = hubContext;
}
//Method
[HttpPost]
public async Task<ActionResult> Message(Message message)
{
await chatHub.Clients.All.SendAsync("ReceiveMessage", message.emisor, message.Text);
}
Chat.js
"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
ChatHub class
public class ChatHub : Hub
{
public async Task SendMessage(string name, string message)
{
await Clients.All.SendAsync("ReceiveMessage", name, message);
}
}
So, this method on chathub, did not have any reference, If I debug, didnt call this method
You only call hub methods from the client. And you call client methods from the server.
IHubContext is on the server and all it does is send to clients. If you want to "call a hub method" from the server, then you need to refactor your code to have a common class that both the hub method calls and your controller calls.
I created a Xamarin app with a chat and a API to store it's data, every 3 minutes the mobile app makes a request to the api for the chat messages.
I decided to use SignalR as was suggested in the comments:
-Added it to my CongifureService as shown:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<HostelContext>(opt =>
opt.UseSqlServer(Configuration.GetConnectionString("HostelContext")));
services.AddCors();
services.AddControllers();
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
services.AddScoped<IUsersService, UsersService>();
services.AddScoped<IConversationsService, ConversationsService>();
services.AddScoped<IMessagesService, MessagesService>();
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HostelApi", Version = "v1" });
});
services.AddSignalR();
}
-Added Endpoint to Configure as shown:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chatHub");
});
-Added Class to my api project in new folder called Hub
-This is the Hub class:
//[Authorize]
public class ChatHub : Hub
{
public async Task SendMessage(Message message)
{
//await Clients.All.SendAsync("ReceiveMessage", message);
await Clients.All.SendAsync("ReceiveMessage" + message.ConversationId, message);
//await Clients.Users(destinationUserIdList).SendAsync("ReceiveMessage" + message.ConversationId, message);
}
}
In my Xamarin app:
-Added Hub Service:
-This is the Hub Service Class:
class HubService : IHubService
{
public HubConnection HubConnection { get; }
public HubService()
{
HubConnection = new HubConnectionBuilder()
.WithUrl($"https://10.0.2.2:5001/chatHub")
.Build();
}
public async Task Connect()
{
await HubConnection.StartAsync().ConfigureAwait(false);
}
public async Task Disconnect()
{
await HubConnection.StopAsync().ConfigureAwait(false);
}
public async Task SendMessage(Message message)
{
await HubConnection.InvokeAsync("SendMessage", message).ConfigureAwait(false);
}
}
-Connect to Hub when app starts after login:
public MainPage()
{
InitializeComponent();
MasterBehavior = MasterBehavior.Popover;
//should be home page
MenuPages.Add((int)MenuItemType.Home, (NavigationPage)Detail);
HubService.Connect().ConfigureAwait(true);
}
-Close connection on log out
-When sending a message i call SendMessage method:
await HubService.SendMessage(message).ConfigureAwait(true);
OutGoingText = string.Empty;
Messages.Add(message);
The Problem now is when i send a message and gets to:
public async Task SendMessage(Message message)
{
await HubConnection.InvokeAsync("SendMessage", message).ConfigureAwait(false);
}
throws this:
System.InvalidOperationException: 'The 'InvokeCoreAsync' method cannot be called if the connection is not active'
Already tried with HubService.Connect().Wait() but app stays in a loop
Can anyone help me with this?? #Nick Kovalsky...
Best Regards
I find out the problem!
The problem was calling "HubService.Connect().ConfigureAwait(true);" which is an async method inside a constructor, so it couldn't have the await.
public MainPage()
{
InitializeComponent();
MasterBehavior = MasterBehavior.Popover;
//should be home page
MenuPages.Add((int)MenuItemType.Home, (NavigationPage)Detail);
HubService.Connect().ConfigureAwait(true);
}
Solved be moving the call "HubService.Connect().ConfigureAwait(true);" to App.xaml.cs and put it inside "OnStart()" method:
protected override async void OnStart()
{
await HubService.Connect().ConfigureAwait(true);
}
Thank you all guys!
EDITED: see at the bottom
I'm new to SignalR and trying to implement with it a simple scenario with Angular7 client using this library, and ASP.NET Core web API. All what I need is to use SignalR to notify the client about the progress of some lengthy operations in methods of the API controllers.
After a number of attempts, I got to a point where apparently the connection is established, but then when the long task starts running and sending messages, my client does not seem to receive anything, nor any traffic appears in web sockets (Chrome F12 - Network - WS).
I post here the details, which might also be useful to other newcomers (full source code at https://1drv.ms/u/s!AsHCfliT740PkZh4cHY3r7I8f-VQiQ). Probably I'm just making some obvious error, yet in the docs and googling around I could not find a code fragment essentially different from mine. Could anyone give a hint?
My start point for the server side was https://msdn.microsoft.com/en-us/magazine/mt846469.aspx, plus the docs at https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-2.2. I tried to create a dummy experimental solution with that.
My code snippets in form of a recipe follow.
(A) Server Side
1.create a new ASP.NET core web API app. No authentication or Docker, just to keep it minimal.
2.add the NuGet package Microsoft.AspNetCore.SignalR.
3.at Startup.cs, ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// CORS
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
// https://github.com/aspnet/SignalR/issues/2110 for AllowCredentials
.AllowCredentials()
.WithOrigins("http://localhost:4200");
}));
// SignalR
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
and the corresponding Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// CORS
app.UseCors("CorsPolicy");
// SignalR: add to the API at route "/progress"
app.UseSignalR(routes =>
{
routes.MapHub<ProgressHub>("/progress");
});
app.UseHttpsRedirection();
app.UseMvc();
}
4.add a ProgressHub class, which just derives from Hub:
public class ProgressHub : Hub
{
}
5.add a TaskController with a method to start some lengthy operation:
[Route("api/task")]
[ApiController]
public class TaskController : ControllerBase
{
private readonly IHubContext<ProgressHub> _progressHubContext;
public TaskController(IHubContext<ProgressHub> progressHubContext)
{
_progressHubContext = progressHubContext;
}
[HttpGet("lengthy")]
public async Task<IActionResult> Lengthy([Bind(Prefix = "id")] string connectionId)
{
await _progressHubContext
.Clients
.Client(connectionId)
.SendAsync("taskStarted");
for (int i = 0; i < 100; i++)
{
Thread.Sleep(500);
Debug.WriteLine($"progress={i}");
await _progressHubContext
.Clients
.Client(connectionId)
.SendAsync("taskProgressChanged", i);
}
await _progressHubContext
.Clients
.Client(connectionId)
.SendAsync("taskEnded");
return Ok();
}
}
(B) Client Side
1.create a new Angular7 CLI app (without routing, just to keep it simple).
2.npm install #aspnet/signalr --save.
3.my app.component code:
import { Component, OnInit } from '#angular/core';
import { HubConnectionBuilder, HubConnection, LogLevel } from '#aspnet/signalr';
import { TaskService } from './services/task.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
private _connection: HubConnection;
public messages: string[];
constructor(private _taskService: TaskService) {
this.messages = [];
}
ngOnInit(): void {
// https://codingblast.com/asp-net-core-signalr-chat-angular/
this._connection = new HubConnectionBuilder()
.configureLogging(LogLevel.Debug)
.withUrl("http://localhost:44348/signalr/progress")
.build();
this._connection.on("taskStarted", data => {
console.log(data);
});
this._connection.on("taskProgressChanged", data => {
console.log(data);
this.messages.push(data);
});
this._connection.on("taskEnded", data => {
console.log(data);
});
this._connection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.error('Error while establishing connection: ' + err));
}
public startJob() {
this.messages = [];
this._taskService.startJob('zeus').subscribe(
() => {
console.log('Started');
},
error => {
console.error(error);
}
);
}
}
Its minimalist HTML template:
<h2>Test</h2>
<button type="button" (click)="startJob()">start</button>
<div>
<p *ngFor="let m of messages">{{m}}</p>
</div>
The task service in the above code is just a wrapper for a function which calls HttpClient's get<any>('https://localhost:44348/api/task/lengthy?id=' + id).
EDIT 1
After some more experimenting, I came with these changes:
use .withUrl('https://localhost:44348/progress') as suggested. It seems that now it no more triggers 404. Note the change: I replaced http with https.
do not make the API method async as it seems that the await are not required (i.e. set the return type to IActionResult and remove async and await).
With these changes, I can now see the expected log messages on the client side (Chrome F12). Looking at them, it seems that the connection gets bound to a generated ID k2Swgcy31gjumKtTWSlMLw:
Utils.js:214 [2019-02-28T20:11:48.978Z] Debug: Starting HubConnection.
Utils.js:214 [2019-02-28T20:11:48.987Z] Debug: Starting connection with transfer format 'Text'.
Utils.js:214 [2019-02-28T20:11:48.988Z] Debug: Sending negotiation request: https://localhost:44348/progress/negotiate.
core.js:16828 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
Utils.js:214 [2019-02-28T20:11:49.237Z] Debug: Selecting transport 'WebSockets'.
Utils.js:210 [2019-02-28T20:11:49.377Z] Information: WebSocket connected to wss://localhost:44348/progress?id=k2Swgcy31gjumKtTWSlMLw.
Utils.js:214 [2019-02-28T20:11:49.378Z] Debug: Sending handshake request.
Utils.js:210 [2019-02-28T20:11:49.380Z] Information: Using HubProtocol 'json'.
Utils.js:214 [2019-02-28T20:11:49.533Z] Debug: Server handshake complete.
app.component.ts:39 Connection started!
app.component.ts:47 Task service succeeded
So, it might be the case that I get no notification because my client ID does not match the ID assigned by SignalR (from the paper quoted above I had the impression that it was my duty to provide an ID, given that it is an argument of the API controller). Yet, I cannot see any available method or property in the connection prototype allowing me to retrieve this ID, so that I can pass it to the server when launching the lengthy job. Could this be the reason of my issue? If this is so, there should be a way of getting the ID (or setting it from the client side). What do you think?
It seems I've finally found it. The issue was probably caused by the wrong ID, so I started looking for a solution. A post (https://github.com/aspnet/SignalR/issues/2200) guided me to the usage of groups, which seems the recommended solution in these cases. So, I changed my hub so that it automatically assign the current connection ID to a "progress" group:
public sealed class ProgressHub : Hub
{
public const string GROUP_NAME = "progress";
public override Task OnConnectedAsync()
{
// https://github.com/aspnet/SignalR/issues/2200
// https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/working-with-groups
return Groups.AddToGroupAsync(Context.ConnectionId, "progress");
}
}
Now, my API controller method is:
[HttpGet("lengthy")]
public async Task<IActionResult> Lengthy()
{
await _progressHubContext
.Clients
.Group(ProgressHub.GROUP_NAME)
.SendAsync("taskStarted");
for (int i = 0; i < 100; i++)
{
Thread.Sleep(200);
Debug.WriteLine($"progress={i + 1}");
await _progressHubContext
.Clients
.Group(ProgressHub.GROUP_NAME)
.SendAsync("taskProgressChanged", i + 1);
}
await _progressHubContext
.Clients
.Group(ProgressHub.GROUP_NAME)
.SendAsync("taskEnded");
return Ok();
}
And of course I updated the client code accordingly, so that it does no more have to send an ID when invoking the API method.
Full demo repository available at https://github.com/Myrmex/signalr-notify-progress.
You set the route for the hub as /progress, but then you're attempting to connect to /signalr/progress, which is going to be a 404. If you open the developer console, you should have an connection error there telling you as much.
Just wanted to add that OP was on the right track with the connection ID.
I send it along optionally in the form data.
I'm just reporting progress while uploading to AWS and I handle with SignalR like this:
Controller
[HttpPost("MyPostRoute")]
public async Task<ActionResult> UploadFiles([FromForm] List<IFormFile> files, [FromForm] string? connectionId)
{
await _logic.UploadFiles(files, connectionId);
return Ok();
}
Logic
public async Task<bool> UploadFiles(List<IFormFile> files, string? connectionId)
{
foreach (var file in files)
{
Guid fileGuid = Guid.NewGuid();
string extension = Path.GetExtension(file.FileName);
using (var transferUtility = new TransferUtility(_awsClient))
{
var request = new TransferUtilityUploadRequest()
{
BucketName = _configuration["AwsBucket"],
Key = fileGuid + extension,
InputStream = file.OpenReadStream()
};
if (connectionId != null)
{
AwsFileProgress progress = new()
{
ConnectionId = connectionId,
FileName = file.FileName
};
request.UploadProgressEvent += (sender, e) => Request_UploadProgressEvent(sender, e, progress);
}
await transferUtility.UploadAsync(request);
}
}
return true;
}
private async void Request_UploadProgressEvent(object? sender, UploadProgressArgs e, AwsFileProgress progress)
{
progress.ProgressPercent = e.PercentDone;
await _hub.Clients.Client(progress.ConnectionId).SendAsync("AWSProgress", progress);
}
I made a model for the progress stuff:
public class AwsFileProgress
{
public string ConnectionId { get; set; }
public string FileName { get; set; }
public int ProgressPercent { get; set; } = 0;
}
And finally, the front-end:
import { Injectable } from '#angular/core';
import * as signalR from "#microsoft/signalr";
import { environment } from "#environment";
import { AwsFileProgress } from "#models/signalr/awsFileProgress";
#Injectable({
providedIn: 'root',
})
export class AwsSignalRService {
private hubConnection!: signalR.HubConnection
public data!: AwsFileProgress;
public startConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${environment.signalRhubRoot}/awsprogresshub`)
.build();
this.hubConnection
.start()
.then(() => { console.log('AWS SignalR Service Connection started') })
.catch(err => console.log('Error while starting AWS SignalR Service WebSocket: ' + err))
}
/////////////////////////////////////////////////////////
//This is all you have to do to get the connection ID:
public getConnectionId() : string | null {
return this.hubConnection.connectionId;
}
/////////////////////////////////////////////////////////
public addAwsProgressListener = () => {
this.hubConnection.on('AWSProgress', (awsFileProgress : AwsFileProgress) => {
this.data = awsFileProgress;
console.log(awsFileProgress.progressPercent);
});
}
}
Then the actual component it's injected into:
import { AwsSignalRService } from "#shared/services/aws-signalr-service";
#Component({
selector: 'settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent implements OnInit {
constructor(
private awsSignalRService: AwsSignalRService
) {
awsSignalRService.startConnection();
awsSignalRService.addAwsProgressListener();
}
^ Whatever is in our progress listener is handled here.
Making the request (and reporting client-side upload progress) is handled like this.
Note that I just use a basic (change) handler on a file input (<input type="file" (change)="fileUploadTest($event)"/>)
fileUploadTest(e: any) {
const formData = new FormData();
var cId = this.awsSignalRService.getConnectionId();
for(let file of e.target.files as File[]) {
formData.append('files', file, file.name);
};
if(cId != null)
formData.append('connectionId',cId);
const url: string = `${environment.ApiRoot}/MyPostRoute`;
this.http.post<any>(url,formData, {
reportProgress: true,
observe: 'events'
}).subscribe({
next: (response) => console.log(response),
error: (error) => console.log(error)
});
}
And the results in console of my API uploading data to AWS:
I'm a newb to SignalR. I'm trying to set up a Asp.Net Core WebAPI so that other clients can connect to it using SignalR and get real-time data.
My Hub class is:
public class TimeHub : Hub
{
public async Task UpdateTime(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
I have a relay class as follows:
public class TimeRelay : ITimeRelay
{
private readonly IHubContext<TimeHub> _timeHubContext;
public TimeRelay(IHubContext<TimeHub> context)
{
_timeHubContext = context;
Task.Factory.StartNew(async () =>
{
while (true)
{
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
});
}
}
Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseSignalR((x) =>
{
x.MapHub<TimeHub>("/timeHub");
});
app.UseMvc();
}
The client is a console application and the code is:
class Program
{
static Action<string> OnReceivedAction = OnReceived;
static void Main(string[] args)
{
Connect();
Console.ReadLine();
}
private static async void Connect()
{
var hubConnectionBuilder = new HubConnectionBuilder();
var hubConnection = hubConnectionBuilder.WithUrl("http://localhost:60211/timeHub").Build();
await hubConnection.StartAsync();
var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
Console.ReadLine();
on.Dispose();
await hubConnection.StopAsync();
}
static void OnReceived(string message)
{
System.Console.WriteLine($"{message}");
}
}
I tried debugging the application. The client got connected to the TimeHub succesfully. The no of connections in Clients.All changed from 0 to 1, when the client got connected. But, when await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString()); is executed, the UpdateTime function in TimeHub is not getting executed and the client is not getting any message.
I tried using "UpdateTime", "SendMessage", and "ReceiveMessage" as method in Clients.All.SendAsync in TimeRelay class. Nothing worked. Could someone point out my mistake in this.
For Clients, it will be null if there is no client connecting to server. For starting Asp.Net Core SignalR and Console App at the same time, the Clients may be null since Index may be called before Console App connects the signalR server.
Try steps below:
Change TimeHub
public class TimeHub: Hub
{
public async Task UpdateTime(string message)
{
if (Clients != null)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
}
Register TimeHub
services.AddSingleton<TimeHub>();
Controller
public class HomeController : Controller
{
private readonly TimeHub _timeHub;
public HomeController(TimeHub timeHub)
{
_timeHub = timeHub;
}
public IActionResult Index()
{
Task.Factory.StartNew(async () =>
{
while (true)
{
try
{
await _timeHub.UpdateTime(DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
catch (Exception ex)
{
}
}
});
return View();
}
I got it to work and thought I will answer it here. Thanks #TaoZhou for the tip.
My mistake was sending "UpdateTime" from server and waiting on "ReceiveMessage" at the client.
Ideally the code should look like the following:
SignalR Server:
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
SignalR Client:
var on = hubConnection.On("UpdateTime", OnReceivedAction);
In this case any message send from the server would be received at the client instantly.
Please refer the code provided in the question for more info.
I defined 2 separate hub in my project, Which is seem they parse into single proxy... and i'm not against it, let it be.
Next i defined my client, the time i had one client i had no issue with it,
but now that i have two hub, and both come to work at a single page, once the first hub start it work well, request goes to server and back.
but once i call the partial page, which connect and talk to second hub, it goes to connection.start, but it wont break on server, mean server is not notified of this action... . now can any one help me?
Controller1:
var app = angular.module("chatApplication", []);
var myHub = $.connection.chatHub;
app.controller("ChatController", [
"$scope", "$timeout", "$templateCache",
function ($scope, $timeout, $templateCache) {
...
$scope.RegisterClientMethod(myHub);
myHub.connection.start().done(function () {
//Already uses OnConnect Override
//TODO: Link Events To Required Buttons And Actions
//like: $(x).click(fnX);
//TODO: Call Started Events
//myHub.server.hello();
}).fail(function (e) {
$scope.connectionMessage = "Connection Failed" + e.toString();
$scope.$apply();
});
}
]);
Controller2:
var myUserHub = $.connection.userHub;
app.controller("UserController", [
"$scope", "$timeout", "$templateCache",
function($scope, $timeout, $templateCache) {
...
$scope.RegisterClientMethod(myUserHub);
$scope.RegisterWatchers();
myUserHub.server.getUsers();
myUserHub.connection.start().done(function () {
//Since Connection is already open, by chatHub, we cannot relay on that
myUserHub.server.getUsers();
}).fail(function (e) {
$scope.connectionMessage = "Connection Failed" + e.toString();
$scope.$apply();
});
}
]);
Hub1:
namespace SignalRChatSystem.Hubs
{
[ChatAuthorize]
public class ChatHub : Hub
{
...
public override Task OnConnected()
{
//client.doSomething
return base.OnConnected();
}
...
}
}
Hub2:
namespace SignalRChatSystem.Hubs
{
[UserAuthorize]
public class UserHub : Hub
{
...
public override Task OnConnected()
{
//client.doSomething
return base.OnConnected();
}
...
}
}
Mapping
[assembly: OwinStartupAttribute(typeof(SignalRChatSystem.Startup))]
namespace SignalRChatSystem
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
}
You should call $.connection.hub.start() only once, even if using multiple hubs. See: http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#establishconnection
If you are not sure if connection was started earlier on not, you could check before starting it, using the connectionState object.
$.signalR.connectionState
Object {connecting: 0, connected: 1, reconnecting: 2, disconnected: 4}
So your start method could be like this:
if ($.connection.hub && $.connection.hub.state === $.signalR.connectionState.disconnected) {
$.connection.hub.start();
//...
}
If your connection is already open, you can call directly what is in the .done() { body.
Maybe you can check before:
if ($.connection.hub && $.connection.hub.state === $.signalR.connectionState.connected) {
// ... logic for second hub here
}