Microsoft Graphを使用して、ユーザがグループに属しているかどうか調べる

うちの社内アプリの認証方法は今までバラバラで、メンテナンスとか非常に大変だったので、今後はAzure ADに統一していきたいなと思っている今日この頃。

とりあえず、Microsoft Graph APIを使用して、認証とアクセス権チェック(指定グループに属しているかどうかで判断する)を試してみた。

dotnet core 3.1コンソールアプリで実装。
まず、プロジェクトにMicrosoft.Graph,Microsoft.Graph.Auth,Microsoft.Identity.Clientパッケージを追加する。

Webアプリではないので、今回は前回と同様、ユーザ名・パスワード指定でTokenを取得する。(実際の取得はGraph API内部で行う)

IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(ApplicationID)
    .WithAuthority(Authority)
    .Build();
// Scopeはユーザ情報の読取とグループ情報の読取があればいいかな
string[] scopes = new string[] { "https://graph.microsoft.com/User.Read", "https://graph.microsoft.com/Group.Read.All" };

// ユーザ名・パスワードでアクセスするので、Microsoft.Graph.Auth.UserNamePasswordProviderを使用する
var provider = new UsernamePasswordProvider(app,scopes);

// Microsoft.GraphGraphServiceClientを作成
var graphClient = new GraphServiceClient(provider);

// ログインユーザの情報を取得する
var me = await graphClient.Me.Request()
    .WithUsernamePassword(username,secPwd)
    .GetAsync();

// ログインユーザが属している、グループを取得する
var members = await graphClient.Me.MemberOf.Request().GetAsync();
if (members != null) {
    foreach(var m in members) {
        if (m.GetType() == typeof(Group)) {
            Group g = (Group)m;
            Console.WriteLine($"{g.Id} : {g.DisplayName}");
        }
    }
}

MemberOfは自分が属しているグループの一覧なので、このグループが他のグループに属している場合、そのグループに属していることはこの一覧からは確認できない。

つまり、以下のような状態で、自分が最終的にAグループに属しているかどうかはこの一覧からは分からないということ。MemberOfで取得されるのは、BグループとCグループとなるからだ。

こういうグループの入れ子は配布リストなどには良く見られるよね。セキュリティグループはこういう設計をしない方が良いとは思うけど、場合によりけりだろうか・・・

で、自分がAグループに属しているかどうかを調べるには、グループを手繰っていく必要がある。効率はともかくとして、とりあえず以下のようなメソッドを作ってみた。

// グループを手繰って、指定グループに属しているかどうか調べる
// 最初に指定するグループが指定グループ
static async Task<bool> IsMember(Group g,string mail,GraphServiceClient graphClient,string username, SecureString secPwd) {
    foreach(var m in g.Members) {
        if (m.GetType() == typeof(User)) { // メンバーがユーザならメールアドレスをチェック
            User u = (User)m;
            if (u.Mail == mail) {
                return true;
            }
        } else if (m.GetType() == typeof(Group)) { // グループならグループ内をチェック
            Group sg = (Group)m;
            var grps = await graphClient.Groups.Request()
                .WithUsernamePassword(username,secPwd)
                .Expand("Members")	// これを入れないと、Membersが取れない
                .Filter($"id eq '{sg.Id}'")	// IDを指定
                .GetAsync();
            var grp = grps.FirstOrDefault();
            if (grp != null) {
				// 再帰呼出し
                return await IsMember(grp,mail,graphClient,username,secPwd);
            } else {
                return false;
            }
        }
    }
    return false;
}

グループ深度が深くなれば、何回もhttpリクエストが発生するので、効率的ではないとは思うけど・・・
まぁ、結果はOKでした。

カテゴリー: C#, Microsoft Graph, OAuth2, 技術系 | コメントする

MailKitとOAuth2を使ったOffice365へのPOP3アクセス

以前、Office365とPOP3プロトコルを使用した、自社用安否確認システムを作成したのだが、今年の10月以降、Office365にPOPアクセスするのに基本認証方式が使えなくなるとのアナウンスが去年辺りからあり、Office365のOAuth2による認証を試験していたのだが、どうもうまく行かなくて、結構悩んだのだが、単純にSCOPEの指定の仕方だったOrz

