mirror of https://github.com/abpframework/abp.git
30 changed files with 465 additions and 76 deletions
@ -0,0 +1,227 @@ |
|||
# SignalR Integration |
|||
|
|||
> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. |
|||
|
|||
## Installation |
|||
|
|||
### Server Side |
|||
|
|||
It is suggested to use the [ABP CLI](CLI.md) to install this package. |
|||
|
|||
#### Using the ABP CLI |
|||
|
|||
Open a command line window in the folder of your project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.AspNetCore.SignalR |
|||
``` |
|||
|
|||
> You typically want to add this package to the web or API layer of your application, depending on your architecture. |
|||
|
|||
#### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.BackgroundJobs.HangFire |
|||
``` |
|||
|
|||
Or use the Visual Studio NuGet package management UI to install it. |
|||
|
|||
2. Add the `AbpAspNetCoreSignalRModule` to the dependency list of your module: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
//...other dependencies |
|||
typeof(AbpAspNetCoreSignalRModule) //Add the new module dependency |
|||
)] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
> You don't need to use the `services.AddSignalR()` and the `app.UseEndpoints(...)`, it's done by the `AbpAspNetCoreSignalRModule`. |
|||
|
|||
### Client Side |
|||
|
|||
Client side installation depends on your UI framework / client type. |
|||
|
|||
#### ASP.NET Core MVC / Razor Pages UI |
|||
|
|||
Run the following command in the root folder of your web project: |
|||
|
|||
````bash |
|||
yarn add @abp/signalr |
|||
```` |
|||
|
|||
> This requires to [install yarn](https://yarnpkg.com/) if you haven't install before. |
|||
|
|||
This will add the `@abp/signalr` to the dependencies in the `package.json` of your project: |
|||
|
|||
````json |
|||
{ |
|||
... |
|||
"dependencies": { |
|||
... |
|||
"@abp/signalr": "~2.7.0" |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Run the `gulp` in the root folder of your web project: |
|||
|
|||
````bash |
|||
gulp |
|||
```` |
|||
|
|||
This will copy the SignalR JavaScript files into your project: |
|||
|
|||
 |
|||
|
|||
Finally, add the following code to your page/view to include the `signalr.js` file |
|||
|
|||
````xml |
|||
@section scripts { |
|||
<abp-script type="typeof(SignalRBrowserScriptContributor)" /> |
|||
} |
|||
```` |
|||
|
|||
It requires to add `@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR` to your page/view. |
|||
|
|||
> You could add the `signalr.js` file in a standard way. But using the `SignalRBrowserScriptContributor` has additional benefits. See the [Client Side Package Management](UI/AspNetCore/Client-Side-Package-Management.md) and [Bundling & Minification](UI/AspNetCore/Bundling-Minification.md) documents for details. |
|||
|
|||
That's all. you can use the [SignalR JavaScript API](https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client) in your page. |
|||
|
|||
#### Other UI Frameworks / Clients |
|||
|
|||
Please refer to [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) for other type of clients. |
|||
|
|||
## The ABP Framework Integration |
|||
|
|||
This section covers the additional benefits when you use the ABP Framework integration packages. |
|||
|
|||
### Hub Route & Mapping |
|||
|
|||
ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
public class MessagingHub : Hub |
|||
{ |
|||
//... |
|||
} |
|||
```` |
|||
|
|||
The hub route will be `/signalr-hubs/messasing` for the `MessasingHub`: |
|||
|
|||
* Adding a standard `/signalr-hubs/` prefix |
|||
* Continue with the **camel case** hub name, without the `Hub` suffix. |
|||
|
|||
If you want to specify the route, you can use the `HubRoute` attribute: |
|||
|
|||
````csharp |
|||
[HubRoute("/my-messasing-hub")] |
|||
public class MessagingHub : Hub |
|||
{ |
|||
//... |
|||
} |
|||
```` |
|||
|
|||
### AbpHub Base Classes |
|||
|
|||
Instead of the standard `Hub` and `Hub<T>` classes, you can inherit from the `AbpHub` or `AbpHub<T>` which hve useful base properties like `CurrentUser`. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
public class MessagingHub : AbpHub |
|||
{ |
|||
public async Task SendMessage(string targetUserName, string message) |
|||
{ |
|||
var currentUserName = CurrentUser.UserName; //Access to the current user info |
|||
var txt = L["MyText"]; //Localization |
|||
} |
|||
} |
|||
```` |
|||
|
|||
> While you could inject the same properties into your hub constructor, this way simplifies your hub class. |
|||
|
|||
### Manual Registration / Mapping |
|||
|
|||
ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) as a **transient service**. If you want to **disable auto dependency injection** registration for your hub class, just add a `DisableConventionalRegistration` attribute. You can still register your hub class to dependency injection in the `ConfigureServices` method of your module if you like: |
|||
|
|||
````csharp |
|||
context.Services.AddTransient<MessagingHub>(); |
|||
```` |
|||
|
|||
When **you or ABP** register the class to the dependency injection, it is automatically mapped to the endpoint route configuration just as described in the previous sections. You can use `DisableAutoHubMap` attribute if you want to manually map your hub class. |
|||
|
|||
For manual mapping, you have two options: |
|||
|
|||
1. Use the `AbpSignalROptions` to add your map configuration (in the `ConfigureServices` method of your [module](Module-Development-Basics.md)), so ABP still performs the endpoint mapping for your hub: |
|||
|
|||
````csharp |
|||
Configure<AbpSignalROptions>(options => |
|||
{ |
|||
options.Hubs.Add( |
|||
new HubConfig( |
|||
typeof(MessagingHub), //Hub type |
|||
"/my-messaging/route", //Hub route (URL) |
|||
hubOptions => |
|||
{ |
|||
//Additional options |
|||
hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); |
|||
} |
|||
) |
|||
); |
|||
}); |
|||
```` |
|||
|
|||
This is a good way to provide additional SignalR options. |
|||
|
|||
If you don't want to disable auto hub map, but still want to perform additional SignalR configuration, use the `options.Hubs.AddOrUpdate(...)` method: |
|||
|
|||
````csharp |
|||
Configure<AbpSignalROptions>(options => |
|||
{ |
|||
options.Hubs.AddOrUpdate( |
|||
typeof(MessagingHub), //Hub type |
|||
config => //Additional configuration |
|||
{ |
|||
config.RoutePattern = "/my-messaging-hub"; //override the default route |
|||
config.ConfigureActions.Add(hubOptions => |
|||
{ |
|||
//Additional options |
|||
hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); |
|||
}); |
|||
} |
|||
); |
|||
}); |
|||
```` |
|||
|
|||
This is the way you can modify the options of a hub class defined in a depended module (where you don't have the source code access). |
|||
|
|||
2. Change `app.UseConfiguredEndpoints` in the `OnApplicationInitialization` method of your [module](Module-Development-Basics.md) as shown below (added a lambda method as the parameter). |
|||
|
|||
````csharp |
|||
app.UseConfiguredEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapHub<MessagingHub>("/my-messaging-hub", options => |
|||
{ |
|||
options.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
### UserIdProvider |
|||
|
|||
ABP implements SignalR's `IUserIdProvider` interface to provide the current user id from the `ICurrentUser` service of the ABP framework (see [the current user service](CurrentUser.md)), so it will be integrated to the authentication system of your application. The implementing class is the `AbpSignalRUserIdProvider`, if you want to change/override it. |
|||
|
|||
## Example Application |
|||
|
|||
See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messages between (authenticated) users. |
|||
|
|||
 |
|||
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 45 KiB |
@ -1,14 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
public class AbpSignalROptions |
|||
{ |
|||
public List<HubConfig> Hubs { get; } |
|||
public HubConfigList Hubs { get; } |
|||
|
|||
public AbpSignalROptions() |
|||
{ |
|||
Hubs = new List<HubConfig>(); |
|||
Hubs = new HubConfigList(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
public class DisableAutoHubMapAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
public class HubConfigList : List<HubConfig> |
|||
{ |
|||
public void AddOrUpdate<THub>(Action<HubConfig> configAction = null) |
|||
{ |
|||
AddOrUpdate(typeof(THub)); |
|||
} |
|||
|
|||
public void AddOrUpdate(Type hubType, Action<HubConfig> configAction = null) |
|||
{ |
|||
var hubConfig = this.GetOrAdd( |
|||
c => c.HubType == hubType, |
|||
() => HubConfig.Create(hubType) |
|||
); |
|||
|
|||
configAction?.Invoke(hubConfig); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
$(function () { |
|||
var links = $("a.page-link"); |
|||
|
|||
$.each(links, function (key, value) { |
|||
var oldUrl = links[key].getAttribute("href"); |
|||
var value = Number(oldUrl.match(/currentPage=(\d+)&page/)[1]); |
|||
links[key].setAttribute("href", "/Components/Paginator?currentPage=" + value); |
|||
}) |
|||
}); |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
public abstract class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest<AbpAspNetCoreSignalRTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreSignalRModule), |
|||
typeof(AbpTestBaseModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpAspNetCoreSignalRTestModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Volo.Abp.AspNetCore.SignalR.SampleHubs; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR |
|||
{ |
|||
public class AbpSignalROptions_Tests : AbpAspNetCoreSignalRTestBase |
|||
{ |
|||
private readonly AbpSignalROptions _options; |
|||
|
|||
public AbpSignalROptions_Tests() |
|||
{ |
|||
_options = GetRequiredService<IOptions<AbpSignalROptions>>().Value; |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Auto_Add_Maps() |
|||
{ |
|||
_options.Hubs.ShouldContain(h => h.HubType == typeof(RegularHub)); |
|||
_options.Hubs.ShouldContain(h => h.HubType == typeof(RegularAbpHub)); |
|||
_options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableConventionalRegistrationHub)); |
|||
_options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableAutoHubMapHub)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Microsoft.AspNetCore.SignalR; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs |
|||
{ |
|||
[DisableAutoHubMap] |
|||
public class DisableAutoHubMapHub : Hub |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Microsoft.AspNetCore.SignalR; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs |
|||
{ |
|||
[DisableConventionalRegistration] |
|||
public class DisableConventionalRegistrationHub : Hub |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs |
|||
{ |
|||
public class RegularAbpHub : AbpHub |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Microsoft.AspNetCore.SignalR; |
|||
|
|||
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs |
|||
{ |
|||
public class RegularHub : Hub |
|||
{ |
|||
} |
|||
} |
|||
Loading…
Reference in new issue