クライアントのIPアドレスが必要になった
DBで保存しておきたいとか、ログに出しておきたいとか、それなりにありがちな要求にBlazorServerでどうすればいいのか調べてみた。
BlazorWASMであればさほど悩まなくて済むであろうクライアントのIPアドレス。
BlazorServerはその名の通りサーバーで動作している。
WASMと同じ方法(HttpContext.Connection.RemoteIpAddress)で取得して正しい値が得られるか気になったので先ずは検索。
最初にgithubのaspnetcoreにある以下のissueが目についた。
How to get the remote IP address of the end user in Blazor Server App running on IIS #31920
https://github.com/dotnet/aspnetcore/issues/31920
が、解決していない様子。
続いて、↓も見てみる
Get IP adress blazor server side #37728
https://github.com/dotnet/aspnetcore/issues/37728
var remoteIpAddress = request.HttpContext.Connection.RemoteIpAddress;
を、_Host.cshtmlで処理しておけばルートコンポーネントに渡せるとか書いてある。
そういえば#31920のコメントにあったサンプルにも_Host.cshtmlで似たようなことが書いてあった。
<component type="typeof(App)" param-InitialState="tokens" render-mode="ServerPrerendered" />
現時点で用意してある環境で試してみる
(サーバーを用意していないためほぼ意味がない)
Win11
IIS10
VS2022 .NET6
- 適当な場所にクラス「UserInfo」を用意しておく。
public class UserInfo
{
public string UserAgent { get; set; }
public string IPAddress { get; set; }
}
- 一応XFFを考慮しつつ、UserInfoをDI
// 略
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
// 略
builder.Services.AddScoped<UserInfo>();
// 略
app.UseForwardedHeaders();
// 略
- アプリのルートページでリクエスト元のIPアドレスを取得、ルートコンポーネントにパラメーターとして渡す。
@using System.Net
@using Microsoft.AspNetCore.Authentication
@code {
string ip = HttpContext.Connection?.RemoteIpAddress.ToString();
// デバッグ実行してるとどうしても::1になる
if (ip == "::1")
{
// TODO:AddressListは環境に依存してしまうので後で考える(ほとんどの環境で[2]なのではないだろうか・・・
ip = Dns.GetHostEntry(Dns.GetHostName()).AddressList[3].ToString();
}
UserInfo _userInfo = new()
{
UserAgent = HttpContext.Request.Headers["User-Agent"],
IPAddress = ip
};
}
// param-InitialUserInfoの「InitialUserInfo」は後述のApp.razorでパラメーター定義
<component type="typeof(App)" param-InitialUserInfo="_userInfo" render-mode="ServerPrerendered" />
- ルートコンポーネントにDIされたUserInfoのインスタンスに、ルートページから渡された値を戻すと、子コンポーネントでも同じ値が得られる
@inject UserInfo UserInfo
@code {
// ルートページから渡されるUserInfo
[Parameter]
public UserInfo InitialUserInfo { get; set; }
protected override Task OnInitializedAsync()
{
// DIされたUserInfo
UserInfo.IPAddress = InitialUserInfo.IPAddress;
UserInfo.UserAgent = InitialUserInfo.UserAgent;
return base.OnInitializedAsync();
}
}
- IPアドレスを使用したいコンポーネントでUserInfoをDIしてもらえるように記述するだけ
@inject UserInfo UserInfo
<p>@UserInfo.UserAgent</p>
<p>@UserInfo.IPAddress</p>
今後
サーバー立てて検証する予定
BlazorServerをUbuntuにデプロイすることも気になっている。
各Razorコンポーネントに @inject
を書くことすら億劫なのでちゃんと共有できるように書き直す。