Office365用Azure AD管理ポータルで、アプリケーションを登録して、Microsoft GraphへPOP.AccessAsUser.Allアクセス許可を設定しているのだが、このプロパティを見ると、

“https://graph.microsoft.com/POP.AccessAsUser.All”

となっている。なので、SCOPEにも当然これを指定するのだろうと思って指定していたのだが、これが間違いの元。
アクセストークンは取得できるのだが、認証時に”Authentication failed.”のエラーが出て、POP3ログインに失敗する・・・

で、実際に指定するSCOPEは以下のようにしなければいけないのであった。

“https://outlook.office365.com/POP.AccessAsUser.All”

確かに、サンプルとか見ると、そうなっていました・・・
こんな事で結構ずーっと悩んでたんだよね・・・
で、自社用の安否確認システムはWindowsサービスで動作させているので、Office365のログインダイアログを出す事は出来ない。
そこで、あまりお薦めできないけれど、一番簡単な、ユーザ名とパスワードを使用した形で、アクセストークンを取得することにした。
以下のような形でアクセストークンの取得が可能。

// OAuth2アクセストークン取得
IPublicClientApplication app = PublicClientApplicationBuilder
    .Create(ApplicationID) // アプリケーション(クライアント) IDの事
    .WithAuthority(Authority) // https://login.microsoftonline.com/ + テナントID
    .Build();
string[] scopes = new string[] { "https://outlook.office365.com/POP.AccessAsUser.All" };
SecureString secPass = new SecureString();
・・・
// 指定するのはOffice365にログインに使用するユーザー名とパスワード
AuthenticationResult res = await app.AcquireTokenByUsernamePassword(scopes, Pop3User, secPass).ExecuteAsync();

↑のスコープをずっと間違えていたんだよね・・・でも、この時点ではエラー出ないんだよ。

で、後はMailKit.Net.Pop3Clientでの認証用のOAuth2の情報を作成して、認証を行えばPOP3で、Office365(Exchange Online)からメールを取得できる。

// OAuth2用の認証機構を作成
var oauth2 = new SaslMechanismOAuth2(Pop3User, res.AccessToken);

