質問するログイン新規登録
blazor

Blazorは、マイクロソフトが開発している.NETベースのWebアプリフレームワークです。C#でフロントエンドもバックエンドも一貫して書くことが可能。クライアントサイド(WebAssembly)とサーバーサイド形式のホスティングモデルがあります。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

106閲覧

Blazor Serverでchat実装したいが特定ユーザーへの送信ができない

RC46

総合スコア12

blazor

Blazorは、マイクロソフトが開発している.NETベースのWebアプリフレームワークです。C#でフロントエンドもバックエンドも一貫して書くことが可能。クライアントサイド(WebAssembly)とサーバーサイド形式のホスティングモデルがあります。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2025/10/27 02:52

0

0

実現したいこと

特定のユーザー向けにメッセージを送信したい

発生している問題・分からないこと

Clients.Allにて全員に送信できることはできている
受信もできているので基本的なコードは間違っていない

問題は await Clients.Users(targetIds).SendAsyncで特定のUserIDに送信したいがそこが上手くいかない

public override Task OnConnectedAsync()
{
Console.WriteLine($"SignalR接続: UserIdentifier = {Context.UserIdentifier}");
return base.OnConnectedAsync();
}

public class CustomUserIdProvider : IUserIdProvider
{
public string? GetUserId(HubConnectionContext connection)
{
var userId = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return string.IsNullOrWhiteSpace(userId) ? null : userId;
}
}
の部分でIDがNullになっている
このページを開いている段階で自身のIDが取れていない=SignalRも認識できない
とのことになるのかなと思っています

@rendermode InteractiveServerで実現はしたいが、BlazorServerだとそもそもHubは使わないなど
情報がどっちが正しいのか不明です

Blazor は初心者です 約3か月です
C#は独学ですが5年ほどしていますがAsp netなどWebシステムは初めて構築しています
基本はAzure系をベースにしています AzureApp / Azure SQL Database / Azure Storage
やりたいことはほぼ実現できるまで学習しましたが、特定ユーザー向けChatで引っかかっています

該当のソースコード

C#

