blazor serverside apache httpdメモ

まぁ、当たり前の話だが、apache上でサーバのサブディレクトリとして、blazor server sideを動作させるには、それ用のサービスと、リバースプロキシ設定が必要。

また、WebSocketのためのリライトルールが必要となる。
以下のような感じ。

http://・・・/blazorappをhttp://localhost:5010/ にマッピングする。

<VirtualHost *:80>
        DocumentRoot /var/www/html
        ServerName ・・・
    
    ProxyPass /blazorapp http://localhost:5010/
    ProxyPassReverse /blazorapp http://localhost:5010/
    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
    RewriteRule /blazorapp/(.*) ws://localhost:5010/$1 [P]
</VirtualHost>

※kestrelサービスはlocalhost:5010で口を開けて待っているという想定。(appsettings.jsonにて指定するとかネ。)

[[Service定義ファイル]]

[Unit]
Description=<サービスの説明>
[Service]
WorkingDirectory=/<working directory path>
ExecStart=/usr/bin/dotnet /<dllパス>/<アプリケーション名>.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=<アプリケーション名>
User=apache
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
[[appsettings.json]]

{
  "Kestrel" : {
        "EndPoints" : {
                "Http" : {
                        "Url" : "http://localhost:5010"
                 }
         }
  },
     ・・・
}

一応、動作確認済み。でも、Websocketの箇所は少々、自信無し・・・
後で別のサービスを追加して確認してみよう・・・

カテゴリー: asp.net core, Blazor, dotnetcore, 技術系 | 6件のコメント

asp.net core webapi

ASP.NET CoreにはRESTful Web APIを作成するためのテンプレートが用意されている。
作成するには以下のように、プロジェクトテンプレートとして、webapiを指定する。

dotnet new webapi -o プロジェクトフォルダ

ASP.NET CoreのWeb APIはhttp要求を受け取って、結果をJSON形式で返す。

APIの実装にはhttp要求のメソッド毎に呼び出すインスタンスメソッドを定義するだけで良い。

簡単な例

プロジェクトフォルダ下のControllersフォルダに<コントローラ名>Controller.csと言う形でファイルを作成すると、”http://・・・/UserInfo”というURLを持つWeb APIが作成される。

UserInfoController.cs

namespace WebAPITest.Controllers
{
    [ApiController] // APIコントローラであることを宣言
    // ルーティングを設定
    // この例では、http://・・・/UserInfo [controller]はクラス名から
    // Contrllerを取り除いたものに置換される
    // 仮想ディレクトリの階層を変える事も可能。例えば、apiの下
    // (http://・・・/api/UserInfo)としたければ、
    // [Route("api/[controller]")]
    // コントローラ名と全く異なるURLにする事も可能
    [Route("[controller]")]
    public class UserInfoController : ControllerBase
    {
        // パラメータ無しGETの処理
        // http://・・・/UserInfo/
        [HttpGet]
        public List<UserInfoItem> GetUserList()
        {
            // ユーザー情報リストを取得
            var lst = ・・・.ToList();
            return lst; // 自動的にJSONに変換される
        }
        // パラメータidありの処理。
        // 例 GET http://・・・/UserInfo/5
        [HttpGet("{id}")]
        public UserInfoItem GetUserInfo(int id)
        {
              // 指定されたIDを持つユーザー情報を取得
              var u = ・・・・.FirstOrDefault();
              if (u == null) {
                  // ControllerBaseに定義されている。
                  // HTTPヘッダ Status 404: Not Foundを返す
                return NotFound();
              } else {
                  return u; // 自動的にJSONに変換される
              }
          }
    }
}

この例ではGETリクエストのみだが、POSTやPUT,DELETEも実装可能。

例えば、[HttpDelete(“{id}”)]のような属性を付けると、

DELETE /UserInfo/5

のようなURLをハンドリングするメソッドとなる。
(通常は削除要求処理を実装だよね)

以上のようにかなり簡単にRESTful Web APIが実装できる。

