SignalR Hubs Api Explanation (C Server End)

Keywords: ASP.NET Javascript JSON

How to Register SignalR Middleware

To enable clients to connect to Hub, you need to call the MapSignalR method when the program starts.

The following code shows how to define SignalR Hubs routing in the OWIN startup class.

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MyApplication.Startup))]
namespace MyApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }

    }
}

 

The /signalr URL

By default, clients connect to your Hub through the'/ signalr'routing address. You can also modify the'/ signalr' without using the default'.

Server-side code specifies Url

app.MapSignalR("/signalrTest", new HubConfiguration());

JavaScript client code specifies Url

<script src="signalrTest/hubs"></script>

var chat = $.connection.chatHub;
chat.url = "/signalrTest";

NET client code specifies Url

var Connection = new HubConnection("http://localhost:8080/signalrTest");

 

How to create and use Hub classes

To create Hub classes, you need to create a class that inherits from Microsoft.Aspnet.Signalr.Hub.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

 

Hub name on JavaScript side

Server (hump nomenclature, the first letter can be capitalized or lowercase)

public class ContosoChatHub : Hub

JavaScript (whether the first letter on the Server side is small or small, the JavaScript client must be small, or the corresponding Hub will not be found)

var contosoChatHubProxy = $.connection.contosoChatHub;

When the Hub name is named using the feature HubName, the case of the Hub name on the Server and Client sides must be consistent.

Server (If the first letter specified by HubName is capitalized, the JavaScript side must also be capitalized. If it's lowercase, then the JavaScript side must also be lowercase.

[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub

JavaScript 

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

 

Strong type Hub

Define an interface T so that your Hub class inherits from Hub < T> so that you can specify the methods that your client can call or open code prompts in your Hub method.

public class StrongHub : Hub<IClient>
{
    public void Send(string message)
    {
        Clients.All.NewMessage(message);
    }
}

public interface IClient
{
    void NewMessage(string message);
}

 

How to define methods that client can call in Hub class

If you need to expose a method that can be invoked on the client side, you need to define a public method in Hub, as shown below.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}
public class StockTickerHub : Hub
{
    public IEnumerable<Stock> GetAllStocks()
    {
        return _stockTicker.GetAllStocks();
    }
}

You can specify method parameters and return types, including complex types and arrays. These data are transmitted using Json on both client and server sides, and SignalR automatically binds complex type objects and array objects.

 

Method name in Hub (non-client method name)

Server (hump nomenclature, the first letter can be capitalized or lowercase)

public void NewContosoChatMessage(string userName, string message)

JavaScript (regardless of whether the first letter on the Server side is small or small, the JavaScript client must be small or no corresponding method can be found)

contosoChatHubProxy.server.newContosoChatMessage(userName, message);

Using the HubMethodName feature to specify method names, the case of the method names on the Server and Client sides must be consistent.

Server terminal

[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)

JavaScript side

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

 

How to Call Client Method in Hub Class

You can use the Clients property in Hub class methods to invoke client-side methods

Server terminal

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

JavaScript side

contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name)
        + '</strong>: ' + htmlEncode(message) + '<li>');
};

You can't get a return value from the client, such as int x = Clients.All.add(1,1) is useless.

You can specify complex types and array types for parameters, as shown below.

Sever terminal

public void SendMessage(string name, string message)
{
    Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}

public class ContosoChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}

JavaScript code

var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
    console.log(message.UserName + ' ' + message.Message);
});

 

Specify which clients receive information

All connected clients

Clients.All.addContosoChatMessageToPage(name, message);

Just Calling's client

Clients.Caller.addContosoChatMessageToPage(name, message);

All connected clients except Calling clients

Clients.Others.addContosoChatMessageToPage(name, message);

Specify a specific client by connection ID

Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);

Exclude specific clients through connection ID

Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);

Specify a special group

Clients.Group(groupName).addContosoChatMessageToPage(name, message);

Specify a special group and exclude clients with a specific connection ID

Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);

Specify a special group, but exclude call

Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);

Specify a particular user through userId, typically IPrincipal.Identity.Name

Clients.User(userid).addContosoChatMessageToPage(name, message);

All clients and groups in a list of connection IDs

Clients.Clients(ConnectionIds).broadcastMessage(name, message);

Specify multiple groups

Clients.Groups(GroupIds).broadcastMessage(name, message);

By username

Clients.Client(username).broadcastMessage(name, message);

A set of usernames

Clients.Users(new string[] { "myUser", "myUser2" }).broadcastMessage(name, message);

 

Case insensitive

Client method name calls are case insensitive, such as Clients.All.addContosoChatMessageToPage calls client's AddContosoChatMessageToPage, addcontosochatmessagetopage, or addContosoChatMessageToPage.

 

Asynchronous execution

public async Task NewContosoChatMessage(string name, string message)
{
    await Clients.Others.addContosoChatMessageToPage(data);
    Clients.Caller.notifyMessageSent();
}

 

How to use a string variable as a method name

If you need to use a string as the method name, then you need to assign the Clients.All (or Clients.Others, Clients.Caller, etc.) object to IClientProxy and call its Invoke(methodName, args...) method.