1@page "/chat/{InquiryId:int}" 2 3@rendermode InteractiveServer 4 5@using Microsoft.AspNetCore.SignalR.Client 6@using System.Security.Claims 7@using Microsoft.AspNetCore.SignalR 8@using HpqWebApp.Hubs 9 10@inject IChatService ChatService 11@inject UserManager<ApplicationUser> UserManager 12@inject AuthenticationStateProvider AuthenticationStateProvider 13@inject NavigationManager NavigationManager 14@inject IHubContext<ChatHub> HubContext 15 16<PageTitle>Chat</PageTitle> 17 18<h3>問い合わせID: @InquiryId</h3> 19 20<ul> 21 @foreach (var m in Messages) 22 { 23 <li><b>@m.SentUserId:</b> @m.Text (@m.SentAt.ToShortTimeString())</li> 24 } 25</ul> 26 27<input @bind="SentName" placeholder="送信者" /> 28<input @bind="MessageText" @onkeydown="HandleKeyPress" placeholder="メッセージを入力" /> 29<button @onclick="SendMessage">送信</button> 30 31@code { 32 33 [Parameter] public int InquiryId { get; set; } 34 35 private HubConnection? hubConnection; 36 37 private string CurrentUser { get; set; } 38 private string UserRole = "A_User"; 39 private string HdwCode; 40 private string MessageText { get; set; } 41 private string SentName { get; set; } 42 43 private List<ChatMessageDetailDto> Messages = new(); 44 45 protected override async Task OnInitializedAsync() 46 { 47 var user = await AuthenticationStateProvider.GetCurrentUserAsync(UserManager); 48 if (user is null) 49 { 50 return; 51 } 52 53 //現在ユーザー取得 54 CurrentUser = user.Id; 55 56 //Hub設定 57 hubConnection = new HubConnectionBuilder() 58 .WithUrl(NavigationManager.ToAbsoluteUri("/chathub")) 59 .Build(); 60 61 //履歴取得 62 Messages = await ChatService.GetChatMessagesAsync(InquiryId); 63 64 //受信設定 65 hubConnection.On<string, string, int>("ReceiveMessage", (sentName, message, inquiryId) => 66 { 67 var msg = new ChatMessageDetailDto 68 { 69 InquiryId = inquiryId, 70 Text = message, 71 SentUserName = sentName 72 }; 73 74 Messages.Add(msg); 75 InvokeAsync(StateHasChanged); 76 }); 77 78 await hubConnection.StartAsync(); 79 80 } 81 82 /// <summary> 83 /// メッセ―ジ送信 84 /// </summary> 85 /// <returns></returns> 86 private async Task SendMessage() 87 { 88 if (string.IsNullOrWhiteSpace(MessageText)) return; 89 90 var msg = new ChatMessageDetailDto 91 { 92 InquiryId = InquiryId, 93 Text = MessageText, 94 SentUserName = SentName, 95 RoleName = UserRole, 96 SentUserId = CurrentUser 97 }; 98 99 //表示 100 Messages.Add(msg); 101 102 await ChatService.SaveMessageAsync(msg); 103 104 // SignalR で対象ユーザーに送信 105 if (hubConnection is not null) 106 { 107 await hubConnection.SendAsync("SendMessage", CurrentUser, SentName, MessageText, InquiryId); 108 } 109 110 MessageText = string.Empty; 111 112 } 113 114 private async Task HandleKeyPress(KeyboardEventArgs e) 115 { 116 if (e.Key == "Enter") 117 await SendMessage(); 118 } 119}

C#

1 public class ChatHub( 2 UserManager<ApplicationUser> userManager, 3 IChatService chatService) : Hub 4 { 5 private readonly UserManager<ApplicationUser> _userManager = userManager; 6 private readonly IChatService _chatService = chatService; 7 8 //接続時に UserIdentifier がどうなっているか確認 9 public override Task OnConnectedAsync() 10 { 11 Console.WriteLine($"SignalR接続: UserIdentifier = {Context.UserIdentifier}"); 12 return base.OnConnectedAsync(); 13 } 14 15 public async Task SendMessage(string senderId, string sentName, string messageText, int inquiryId) 16 { 17 //送信UserID 18 var sender = await _userManager.FindByIdAsync(senderId!); 19 if (sender == null) return; 20 21 //ロール取得 22 var roles = await _userManager.GetRolesAsync(sender); 23 var userRole = roles.FirstOrDefault() ?? "User"; 24 25 List<ApplicationUser> targetUsers; 26 27 if (userRole == "User") 28 { 29 // 一般ユーザー → 管理者 or SVR へ 30 var adminUsers = await _userManager.GetUsersInRoleAsync("Admin"); 31 var svrUsers = await _userManager.GetUsersInRoleAsync("SVR"); 32 33 // 例: BaseCode が一致する管理者/SVR のみに通知 34 var filteredAdmins = adminUsers.Where(u => u.BaseCode == sender.BaseCode); 35 var filteredSvrs = svrUsers.Where(u => u.BaseCode == sender.BaseCode); 36 37 targetUsers = filteredAdmins.Concat(filteredSvrs).ToList(); 38 } 39 else 40 { 41 // 管理者 or SVR → 一般ユーザー側へ 42 targetUsers = await _chatService.GetInquiryUsersAsync(inquiryId); 43 } 44 45 // 宛先の UserId リスト 46 var targetIds = targetUsers.Select(u => u.Id).Distinct().ToList(); 47 48 // 宛先ユーザーのみへ送信 49 await Clients.Users(targetIds).SendAsync("ReceiveMessage", sentName, messageText, inquiryId); 50 51 await Clients.All.SendAsync("ReceiveMessage", sentName, messageText, inquiryId); 52 } 53 }

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

ChatGPTやCopilotにも聞いたが上手くいかない
そもそもページを開く段階で
public override Task OnConnectedAsync()
public string? GetUserId(HubConnectionContext connection)
が2回呼ばれ、2回目はNullになってしまう

補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

自己解決しました
自己解釈ですが、Blazor ServerではClients.Groupでないと送信できない

ChatPageで自身のConnectionIdを登録

C#

1 hubConnection = new HubConnectionBuilder() 2 .WithUrl(NavigationManager.ToAbsoluteUri($"/chathub?user={User.Id}&inq={InquiryId}")) 3 .Build();

これでグループにログイン者のConnectionIdが登録される

登録したグループを下記で送信

C#

1 await hubConnection.SendAsync("SendMessageToInquiry", User.Id, SentName, InputMessageText, InquiryId);

リアルタイムで受信・更新できました
一先ず正規なやり方か分かりませんが、デバッグ環境、本番環境でもできました

Hubは下記です

C#

1public quiryId);class ChatHub() : Hub 2{ 3 public override async Task OnConnectedAsync() 4 { 5 var http = Context.GetHttpContext(); 6 var userId = http?.Request.Query["user"]; 7 var inquiryId = http?.Request.Query["inq"]; 8 9 // 問い合わせIDがある場合 → グループに登録 10 if (!string.IsNullOrEmpty(inquiryId)) 11 { 12 var connectid = Context.ConnectionId; 13 await Groups.AddToGroupAsync(Context.ConnectionId, $"INQ_{inquiryId}"); 14 Console.WriteLine($"User {userId} joined group INQ_{inquiryId} ConeId_{connectid}"); 15 } 16 17 await base.OnConnectedAsync(); 18 } 19 20 public async Task SendMessageToInquiry(string senderId, string sentName, string messageText, int inquiryId) 21 { 22 // 同じグループに登録した全員へ送信 23 await Clients.Group($"INQ_{inquiryId}").SendAsync("ReceiveMessage", sentName, messageText + "S", inquiryId); 24 } 25}

ChatPage.razor

C#

1 protected override async Task OnInitializedAsync() 2 { 3 var user = await AuthenticationStateProvider.GetCurrentUserAsync(UserManager); 4 if (user is null) 5 { 6 return; 7 } 8 9 User = user; 10 11 //問合IDを元にグループチャット 12 hubConnection = new HubConnectionBuilder() 13 .WithUrl(NavigationManager.ToAbsoluteUri($"/chathub?user={User.Id}&inq={InquiryId}")) 14 .Build(); 15 16 //履歴取得 17 ChatMessages = await ChatService.GetChatMessagesAsync(InquiryId); 18 19 // メッセージ受信イベント登録 20 hubConnection.On<string, string, int>("ReceiveMessage", (sentName, message, inquiryId) => 21 { 22 var msg = new ChatMessageDetailDto 23 { 24 InquiryId = inquiryId, 25 Text = message, 26 SentUserName = sentName 27 }; 28 29 ChatMessages.Add(msg); 30 InvokeAsync(StateHasChanged); 31 }); 32 33 await hubConnection.StartAsync(); 34 } 35 36 /// <summary> 37 /// メッセ―ジ送信 38 /// </summary> 39 /// <returns></returns> 40 private async Task SendMessage() 41 { 42 43 if (string.IsNullOrWhiteSpace(InputMessageText)) return; 44 45 var msg = new ChatMessageDetailDto 46 { 47 InquiryId = InquiryId, 48 Text = InputMessageText, 49 SentUserName = SentName, 50 RoleName = UserRole, 51 SentUserId = User.Id 52 }; 53 54 // DBに保存 55 await ChatService.SaveMessageAsync(msg); 56 57 // SignalRで送信(接続状態を確認) 58 if (hubConnection is not null) 59 { 60 if (hubConnection.State != HubConnectionState.Connected) 61 { 62 try 63 { 64 // 接続がまだならStartAsyncで接続 65 await hubConnection.StartAsync(); 66 } 67 catch 68 { 69 // 接続失敗時はログ出力だけにして続行 70 Console.WriteLine("SignalR接続失敗: メッセージ送信できませんでした"); 71 return; 72 } 73 } 74 75 // 接続済みなら送信 76 await hubConnection.SendAsync("SendMessageToInquiry", User.Id, SentName, InputMessageText, InquiryId); 77 } 78 79 // 入力クリア 80 InputMessageText = string.Empty; 81 82 }

投稿2025/10/28 01:07

RC46

総合スコア12

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問