Azure SignalR Service は serverless モードにして Azure Functions と使うことができる。その場合には Azure Functions から REST API1 で Azure SignalR Service へ メッセージを発行する。 現時点(2019/12/29)の Azure SignalR Service では URL区切り文字を含んだユーザーIDが有効である。しかし、REST API から URL区切り文字を含んだユーザーIDをあて先としたメッセージ送信ができない。 URL区切り文字を含んだユーザーIDとは例えば、"user/1"のような文字列。
原因は、Azure SignalR Service が REST API のパラメーターとして送られた URLエンコードされたユーザーIDをデコードせずにあて先ユーザーIDとするから。
ユーザーIDが user/1 の場合、REST API へ URL {prefix}/user%2F1 としてリクエストするとメッセージがユーザーIDが user%2F1 のクライアントへメッセージが送信される。 URLエンコードをせずに REST API へ URL {prefix}/user/1 としてリクエストすると、URL が API のルートに一致せずに 404 Not Found レスポンスが返る。
なお、この REST API では リクエスト URL は ヘッダーで送信する JWT の aud claim と一致する必要がある。ユーザーIDを URLエンコードした場合であっても、aud claim はエンコード前の文字列と一致して有効な API リクエストとして扱われる。
Azure SignalR Service へは、REST API 以外に websocket で接続してメッセージ送信することができる。その場合にはユーザーID に / スラッシュなどの URL区切り文字が含まれていてもメッセージのあて先にできる。
接続したクライアントのユーザーID は Hub の接続イベントで確認できる。Azure SignalR Service では EventGrid を通して Hub への connected イベントが購読できる。
ためしたこと
Azure SignalR Service SDK のコードを変更して Azure SignalR Service へ接続して試してみた。Microsoft.Azure.SignalR.Management.ServiceTransportType.Transient を指定した ServiceManager が REST API を使用する。API への HTTP リクエスト URL を組み立てるコードを変更して、ユーザーID を URLエンコードして実行した。
public RestApiEndpoint GetSendToUserEndpoint(string userId, TimeSpan? lifetime = null) { var path_token = $"/users/{userId}"; // user/1 -> user%2F1 var path_url = $"/users/{Uri.EscapeDataString(userId)}"; var token = _restApiAccessTokenGenerator.Generate($"{_audiencePrefix}{path_token}", lifetime); return new RestApiEndpoint($"{_requestPrefix}{path_url}", token); }
- "user/1"だとする
- Connected イベントでは、"user/1"
- access_token.id では、 "{prefix}/user/1"
- rest api に
- url = {prefix}/user/1 , token.aud = "{prefix}/user/1"
- url = {prefix}/user/1 , token.aud = "{prefix}/user%2F1"
- url = {prefix}/user%2F1 , token.aud = "{prefix}/user%2F1"
- client "user%2F1" でメッセージを受信した
- url = {prefix}/user%2F1 , token.aud = "{prefix}/user/1"
- client "user%2F1" でメッセージを受信した