public void NewContosoChatMessage(string name, string message)
{
    string methodToCall = "addContosoChatMessageToPage";
    IClientProxy proxy = Clients.All;
    proxy.Invoke(methodToCall, name, message);
}

 

Management team membership

Server terminal

public class ContosoChatHub : Hub
{
    public Task JoinGroup(string groupName)
    {
        return Groups.Add(Context.ConnectionId, groupName);
    }

    public Task LeaveGroup(string groupName)
    {
        return Groups.Remove(Context.ConnectionId, groupName);
    }
}

Client

contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);

Asynchronous execution

public async Task JoinGroup(string groupName)
{
    await Groups.Add(Context.ConnectionId, groupName);
    Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}

 

How to capture and process connection lifecycle events in Hub classes

public class ContosoChatHub : Hub
{
    public override Task OnConnected()
    {
        // Add your own code here.
        // For example: in a chat application, record the association between
        // the current connection ID and user name, and mark the user as online.
        // After the code in this method completes, the client is informed that
        // the connection is established; for example, in a JavaScript client,
        // the start().done callback is executed.
        return base.OnConnected();
    }

    public override Task OnDisconnected()
    {
        // Add your own code here.
        // For example: in a chat application, mark the user as offline, 
        // delete the association between the current connection id and user name.
        return base.OnDisconnected();
    }

    public override Task OnReconnected()
    {
        // Add your own code here.
        // For example: in a chat application, you might have marked the
        // user as offline after a period of inactivity; in that case 
        // mark the user as online again.
        return base.OnReconnected();
    }
}

 

How to get Clients information from Content

connection ID of Calling Client

string connectionID = Context.ConnectionId;

Connection ID is a GUID assigned by SignalR (you can't specify it in your own code). Each connection has a connection ID. If your application contains multiple Hubs, multiple Hubs will share the same connection ID.

Http Header data

System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
System.Collections.Specialized.NameValueCollection headers = Context.Headers;

Query string data

System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
System.Collections.Specialized.NameValueCollection queryString = Context.QueryString;
string parameterValue = queryString["parametername"];

JavaScript client QueryString

$.connection.hub.qs = { "version" : "1.0" };

This $. connection.hub.qs does not refer to your current Hub ($. connection.chatHub.qs).

Cookies

System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.RequestCookies;

User information

System.Security.Principal.IPrincipal user = Context.User;

Request's HttpContext object

System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();

 

How to Pass State Between Client and Server

JavaScript Client

contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";

Server side access, you can use Caller or CallerState two ways

string userName = Clients.Caller.userName;
string computerName = Clients.Caller.computerName;
string userName = Clients.CallerState.userName;
string computerName = Clients.CallerState.computerName;

 

How to catch exceptions in Hub class

  • Use try - catch and log
  • Create a Houbs pipeline model that can handle OnIncoming Error
    • public class ErrorHandlingPipelineModule : HubPipelineModule
      {
          protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext)
          {
              Debug.WriteLine("=> Exception " + exceptionContext.Error.Message);
              if (exceptionContext.Error.InnerException != null)
              {
                  Debug.WriteLine("=> Inner Exception " + exceptionContext.Error.InnerException.Message);
              }
              base.OnIncomingError(exceptionContext, invokerContext);
      
          }
      }
      
      public void Configuration(IAppBuilder app)
      {
          // Any connection or hub wire up and configuration should go here
          GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); 
          app.MapSignalR();
      }
  • Use the HubException class to throw exceptions
    • public class MyHub : Hub
      {
          public void Send(string message)
          {
              if(message.Contains("<script>"))
              {
                  throw new HubException("This message will flow to the client", new { user = Context.User.Identity.Name, message = message });
              }
      
              Clients.All.send(message);
          }
      }

 

Enabling Log Diagnosis

If you need to start the Server end log, you need to add a system.diagnostics node to the web.config

<system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
     <source name="SignalR.ServiceBusMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
     </source>
     <source name="SignalR.ScaleoutMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.Transports.WebSocketTransport">
        <listeners>
          <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.ServerSentEventsTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.ForeverFrameTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.LongPollingTransport">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.TransportHeartBeat">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
    </switches>
    <sharedListeners>
      <add name="SignalR-Transports" 
           type="System.Diagnostics.TextWriterTraceListener" 
           initializeData="transports.log.txt" />
        <add name="SignalR-Bus"
           type="System.Diagnostics.TextWriterTraceListener"
           initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>

 

How to call client method and management group outside Hub class

If you need to call client methods and management groups outside the Hub class, you need to get a SignalR context object, and then you can manipulate client methods and groups through context.Clients objects.

IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
_context.Clients.All.updateStockPrice(stock);

 

How to Customize Pipeline Model

SignalR allows you to inject your code into the Hub pipeline model, which you can implement by inheriting the Hub Pipeline Module

public class LoggingPipelineModule : HubPipelineModule 
{ 
    protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) 
    { 
        Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); 
        return base.OnBeforeIncoming(context); 
    }   
    protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) 
    { 
        Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); 
        return base.OnBeforeOutgoing(context); 
    } 
}

Then inject a custom Houb pipeline model into Startup.cs

public void Configuration(IAppBuilder app) 
{ 
    GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); 
    app.MapSignalR();
}

 

Reference link:

https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server

Posted by antray on Tue, 18 Dec 2018 06:06:04 -0800