Pop3Client cli = new Pop3Client();
// POP3サーバー接続
await cli.ConnectAsync(Pop3Host, Pop3Port, Pop3Security);
// 認証
await cli.AuthenticateAsync(oauth2);
int count = await cli.GetMessageCountAsync();
if (count != 0)
{
	// POP3からメールの受信
    var msgs = await cli.GetMessagesAsync(0, count);
    foreach (var msg in msgs)
    {
		・・・

いや~、くだらないとこで引っかかってました・・・

追記:
このサンプルで、Office365のAOuth2アクセストークンを得るには、Micrsoft.Identity.Clientパッケージが必要。

カテゴリー: C#, OAuth2, 技術系 | コメントする

WSLをWSL2に更新してみた

Windows 10 2004がリリースされ、WSL2が利用可能になったので、WSLのUbuntu LinuxをWSL2に変更してみた。

まず、このページのリンクから、wsl_update_x64.msiをダウンロードして実行。

その後、コマンドプロンプト等で、wslコマンドを使用して、WSLのバージョンを変更する。
まず、下記コマンドを実行して現在のバージョンを見てみよう。

C:\>wsl -l -v
  NAME                  STATE           VERSION
* openSUSE-Leap-15-1    Running         1
  Ubuntu                Running         1

次に、バージョンを2に変更したいディストリビューションを指定して、下記コマンドを実行する。今回はUbuntuをバージョン2にしてみる。

C:\>wsl --set-version Ubuntu 2

実行が終わると、指定したディストリビューションはWSL2で動作するようになる。

WSL2に変換後、使用してみて、ちょっと困った点がいくつか。

まず、WSL2はVMとして動作するので、今までは、Windowsと同じIPで動作していたものが、別IPとなる点。(仮想アダプタが作成され、Windows側とWSL側に別IPが振られる)

これは、WSL上のプログラムとWindowsのプログラムでポートがバッティングしなくなるので悪い点ばかりではないが、例えば、WSL上でasp.dotnet coreのアプリをdotnet run等で動かしてWindows側からブラウザでテストしようとした場合、launchsettings.jsonのIPを直さないと、Windows側からアクセスできない。
(デフォルトではhttps://localhost:5001;http://localhost:5000が指定されているため。)

後、デフォルト設定では名前解決が出来ない場合がある。これは、/etc/resolv.confの内容がクライアント(Windows)の仮想IPアドレスに設定されているので、Windows側に名前解決機能がないと、ホスト名等が解決されず、dotnet new等も失敗する。
(nuget.orgが解決できないため)

そういう場合は、/etc/resolv.confの内容をクライアント(Windows)のDNSサーバ設定に合わせる必要がある。
私の場合は以下のようなアドレスが振られていた。

イーサネット アダプター vEthernet (WSL):

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::bdb1:e7a4:4e3:46c4%45
   IPv4 アドレス . . . . . . . . . . . .: 172.23.240.1
   サブネット マスク . . . . . . . . . .: 255.255.240.0
   デフォルト ゲートウェイ . . . . . . .:
eth0      Link encap:Ethernet  HWaddr 00:15:5d:5d:24:31
          inet addr:172.23.251.65  Bcast:172.23.255.255  Mask:255.255.240.0
          inet6 addr: fe80::215:5dff:fe5d:2431/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12294 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2691 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:15734461 (15.7 MB)  TX bytes:6991441 (6.9 MB)
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /e
tc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.23.240.1 # ←Windows側のIPアドレスが設定されている

速度に関してはVMになったことで、動作が遅くなるかと心配したが、普通に使う分には問題なさそうだ。

ちなみに、複数のディストリビューションをWSL2にしてみたところ、全て同じVMを共有しているようだ。

カテゴリー: Windows, 技術系 | 2件のコメント

Blazor WASMが正式リリースへ!

asp.net core blazor wasm版が正式リリースされた!!

とりあえず、以前から気になっていた、既存のSignalR Hubへの接続を試してみた。
Preview版では、接続できなかったので・・・

で、結論から言うと、接続できました!!

単純にServer Side版からrazorファイルと関連ファイルをコピーしただけで問題無く動作。

RC版では試していなかったので、リリース版からかどうかは不明だけど、とりあえず、既存のHUBに接続できるようになったのはうれしい限り。

正式リリースされたことにより、仕事にも使えるようになるね。

カテゴリー: asp.net core, Blazor, dotnetcore, SignalR, 技術系 | コメントする

EF CoreでSQL Serverのフルテキストインデックスを使う

Entity Framework CoreでSQL Serverのフルテキストインデックスを使用した検索を使ったアプリを作成してみた。

SQL Serverのフルテキストインデックスは、対象テーブルの行に対して、ユニークキーを指定しなければならない。例えば以下のようなテーブル構成にしておく必要がある。

検索結果として、このキーだけを取得するには、CONTAINSTABLE関数で、テーブルとカラム、検索文字列を指定すれば良い。上記構成で、対象テーブルのカラムデータ中から指定文字列を持つ行を取得するには、以下のようなSQLを発行する。

select * from BusinessHistoryDetail d where d.FullTextUniqKey
                in (select [KEY] from CONTAINSTABLE(BusinessHistoryDetail,*,'"C#*"'))

これにより、全てのカラムを対象に指定したキーワードを含む、レコードのユニークキーを取得する事が可能である。

EF Coreで生SQLを発行した結果をLINQクエリに返すにはFromSqlRawメソッドを使用する。上記の例で、キーワード検索結果をベースとして、氏名なども結果として取りたい場合は、以下のような感じのクエリを発行すればよい。

// 検索キーワードはstrkeywに入力されている
// フルテキストインデックス検索のキーワードは"<キーワード>*"の形で指定
string keyw = @"""" + strkeyw + "*"""; 
var q = from d in ctx.BusinessHistoryDetail.FromSqlRaw(
    @"select * from BusinessHistoryDetail d 
        where d.FullTextUniqKey
        in (select [KEY] from CONTAINSTABLE(BusinessHistoryDetail,*,{0}))",keyw)
    join h in ctx.BusinessHistoryHeader on d.EmployeeNo equals h.EmployeeNo
    select new {
        EmpNo = d.EmployeeNo,
        Name = h.Name,
        Project = d.ProjectName,
        Customer = d.CustomerName,
        OS = d.OS,
        Language = d.Language,
        Tools = d.Tools
    };

こんな感じでフルテキスト検索を行う事ができた。

なぜ、フルテキスト検索をEF Coreでやりたかったのかというと、以前作成した社内システムをdotnet coreに置き換えたいから。
以前のシステムでもフルテキスト検索を行っているが、その部分だけ、EFクエリではなく、SqlClientを使わなければならなかったため。
「EFだけで出来ればいいのになぁ」と思っていたので。

まぁ、EF使ってるのにSQLをプログラムに直書きするのは如何な物かとも思うけど・・・

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする

System.Text.Jsonネームスペース

.netでJSONを扱う場合、Newtonsoft.JSONを使用するのが一般的であったが、dotnet core 3から、System.Text.Jsonネームスペースのライブラリが使用できるようになったので、試してみた。まず、シリアライズ(POCOからJSONへの変換)を試してみる。
↓のようなクラスインスタンスをJSONへ変換してみよう。

class Person {
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
    public List<MailAddress> Mail {get; set;}
    [JsonIgnore] // シリアライズ時に無視する(JSONに吐き出さない)プロパティ
    public int Age {
        get => Period.Between(LocalDate.FromDateTime(Birthday),LocalDate.FromDateTime(DateTime.Now)).Years;
    }
    public override string ToString() {
        string retstr = $"Name={Name}, Birthday={Birthday.ToString("yyyy/MM/dd")}({Age})";
        foreach(var m in Mail) {
            retstr += $"\r\n\t{m}";
        }
        return retstr;
    }
}
class MailAddress {
    public string Address {get; set;}
    public string DisplayName { get; set; }
    public override string ToString() {
        return $"{DisplayName}<{Address}>";
    }
}

インスタンスに値を設定して・・・

List<Person> personel = new List<Person>() {
    new Person() {
            Name = "T.Sumomo",
            Birthday = DateTime.Parse("1964/02/03"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "t.sumomo@momo.com", DisplayName = "Taro Sumomo" },
                new MailAddress() { Address = "t-sumomo@gmail.com", DisplayName = "すももから生まれたすもも太郎" }
            }
    },
    new Person() {
            Name = "J.Sumomo",
            Birthday = DateTime.Parse("1976/12/13"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "j.sumomo@momo.com", DisplayName = "Jiro Sumomo" },
                new MailAddress() { Address = "j-sumomo@live.com", DisplayName = "ジロー・チェインジ" }
            }
    },
    new Person() {
            Name = "S.Sumomo",
            Birthday = DateTime.Parse("1989/06/25"),
            Mail = new List<MailAddress>() {
                new MailAddress() { Address = "s.sumomo@momo.com", DisplayName = "Sabro Sumomo" },
                new MailAddress() { Address = "s-sumomo@hotmail.com", DisplayName = "すももサブロ~" }
            }
    }
};

シリアライズをする。

Console.WriteLine("Serialize");
// 結果にインデントを付けよう
var opt = new JsonSerializerOptions() {
    WriteIndented = true
};
// シリアライズ
string jsonstr = JsonSerializer.Serialize(personel,opt);
Console.WriteLine($"JSON String={jsonstr}");

結果は

Serialize
JSON String=[
  {
    "Name": "T.Sumomo",
    "Birthday": "1964-02-03T00:00:00",
    "Mail": [
      {
        "Address": "t.sumomo@momo.com",
        "DisplayName": "Taro Sumomo"
      },
      {
        "Address": "t-sumomo@gmail.com",
        "DisplayName": "\u3059\u3082\u3082\u304B\u3089\u751F\u307E\u308C\u305F\u3059\u3082\u3082\u592A\u90CE"
      }
    ]
  },
・・・
]

と、こんな形にシリアライズされる。

この、シリアライズされたJSONをデシリアライズ(JSON⇒POCO)してみる。

Console.WriteLine("Deserialize");
// 先ほどシリアライズしたJSONをデシリアライズ
var person = JsonSerializer.Deserialize<List<Person>>(jsonstr);
foreach(var p in person) {
    Console.WriteLine(p);
}

結果は

Deserialize
Name=T.Sumomo, Birthday=1964/02/03(56)
        Taro Sumomo<t.sumomo@momo.com>
        すももから生まれたすもも太郎<t-sumomo@gmail.com>
Name=J.Sumomo, Birthday=1976/12/13(43)
        Jiro Sumomo<j.sumomo@momo.com>
        ジロー・チェインジ<j-sumomo@live.com>
Name=S.Sumomo, Birthday=1989/06/25(30)
        Sabro Sumomo<s.sumomo@momo.com>
        すももサブロ~<s-sumomo@hotmail.com>

このように、結構簡単にJSONを扱うことができる。
なお、シリアライズ/デシリアライズ以外にも、JsonDocumentを使用することで、DOMのようなアクセス方法も可能。

ちなみに、今回試したのは.NET 5 Preview 3。MSのドキュメントを見る限りでは.net core 3.0から使用できるはず。

後、下記usingを忘れずにね。

using System.Text.Json;
using System.Text.Json.Serialization;

細かいところまでは見ていないけど、簡単な使い方なら、Newtonsoft.JSONを置き換えることはできそうだね。

参考サイト JSON serialization and deserialization (marshalling and unmarshalling) in .NET – overview

カテゴリー: C#, dotnetcore, 技術系 | コメントする

blazor wasm 3.2.0 Preview 3-構成ファイル読み取り

blazor wasm 3.2.0 Preview 3では、アプリケーション構成ファイルを標準で読み込めるようになった。

構成ファイルはappsettings.jsonと言う名前で、wwwroot下に置く必要がある。内容的には下記のような形。

{
    "MyString": "Hello World!!",
    "MySection": {
        "String":"String",
        "Number":500
    }
}

値を取得するには、

@using Microsoft.Extensions.Configuration
@inject IConfiguration Config

で、構成ファイル取得用のライブラリをusingし、IConfigurationをインジェクトする。

実際に値を取得するには下記のように記述。

// 単体のキーから値を取得(MyStringの値を取得)
string myString = Config["MyString"];
// セクション中の値をキー指定で取得
string strValue = Config.GetSection("MySection")["String"];
int iValue = Convert.ToInt32(Config.GetSection("MySeciton")["Number"]);

通常のConfigurationExtensionと異なり、<T>GetValue(string)が無いのが残念だね。

カテゴリー: asp.net core, Blazor, C#, dotnetcore, 技術系 | コメントする

Entity Framework Core

Entity Framework Coreは.NET Entity Frameworkのdotnet core版。

必要パッケージ

  1. Microsoft.EntityFrameworkCore
  2. 各DBのEntityFrameworkプロバイダ
  3. Microsoft.EntityFrameworkCore.Design
  4. 各DBのEntityFrameworkデザイナ

1,2は必須で、2は実際にデータベースにアクセスするためのドライバ。例えば、MS SQL Serverなら、Microsoft.EntityFrameworkCore.SqlServer,MySQLなら、Pomelo.EntityFrameworkCore.MySql(本家Oracleのドライバは実行時エラーが出た-もう、直ったかな?)等。

3,4はC#コードからデータベーステーブルをジェネレートするのに必要。

なお、Entity Framework関連のMigration等を行う場合、最初に下記コマンドを実行して、dotnet-efツールをインストールしておく必要がある。

dotnet tool install --global dotnet-ef

EntityやDbContext等の定義は.NET Framework版とほぼ同様だが、DBにアクセスするためのコードが必要。
通常はDbContextから派生させたクラスの、OnConfiguringメソッドをオーバーライドして
接続先などを設定する。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.SqlServer;
・・・
    public class PersonDb : DbContext {
      ・・・
        protected override void OnConfiguring(DbContextOptionsBuilder builder) {
            builder
                .UseSqlServer(<接続文字列>);
        }
    }
}

上記は、MS SQL Serverに接続するための設定。
UseSqlServerの部分が拡張メソッドで、使用するプロバイダにより異なる。

DbContextとEntity定義のコードを作成して、下記コマンドを実行することにより、指定データベース上にテーブルが作成される。

dotnet ef migrations add <バージョン等分かりやすい名前>
dotnet ef database update

※データベースは作成しておく必要あり(?)

ちなみに、既存のテーブルから、DbContextとEntityコードを作成する事も可能。

dotnet ef dbcontext scaffold <接続文字列> <プロバイダ>
例)
dotnet ef dbcontext scaffold “Data Source=・・・” Microsoft.EntityFrameworkCore.SqlServer

