Skip to content

Commit

Permalink
Add blazor server demo project, fix an unhandled NRE when a hub tries…
Browse files Browse the repository at this point in the history
… to send to a null userID or groupID
  • Loading branch information
ascott18 committed Dec 6, 2022
1 parent eb6b438 commit be051b3
Show file tree
Hide file tree
Showing 31 changed files with 825 additions and 11 deletions.
13 changes: 10 additions & 3 deletions IntelliTect.AspNetCore.SignalR.SqlServer.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31424.327
# Visual Studio Version 17
VisualStudioVersion = 17.4.33103.184
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{62241700-24B6-4D13-A183-2502D628ED43}"
EndProject
Expand All @@ -13,7 +13,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoServer", "demo\DemoServ
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CA676E97-11D2-4D19-96DA-2B66392A05D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntelliTect.AspNetCore.SignalR.SqlServer.Tests", "test\IntelliTect.AspNetCore.SignalR.SqlServer.Tests\IntelliTect.AspNetCore.SignalR.SqlServer.Tests.csproj", "{468B17AA-5509-481D-884E-145978311BBA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntelliTect.AspNetCore.SignalR.SqlServer.Tests", "test\IntelliTect.AspNetCore.SignalR.SqlServer.Tests\IntelliTect.AspNetCore.SignalR.SqlServer.Tests.csproj", "{468B17AA-5509-481D-884E-145978311BBA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoBlazorServer", "demo\DemoBlazorServer\DemoBlazorServer.csproj", "{888FC3BC-2589-4A5F-8235-5737053065A6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -33,6 +35,10 @@ Global
{468B17AA-5509-481D-884E-145978311BBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{468B17AA-5509-481D-884E-145978311BBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{468B17AA-5509-481D-884E-145978311BBA}.Release|Any CPU.Build.0 = Release|Any CPU
{888FC3BC-2589-4A5F-8235-5737053065A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{888FC3BC-2589-4A5F-8235-5737053065A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{888FC3BC-2589-4A5F-8235-5737053065A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{888FC3BC-2589-4A5F-8235-5737053065A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -41,6 +47,7 @@ Global
{62E576F6-E0F9-43AC-848E-97E7E93E4EF0} = {21874FF3-BE97-4F3B-AD63-EC347FF757FF}
{FACCC87C-37E3-4095-9997-75EE3C54CD49} = {62241700-24B6-4D13-A183-2502D628ED43}
{468B17AA-5509-481D-884E-145978311BBA} = {CA676E97-11D2-4D19-96DA-2B66392A05D5}
{888FC3BC-2589-4A5F-8235-5737053065A6} = {62241700-24B6-4D13-A183-2502D628ED43}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {41DEFC27-E139-4A35-989F-DA872E4B9F0A}
Expand Down
12 changes: 12 additions & 0 deletions demo/DemoBlazorServer/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
84 changes: 84 additions & 0 deletions demo/DemoBlazorServer/ChatHub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;

public class UserIdProvider : IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
return "staticUserid";
}
}

public class ChatHubA : Hub
{
private readonly ILogger<ChatHubA> _logger;

public ChatHubA(ILogger<ChatHubA> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

private readonly string groupId = Guid.Empty.ToString();

public async Task SendMessage(string name, string message)
{
// _logger.LogInformation($"{nameof(SendMessage)} called. ConnectionId:{Context.ConnectionId}, Name:{name}, Message:{message}");
await Clients.Group(groupId).SendAsync("BroadcastMessage", name, message);
}

public override async Task OnConnectedAsync()
{
_logger.LogInformation($"{nameof(OnConnectedAsync)} called.");

await base.OnConnectedAsync();
await Groups.AddToGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
}

public override async Task OnDisconnectedAsync(Exception exception)
{
_logger.LogInformation(exception, $"{nameof(OnDisconnectedAsync)} called.");

await base.OnDisconnectedAsync(exception);
await Groups.RemoveFromGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
}
}

public class ChatHubB : Hub
{
private readonly ILogger<ChatHubB> _logger;

public ChatHubB(ILogger<ChatHubB> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

private readonly string groupId = Guid.Empty.ToString();

public async Task SendMessage(string name, string message)
{
// _logger.LogInformation($"{nameof(SendMessage)} called. ConnectionId:{Context.ConnectionId}, Name:{name}, Message:{message}");
await Clients.Group(groupId).SendAsync("BroadcastMessage", name, "group");
await Clients.Caller.SendAsync("BroadcastMessage", name, "caller");
await Clients.User(Context.UserIdentifier).SendAsync("BroadcastMessage", name, "user");
await Clients.All.SendAsync("BroadcastMessage", name, "all");
await Clients.AllExcept(Context.ConnectionId).SendAsync("BroadcastMessage", name, "allExcept");
}

public override async Task OnConnectedAsync()
{
_logger.LogInformation($"{nameof(OnConnectedAsync)} called.");

await base.OnConnectedAsync();
await Groups.AddToGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
}

public override async Task OnDisconnectedAsync(Exception exception)
{
_logger.LogInformation(exception, $"{nameof(OnDisconnectedAsync)} called.");

await base.OnDisconnectedAsync(exception);
await Groups.RemoveFromGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
}
}
13 changes: 13 additions & 0 deletions demo/DemoBlazorServer/Data/WeatherForecast.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace DemoBlazorServer.Data
{
public class WeatherForecast
{
public DateTime Date { get; set; }

public int TemperatureC { get; set; }

public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

public string? Summary { get; set; }
}
}
20 changes: 20 additions & 0 deletions demo/DemoBlazorServer/Data/WeatherForecastService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace DemoBlazorServer.Data
{
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray());
}
}
}
24 changes: 24 additions & 0 deletions demo/DemoBlazorServer/DemoBlazorServer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\IntelliTect.AspNetCore.SignalR.SqlServer\IntelliTect.AspNetCore.SignalR.SqlServer.csproj" />
</ItemGroup>

<ItemGroup>
<Content Update="wwwroot\css\site.css">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>

</Project>
77 changes: 77 additions & 0 deletions demo/DemoBlazorServer/Pages/ChatHub.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager Navigation
@implements IAsyncDisposable

<h1>Hub @Suffix</h1>

<div class="container">
<div class="row">&nbsp;</div>
<div class="row">
<div class="col-2">User</div>
<div class="col-4"><input type="text" @bind="userInput" /></div>
</div>
<div class="row">
<div class="col-2">Message</div>
<div class="col-4"><input type="text" @bind="messageInput" /></div>
</div>
<div class="row">&nbsp;</div>
<div class="row">
<div class="col-6">
<input type="button" @onclick="Send" disabled="@(!IsConnected)" value="Send Message" />
</div>
</div>
</div>

<hr>

<ul id="messagesList">
@foreach (var message in messages)
{
<li>@message</li>
}
</ul>

@code {
[Parameter]
public string Suffix { get; set; }

private HubConnection? hubConnection;
private List<string> messages = new List<string>();
private string? userInput;
private string? messageInput;

protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chatHub" + Suffix))
.Build();

hubConnection.On<string, string>("BroadcastMessage", (user, message) =>
{
var encodedMsg = $"{user}: {message}";
messages.Add(encodedMsg);
InvokeAsync(StateHasChanged);
});

await hubConnection.StartAsync();
}

private async Task Send()
{
if (hubConnection is not null)
{
await hubConnection.SendAsync("SendMessage", userInput, messageInput);
}
}

public bool IsConnected =>
hubConnection?.State == HubConnectionState.Connected;

public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
18 changes: 18 additions & 0 deletions demo/DemoBlazorServer/Pages/Counter.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0;

private void IncrementCount()
{
currentCount++;
}
}
42 changes: 42 additions & 0 deletions demo/DemoBlazorServer/Pages/Error.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@page
@model DemoBlazorServer.Pages.ErrorModel

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
</head>

<body>
<div class="main">
<div class="content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</div>
</div>
</body>

</html>
27 changes: 27 additions & 0 deletions demo/DemoBlazorServer/Pages/Error.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;

namespace DemoBlazorServer.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

private readonly ILogger<ErrorModel> _logger;

public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}

public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
Loading

0 comments on commit be051b3

Please sign in to comment.