Creating a SignalR hub

In this section, we are going to implement what is called a SignalR hub in our ASP.NET core backend. A hub is a class on the server where we can interact with clients. We can choose to interact with a single client, all connected clients, or just a subset of them.

Let's open our backend project in Visual Studio and carry out the following steps:

  1. In Solution Explorer, create a new folder called Hubs at the root level.
  2. In the Hubs folder, create a new class file called QuestionsHub.cs that contains the following content:
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;

namespace QandA.Hubs
{
public class QuestionsHub: Hub
{

}
}

Our class is called QuestionsHub and we inherit from the base Hub class in SignalR. The base Hub class gives us the features we need to interact with clients.

  1. Let's override a method in the base class that gets invoked when a client connects:
public class QuestionsHub: Hub
{
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
}
}

So, when a client connects to this hub, this OnConnectedAsync method will be called, which calls the base implementation of this method in the first statement. 

Notice the async keyword in front of the method name and the await keyword before the statement in the function. This denotes that the method is asynchronous. We'll cover asynchronous methods later in this book in Chapter 10Improving Performance and Scalability.

  1. Let's push a message to the client to inform it that a connection has been successfully made:
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
await Clients.Caller.SendAsync("Message",
"Successfully connected");

}

We use the Clients object from the base client to interact with the client that has just been connected using the Caller property. We use the SendAsync method in the Caller object to push some data to the client. The first parameter in SendAsync is the handler name in the JavaScript client we need to call, while the second parameter is the data to pass in as a parameter to that handler. So, we are invoking a handler called Message in our React client while passing a string parameter with the "Successfully connected" value.

In reality, we don't need to inform the client that a connection has been successfully made because SignalR does that for us already. We are purely using this as an example of pushing data from the server to the client.

  1. There is also an OnDisconnectedAsync method we can override that is invoked when a client disconnects. Let's implement this method by sending a message to the client:
public class QuestionsHub: Hub
{
public override async Task OnConnectedAsync()
{
...
}

public override async Task
OnDisconnectedAsync(Exception exception)

{
await Clients.Caller.SendAsync("Message",
"Successfully disconnected");

await base.OnDisconnectedAsync(exception);
}
}

So, a handler called Message with a parameter value of "Successfully disconnected" will be called in our React client when it disconnects from the SignalR API.

  1. Now, we are going to expose a method that the client can call to subscribe to updates for a particular question:
public class QuestionsHub: Hub
{
public override async Task OnConnectedAsync()
{
...
}

public override async Task
OnDisconnectedAsync(Exception exception)
{
...
}

public async Task SubscribeQuestion(int questionId)
{
// TODO - add the client to a group of clients interested in getting updates on the question
// TODO - send a message to the client to indicate that the subscription was successful
}
}

Our method is called SubscribeQuestion and has a parameter that contains the question ID of the question to subscribe to. We will use this exact name when invoking this function from our React frontend later in this chapter.

Notice that the method is declared as asynchronous with the async keyword. This is because the SignalR methods that we are going to invoke next are asynchronous.

  1. We are going to store all the subscribers to the question in a group. So, let's add the client to a group: 
public async Task SubscribeQuestion(int questionId)
{
await Groups.AddToGroupAsync(Context.ConnectionId,
$"Question-{questionId}");

// TODO - send a message to the client to indicate that the subscription was successful
}

So, SignalR has a groups feature that we use to store all the subscribers to the question in. These groups can be accessed via a Groups property in the base Hub class. We use the AddToGroupAsync method in the Groups property to add the client to the group while passing in the client connection ID, which we can get from the Context property on the Hub base class. The second parameter that's passed to the AddToGroupAsync method is the name of the group, which we set to the word "Question", followed by a hyphen and then the question ID. If the group doesn't exist, SignalR will automatically create the group, which will be the case for the first client that subscribes to a question.

  1. Let's finish the SubscribeQuestion method's implementation by sending a message to the client to indicate that the subscription was successful:
public async Task SubscribeQuestion(int questionId)
{
await Groups.AddToGroupAsync(Context.ConnectionId,
$"Question-{questionId}");
await Clients.Caller.SendAsync("Message",
"Successfully subscribed");

}

  1. The last method we are going to implement in the hub is a method to unsubscribe from getting updates about a question:
public class QuestionsHub: Hub
{
public override async Task OnConnectedAsync()
{
...
}

public override async Task
OnDisconnectedAsync(Exception exception)
{
...
}

public async Task SubscribeQuestion(int questionId)
{
...
}

public async Task UnsubscribeQuestion(int questionId)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId,
$"Question-{questionId}");

await Clients.Caller.SendAsync("Message",
"Successfully unsubscribed");

}
}

This implementation is very similar to the SubscribeQuestion method, except that we call the RemoveFromGroupAsync method on the Groups property in the base Hub class to remove the client from the group. When all the clients have been removed from the group, SignalR will automatically remove the group.

That completes the implementation of our SignalR hub. Before finishing this section, let's take some time to explore the different properties that we can use in the Clients property in the base Hub class. In any method, we can type the property name, that is,  Clients, followed by a dot, to see all the methods that are available: 

The following are descriptions of some useful methods:

  • AllExcept: This allows us to interact with clients, except for a list of clients we supply by their connection ID.
  • Client: This allows us to interact with a specific client by passing their connection ID.
  • Clients: This allows us to interact with a list of clients by passing a list of their connection IDs.
  • Group: This allows us to interact with a group of clients by passing the group name.
  • Groups: This allows us to interact with a list of groups by passing a list of group names.

Don't forget to remove the Clients property we just explored before moving on.

We've made a great start on our real-time API, but we aren't pushing new answers to subscribed clients yet. We'll implement this in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.71.71