Blazor Serverアプリケーションを開発していると、デバッグのためにブラウザのコンソールにデータを出力したくなることよくがあります。まぁ「デバッガー使えばいいじゃん」はたしかにそうなのですが、単純に「ここの処理通過してるか見ておきたいなー」程度の確認のとき、パッと「********hoge処理通過********」とか適当な文字列をコンソールに出したりします。
しかし、ことBlazorでの開発の場合、コンソールに出力するSystem.Diagnostics.Debug.WriteLine()やConsole.WriteLine()を使っても、ブラウザのコンソールには表示されません。今回はこの点について書きたいと思います。
結論から言えば、Blazor Serverの仕組みに起因しています。
Blazor Serverでは、C#のコードはサーバー側で実行されます。
System.Diagnostics.Debug.WriteLine() → (サーバー側である)Visual Studioのデバッグコンソールに出力Console.WriteLine() → サーバーのコンソール(ターミナル)に出力上記のようにそれぞれ「サーバー側」で実行され出力されます。しかし、ブラウザはクライアント側で動作しているため、サーバー側のコンソール出力は見えないのです。考えれば当たり前体操なのですが、つい忘れがち。
出力されているところはわかったのですが、「確認したいんだよぉ、ブラウザのコンソールでよぉ!」を実現するにはまあまあ面倒です。というのも、ブラウザのコンソールに出力するにはJavaScript Interop (JSInterop) を使って、JavaScriptのconsole.log()を呼び出す必要があります。
最もシンプルな方法は、IJSRuntimeを使ってJavaScriptのconsole.logを直接呼び出すことです。
@page "/console-example"
@inject IJSRuntime JS
<h3>ブラウザコンソール出力のデモ</h3>
<button @onclick="LogToConsole">ブラウザコンソールに出力</button>
@code {
private async Task LogToConsole()
{
await JS.InvokeVoidAsync("console.log", "Hello from Blazor Server!");
}
}この方法には以下のようなメリットがあります。
InvokeVoidAsync() を使用(戻り値が不要な場合)"console.log"を指定@code {
private async Task LogMultipleValues()
{
var user = new { Name = "田中太郎", Age = 30 };
await JS.InvokeVoidAsync("console.log", "ユーザー情報:", user);
}
}頻繁に使用する場合は、ヘルパークラスを作成すると便利です。
// Services/ConsoleLogger.cs
public class ConsoleLogger
{
private readonly IJSRuntime _js;
public ConsoleLogger(IJSRuntime js)
{
_js = js;
}
public async Task Log(params object[] args)
{
await _js.InvokeVoidAsync("console.log", args);
}
public async Task Warn(params object[] args)
{
await _js.InvokeVoidAsync("console.warn", args);
}
public async Task Error(params object[] args)
{
await _js.InvokeVoidAsync("console.error", args);
}
}builder.Services.AddScoped<ConsoleLogger>();@page "/logger-example"
@inject ConsoleLogger Logger
<button @onclick="TestLogger">各種ログ出力</button>
@code {
private async Task TestLogger()
{
await Logger.Log("通常のログ");
await Logger.Warn("警告メッセージ");
await Logger.Error("エラーメッセージ");
}
}より高度な出力を行いたい場合は、カスタムJavaScript関数を作成できます。でも今回の「ちょっと確認したい」みたいな場合には重すぎるんですよねぇ・・・。
window.consoleHelper = {
logWithTimestamp: function(message) {
const timestamp = new Date().toLocaleTimeString();
console.log(`[${timestamp}] ${message}`);
},
logObject: function(obj) {
console.log(JSON.stringify(obj, null, 2));
}
};
<script src="js/consoleHelper.js"></script>@code {
private async Task LogWithCustomFunction()
{
await JS.InvokeVoidAsync("consoleHelper.logWithTimestamp",
"カスタム関数からの出力");
}
}JSInteropは非同期処理なので、awaitとasyncを使用する必要があります。
コンポーネントの初期化中(OnInitializedなど)で呼び出す場合は、OnAfterRenderAsyncを使用してください。
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("console.log", "コンポーネントがレンダリングされました");
}
}Blazor Serverではデフォルトでプリレンダリングが有効になっており、サーバー側で最初のレンダリングが行われます。ただ、この時点ではJSInteropは使用できません。
プリレンダリングを無効にする場合は、_Host.cshtmlで以下のように設定します。
<component type="typeof(App)" render-mode="Server" />Blazor Serverでブラウザコンソールに出力するには、以下のようなわりと面倒な手順をどれかしら採用する必要があります。
console.logを呼び出すBlazorだとReactやVue.jsとは感覚が異なるので、割と沼だと思ってます。