詳しくは下記URLを参照
https://docs.microsoft.com/ja-jp/aspnet/core/web-api/?view=aspnetcore-3.1

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

blazor wasmとSignalR

blazor wasm版でSignalRを使用する場合に注意しなくてはならない事。
wasm版はバックエンドとしてブラウザのhttp送受信機能を使用するので、HUB側とドメインが異なる場合にHUB側でCORS設定をしておかないとHubConnection.StartAsyncを呼び出した時点で実行時エラーとなる。

どうやら、ヘッダに関しての物らしく、以下のようにAllowAnyHeader()を追加したところ、接続が完了し、クライアント側が正常に動作するようになった。(Originだけではエラー回避できない)

            services.AddCors(options => {
                options.AddPolicy("AllowAnyHostsPolicy",
                    builder => {
                        builder.WithOrigins(・・・)
                        .AllowAnyHeader();
                    });
            });

動かない理由を把握するの少々手こずったので、メモ。

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

blazor WASM版をIISへ公開

blazor WASM版のIISへ公開するには、まず、URL Rewriteモジュールの導入が必要。導入されていない場合はWeb Platform Installer等でURL Rewrite 2.0モジュールをIISへ追加する。

後は、dotnet publishコマンドで作成された、bin\Relase\<フレームワーク>\publish\*.*をIISが認識できるフォルダーへコピーし、アプリケーションとして登録するだけ。

.wasm等のMIME設定はweb.configに含まれておりまた、URLリダイレクトの設定もこのファイルに含まれているため、URLを指定するだけでWASMアプリが動作する。

カテゴリー: asp.net core, Blazor, dotnetcore, Windows | 1件のコメント

asp.net coreアプリをIISへ公開する

asp.net coreアプリをWindows Serverの標準HTTPDである、IIS(Internet Information Server)へ公開する手順を調べて、実際に公開してみた。(Visual Studioではなく、手動)

まず、IISが動作するサーバに Windows Hosting Bundleをインストールする必要がある。dotnet core 3.1の場合は、このページのWindows Hoting Bundleをダウンロードし、インストール。

次に、asp.net core用のアプリケーションプールを作成。
「マネージコードなし」に指定するのを忘れずに。

後は、IISがアクセス可能なディレクトリを作成して、そこに、

dotnet publish <プロジェクト名> --configuration Release

で作成したファイル(bin\Release\<フレームワーク>\publish\*.*)をコピー。

そのディレクトリをIISのアプリケーションとして登録。
この時、先程作成した、アプリケーションプールを指定する。

これで、公開は完了。
ただし、一度アプリが動作すると、一部ファイルがオープンされっぱなしになるようで、再度コピーしようとすると、netcoreが使用中とのメッセージが出力される事があるが、runtime dllなので無視しても大丈夫だと思うけど、心配ならば、一度アプリケーションプールを再起動してからコピーすれば良い。

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

asp.net core Web API CORS

asp.net core Web APIではデフォルトでCORS (Cross Origin Resource Sharing) がディセーブルになっているため、別サイトからアクセスすると、クライアント側でエラーが発生してしまう。

CORSを有効にするには、Startup.csに以下のような、設定が必要となる。

public IConfiguration Configuration { get; }
public Startup(IServiceConfiguration configuration) {
  Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
  // CORSポリシーの追加
  services.AddCors(options =>
  {
    options.AddPolicy("AllowAllOriginsPolicy", // 名前は何でも良い
      builder =>
      {
        // この例では全ホストから受付け可能(どうせ、FWでブロックされるし・・・)
        // ホストを制限するには、.WithOriginsメソッドを使用
        // ヘッダを制限するには、.WithHeadersメソッドを使用
        // HTTPメソッドを制限するには、.WithMethodsメソッドを使用
        builder.AllowAnyOrigin();
       });
  });
  services.AddControllers();
}

↑の例では、全てのホストからの接続を許可している。
特定のホストだけ許可するには、.WithOriginsを使用する。

    builder.WithOrigins("http://boo.foo.com","http://www.boo.foo.com");

