Using SignalR in Volo.Abp Microservices

Suppose you need to send message notifications through SignalR and receive message notifications on the front end

Create a SignalR service

referenced in the project

abp add-package Volo.Abp.AspNetCore.SignalR

Add module dependencies to the Module file

[DependsOn(
    ...
    typeof(AbpAspNetCoreSignalRModule)
    )]
public class IdentityApplicationModule : AbpModule

Create interface INotificationHub

public interface INotificationHub
{
    // 发送消息
    Task ReceiveTextMessageAsync(SendNotificationDto input);
}

It is also possible not to create an interface, the AbpHub class defines generic and non-generic types.

Create the NotificationHub class and inherit AbpHub.
You can directly inherit Microsoft.AspNetCore.SignalR.Hub, but you can't use injected properties, such as CurrentUser

/// <summary>
/// SignalR消息Hub
/// </summary>
[HubRoute("signalr/Identity/notification")]
[Authorize]
[DisableAuditing]
public class NotificationHub : AbpHub<INotificationHub>
{

}

Send SignalR message

Inject IHubContext where it needs to be called, and initialize it

private readonly IHubContext<NotificationHub, INotificationHub> _hubContext;
public NotificationAppService(IHubContext<NotificationHub, INotificationHub> hubContext) 
{
    _hubContext = hubContext;
}

Use the following methods to send to specified users or all users

public async Task SendMessageToUsersAsync(List<string> userIds, SendNotificationDto sendNotificationDto)
{
    await _hubContext.Clients
        .Users(userIds.AsReadOnly().ToList())
        .ReceiveTextMessageAsync(sendNotificationDto);
}


public async Task SendMessageToAllAsync(SendNotificationDto sendNotificationDto)
{
    await _hubContext.Clients.All.ReceiveBroadCastMessageAsync(sendNotificationDto);
}

Configure Ocelet Gateway

Create a forwarding rule for the /signalr/identity/ route

When SignalR starts the connection, it first sends a negotiation protocol request, and the negotiation protocol returns availableTransports to tell the client which protocols are supported, as well as connetcionId and connectionToken, which will be used in subsequent connections.

insert image description here

Under the current routing configuration, the request address is: /signalr/identity/negotiate, and this http request will be forwarded to IdentityServer through the gateway.

Configure gateway forwarding rules in appsettings.json of the Gateway project, as follows:

"Routes": [
    {
      "DownstreamPathTemplate": "/signalr/identity/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44368
        }
      ],
      "UpstreamPathTemplate": "/signalr/identity/{everything}",
      "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
    },
    ...

In addition, the forwarding rules of the ws protocol must be configured. SignalR first tries to establish a WebSocket connection. WebSocket is the best transmission method for SignalR. The configuration is as follows:

  {
    "DownstreamPathTemplate": "/signalr/identity/{everything}",
    "DownstreamScheme": "ws",
    "Priority": 1,
    "DownstreamHostAndPorts": [
      {
        "Host": "localhost",
        "Port": 44368
      }
    ],
    "UpstreamPathTemplate": "/signalr/identity/{everything}",
    "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
  },

Try to use kestrel to run the gateway program. Before IIS7.0, websocket is not supported. If you use IIS, please make sure that the Websocket function is enabled.
Add UseWebSockets() before UseOcelot() so that the gateway can receive ws or wss protocol requests. If this gateway is not added, a 499 error code will be returned when forwarding.

app.UseWebSockets();
app.UseOcelot().Wait();

Create a SignalR client

Client installation depends on your UI framework/client type. If you use Asp.NetCore MVC or Razor, please refer to the abp official document
here to supplement the usage of other UI frameworks. Add a dependency on SignalR in the webpackage project

yarn add @microsoft/signalr

Create a hubConnection
and add the following code in main.js

const hubConnection: signalR.HubConnection = new signalR.HubConnectionBuilder()
  .withUrl(baseURL + requestUrl, {
    headers: header,
    accessTokenFactory: () => getAccessToken(),
    transport: signalR.HttpTransportType.WebSockets,
    logMessageContent: true,
    logger: signalR.LogLevel.Information,
  })
  .withAutomaticReconnect()
  .withHubProtocol(new signalR.JsonHubProtocol())
  .build();

The accessTokenFactory callback is used to obtain the access_token, which will be called every time a request is made to ensure that the latest access_token is obtained.

connection service

Call the hubConnection method where it needs to be used

hubConnection.start() //开始连接
hubConnection.stop()  //停止连接

subscribe news

hubConnection.on("ReceiveTextMessage", (newMsg) => {
  console.info("new msg recived!", newMsg)
});

Authentication

WebSockets does not support custom headers, so Authorization cannot be used, and the access_token parameter needs to be used to pass the token

client

Configure getAccessToken in the client as follows:

const getAccessToken: Function = (): string => {
  var token = UserModule.token !== undefined ? UserModule.token : ""; 
  return token;
}

UserModule.token is the token of the currently logged in user, which needs to be saved in the UserModule after successful login.

Server

On the server side, if IdentityServer has been used, you need to configure IdentityServerAuthentication in Startup, as follows:

context.Services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
        options.Authority = configuration["AuthServer:Authority"];
        options.ApiName = configuration["AuthServer:ApiName"];
        options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
        options.TokenRetriever = (request) =>
        {
            var path = request.Path;
            if (path.StartsWithSegments("/signalr"))
            {
                var accessToken = request.Query["access_token"].FirstOrDefault();

                if (!accessToken.IsNullOrWhiteSpace())
                {
                    return accessToken;
                }
            }
            return TokenRetrieval.FromAuthorizationHeader().Invoke(request);

        };                  
    });
    

If you use IISExpress to run the project, note that the url parameter of SignalR may be too long at this time and report 404.15 - Query String Too Long. The default limit of IIS is 2048, which needs to be configured in C:\Windows\System32\inetsrv\config\applicationHost.config The maxQueryString rules are as follows:

<configuration>
   <system.webServer>
      <security>
         <requestFiltering>
             <requestLimits maxQueryString="4096" />
         </requestFiltering>
      </security>
   </system.webServer>
</configuration>

Guess you like

Origin blog.csdn.net/jevonsflash/article/details/132079556