上記によりDbContextを含んだソース,Entity毎のソースが作成される。

カテゴリー: C#, dotnetcore, Entity Framework, 技術系 | コメントする

.net coreでの設定ファイル読み込み

.net coreで設定ファイルを読み込むには通常、下記パッケージを使用する。

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.Json

上記はJSON形式で定義された設定ファイルを読み込むもの。
JSON形式の設定ファイルは下記のような感じで定義する。

{
    "ConnectionStrings": {
        "MyConnection" : "Data Source=..."
    },
    "MySettings": {
        "String" : "Hello",
        "NUmber" : 100
    }
}

設定値を参照するには、ConfigurationBuilderクラスを使用して、IConfiguraionRootのインスタンスを作成し、GetSectionメソッドおよびGetValueメソッドを用いて値を取得することが可能。
なお、接続文字列(ConnectionStrings)に関しては上記を用いずにGetConnecitonStringsを使用することが可能。

// 設定値の取得
var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
// MySettingsセクションの"String"の値を取得
string s = config.GetSection("MySettings").GetValue<string>("String");
// MySettingsセクションの"Number"の値を取得
int i = config.GetSection("MySettings").GetValue<int>("Number");
// 接続文字列"MyConnection"の値を取得
string strCon = config.GetConnectionString("MyConnection");