後は、Configuraメソッドの中で、全てのエンドポイントに適用するか、エンドポイント毎に適用するかによって、指定が異なる。
全てのエンドポイントに適用するには、Configureメソッドの中で、

  app.UseCors();

を呼び出す。

詳しくは、

https://docs.microsoft.com/ja-jp/aspnet/core/security/cors?view=aspnetcore-3.1

を参照。

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

SignalRについて

この間SignalRで云々というエントリを投稿したが、SignalRそのものの説明が無かったので、簡単に説明しておこうかと。

SignalRはasp.net coreアプリとして、HUB(サーバ)を作成することにより、複数のクライアントとHUB間でメッセージのやりとりを行う事ができる仕組み。

クライアント用ライブラリは.net,java,javascriptが用意されている。

クライアントから、HUBに接続し、下記のような事を行う事が可能。

  • サーバからのPush配信
  • クライアントからHUB経由で全クライアントまたはグループ内クライアントへメッセージ一斉送信
  • クライアントからHUB経由で特定のクライアントへメッセージ送信

実装想定例としては、

  • サーバからの高頻度の更新を必要とするアプリ、例えばGPSを利用した位置特定および周辺情報のリアルタイム表示
  • システム監視アプリ
  • チャットなどのコミュニケーションアプリ
  • ホワイトボードなどのコラボレーションアプリ

仕組みとしては、クライアント~HUB間の接続を行った後、RPCを用いて、クライアント側からHUBのプロシジャ(実装はメソッド)を実行したり、クライアント側でHUBからのメッセージ受信イベントハンドラ(メッセージ毎に定義可能)を定義して、特定処理を行う。

簡単な例を挙げると、以下のような感じでチャットアプリのようなものを作成することが可能。

HUB側
// HUBに接続されている、全てのクライアントにメッセージ送信
public async Task SendMessageAll(string user, string message) {
              await Clients.All.SendAsync("ClientMessge",user,message);
}
// HUB側からのPush通知
private async Task Notify(string message) {
              await Clients.All.SendAsync("SystemNotify", message);
}
Client側
HubConnection con = HubConnectionBuilder()
            .WithUrl("http://・・・")
            .Build();

// メッセージハンドラの登録
con.On<string,string>("ClientMessage",
      (user,message) => {
                    // 処理
      }
);
con.On<string>(""SystemNotify",
      (message) => {
                    // 処理
      }
);
・・・
// 現在HUBに接続している全てのクライアントにメッセージ送信要求
await con.InvokeAsync("SendMessageAll",MyUserName,Message);

HUB側ではClients.Allの他

  • Clients.Other                    要求元以外のクライアント
  • Clients.Group                   特定のグループ
  • Clients.User                     特定のユーザ
  • Clients.AllExcept             指定されたユーザ以外全て
  • Clients.Caller 呼出元のクライアント

等、送信先の指定が可能。

クライアント側は

  On<パラメータ型,[パラメータ型,…]>(メッセージID,メッセージハンドラ)

の形で複数種類のメッセージをハンドリングする事が可能。
※パラメータの型はHUB上の定義と合っていなければハンドリングされない。

blazor wasm版などと組み合わせると、結構面白いアプリを作成することができるのでは無いだろうか。

ちなみに、クライアント,HUB双方でメソッド名やメッセージ名を文字列で扱っているが、バグの温床となり得るので、Interfaceを作成して、nameof(メソッド名)やnameof(メッセージ名)等とすると良いだろう。

詳細は下記などを参照
https://docs.microsoft.com/ja-jp/aspnet/core/signalr/introduction?view=aspnetcore-3.1

カテゴリー: asp.net core, C#, dotnetcore, SignalR, 技術系 | 1件のコメント

asp.net core blazor(server side)で、SignalR

asp.net core blazor(server side)でSignalRのテストをしてみた。同じプロジェクト中にHubを作成して簡易チャットを作成。

接続や処理要求は良いのだが、問題が1つ。それは、ページを閉じたり、ページから移動した時にHub側で切断イベント(OnDisconnectedAsync)が発生しないこと。色々調べたんだけど、結局、クライアント側のアプリから明示的に切断しないと発生しないみたい。

Keepaliveとかのタイマー値をいじってみたが、つながっているのに切断イベントが上がったり・・・
多分、クライアントとHub双方で値を調整しなければいけないんだろうね。

じゃぁ、「ページ閉じた時に明示的に切断したれと」思い、blazorページにIDisposableインターフェースをImplementしてDisposableメソッドを定義、その中で明示的に切断するようにしたら、Hub側でイベントが発生するようになりました。

@implements System.IDisposable
・・・
    //
    //  ページ破棄時に明示的にSignalRを切断してみる
    //
    void IDisposable.Dispose() {
        // conはHubConnectionのインスタンス
        // 明示的に接続を破棄
        con.DisposeAsync();
    }
}

まぁ、これが正しい方法かどうかは分からないけど・・・

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

blazor Server Sideアプリのサブサイトへの公開

asp.net blazor server sideアプリを/blazorAppのようにサブサイトに公開するには、Pages/_Host.cshtml中の<base>タグを<base href=”/blazorApp/”/>のように書き換えるだけではNGで、Startup.cs中のConfigureメソッドに下記を追加しなければならない。

app.UsePathBase("/blazorApp")

※備忘録として

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

blazor JSからC#メソッド呼び出し

asp.net core blazorでJavascriptからC#メソッドを呼び出す方法をMSのサイトや、他のサイトで検索した結果、staticメソッドや他クラスインスタンスメソッドを呼び出す方法については書いてあったが、ページクラスのメソッド呼出しについて書いてあるサイトが見つからなかった。

ページクラスのメソッドが呼べれば、クラス変数がそのまま使用できるので、変数とバインドされているHTMLエレメント値を直接いじれるので便利だと思い、色々試してみた。

サイトに書かれていた説明では、呼出されるC#メソッドには[JSInvokable]属性をつける必要がある。これはどのサイトでも同じことが書かれている。

staticメソッドの場合は、 下記のような感じでJavascriptから呼び出すことが可能。

Dotnet.invokeMethod(<アプリ名>,<メソッド名>[,パラメータ1[,パラメータ2...]])
または
Dotnet.invokeMethodAsync(上記と同様)

ただし、staticメソッドなので、バインドされたページインスタンス変数にはアクセスできない。

もう一つ例が載っているのが、ヘルパークラスを介した呼出し。

C#側
var helper = new MyJsHelper();
await JSRuntime.InvokeVoidAsync("JsFunc",
  DotNetObjectReference.Create(helper));

JS側
function JsFunc(helper) {
  helper.invokeMethodAsync(・・・); // invokeMethodも使用可
}

でも、同じようにバインドされたページインスタンス変数にはアクセスできないよね。(もちろん、ページインスタンスをヘルパーインスタンス内に保存すれば大丈夫かもしれないけれど・・・)

で、上記を見てもしかしてと思って以下のようなコードを書いてみた。

C#側
private string Mesg = "";
・・・
[JSInvokable]
public void CSMethod(int p1, double p2, string p3) {
  Mesg = $"p1={p1}, p2={p2}, p3={p3}";
  this.StateHasChanged(); // JSから呼び出された場合は自動更新されないので
}
・・・
  await JSRuntime.InvokeVoidAsync("JsFunc",
    DotNetObjectReference.Create(this), 
    "CSMethod",10,3.14,"Hello");

JS側
function JsFunc(helper,func,p1,p2,p3) {
  helper.invokeMethodAsync(func,p1,p2,p3);
}

見事に動いてくれました。

で、これ使って何をやりたかったかというと、BingMap等のイベントハンドラをC#で書きたかっただけなのです。実際には、ピン等を追加したりするためにJS関数を細切れに作って、C#から呼び出すことになるんだけどね・・・

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