上記では、実行ファイルと同じ場所にappsettings.jsonというファイル名で定義ファイルが置いてあるという想定。フォルダが実行ファイルと異なる場所にあれば、AddJsonFileメソッドを呼び出す前に、    SetBasePathメソッドでディレクトリを指定する。

以上、忘れないようにメモ。

カテゴリー: C#, dotnetcore, 技術系 | コメントする

blazor wasm(hosted)とSignalR

以前、同じようなタイトルで、asp.net core blazor wasm版とSingalRの話を投稿した様な気がするが、blazor wasm版から既存のHubに接続できず、色々試したのだが、結局解決しなかった。

やはり、現状ではhostedプロジェクトとして作成し、同一ドメインでのみしかSignalRを利用できないようだ。

なので、とりあえず、hostedプロジェクトを試してみた。
まず、下記コマンドで、クライアントとサーバプロジェクトを作成する。

dotnet new blazorwasm --hosted -o <ソリューション名>

ソリューションフォルダ下に下記プロジェクトフォルダが作成される。

  • Client-blazor wasm プロジェクト
  • Server-asp.netプロジェクト
  • Shared-共通DLLプロジェクト

ClientプロジェクトにはSharedプロジェクトの参照が含まれ、ServerプロジェクトにはSharedプロジェクトとClientプロジェクトの参照が含まれる。つまり、共通クラスやInterface等はSharedプロジェクトに定義しておけば、Client/Server双方から参照出来るようになっている。

ServerプロジェクトにHUB本体を作成し、HubのEndpoint定義をして、ClientプロジェクトにSignalRクライアントライブラリを追加し、

dotnet add Client package Microsoft.AspNetCore.SignalR.Client

下記要領でHubに接続するコードを書くことで、ServerのHubに接続できるようになる。

    private HubConnection con;
          ・・・
            con = new HubConnectionBuilder()
                .WithUrl(NavigationManager.ToAbsoluteUri("/[ハブのエンドポイント]"))
                .Build();
           ・・・
            // 接続
            await con.StartAsync();

後はServerプロジェクトを起動して、ブラウザからアクセスするだけ。
もちろん、blazor server side版やWindows FormsなどからもこのHubに接続できる。

詳しくは、MSのサイト「Blazor WebAssembly で ASP.NET Core SignalR を使用する」をどうぞ。

正式リリース版では是非とも、既存サイトのHUBに接続できるようになって欲しいな。

カテゴリー: asp.net core, Blazor, C#, dotnetcore, SignalR, 技術系 | コメントする