Blazor - サーバーで実行されているのかブラウザで実行されているのかを判別する方法

## 先に結論

`System.OperatingSystem.IsBrowser()` を実行してください。`true` が返る場合は、ブラウザ上で (いわゆる Blazor WebAssembly として) 実行されています。

## 背景

Blazor には Blazor WebAssembly、Blazor Server の基本2形態があり、さらに 2023年11月リリースの .NET 8 からは、Blazor Static Server Side Rendering が加わりました。このように、Blazor は、サーバー側プロセスとして実行される場合と、Web ブラウザ内の WebAssembly 上で実行される場合の、2つの場合があります。

また、一般に Blazor WebAssembly に分類される場合でも、サーバー側事前レンダリングを実装していると、その事前レンダリング処理はサーバー側プロセスで実行されます。.NET 8 のリリースにともない、"Auto レンダーモード" の登場や、標準のプロジェクトテンプレートが生成するプロジェクトで事前レンダリングが既定で有効になっていることによって、ますます、同じコンポーネント実装が、サーバー側とブラウザ側の両方で実行されうる機会が増えました。

Blazor のコンポーネント (Razor コンポーネント) の実装にあたっては、基本的には Microsoft 公式ドキュメントのガイドラインに沿っていれば、サーバー側でもブラウザ側でも、どちらでも問題なく動作するようにできあがります。とはいえ、いくつかのエッジケースでは、今現在、サーバー側で実行されているのか、ブラウザ上で実行されているのかを判定したいケースがでてくるでしょう。

そうしたときに、具体的には、どのようなコードを書けば、サーバー側とブラウザ側のどちらで実行されているのかを知ることができるのでしょうか。

## 検証

実行時のプラットフォーム環境に関する各種情報は、`System.Runtime.InteropServices` 名前空間の `RuntimeInformation` クラスや `System.Environment` クラス、そして `System.OperatingSystem` クラスから公開されているメンバーを介して様々知ることができます。

そこで、それらメンバーの値を表示する以下のコンポーネントを実装し、

```html:RuntimeInfo.razor
@using System.Runtime.InteropServices

<div>
    <dt>OperatingSystem.IsBrowser()</dt>
    <dd>@OperatingSystem.IsBrowser()</dd>

    <dt>Framework Description</dt>
    <dd>@RuntimeInformation.FrameworkDescription</dd>

    <dt>Process Architecture</dt>
    <dd>@RuntimeInformation.ProcessArchitecture</dd>

    <dt>OS Architecture</dt>
    <dd>@RuntimeInformation.OSArchitecture</dd>

    <dt>OS Description</dt>
    <dd>@RuntimeInformation.OSDescription</dd>

    <dt>OS Platform</dt>
    <dd>@Environment.OSVersion.Platform</dd>

    <dt>OS Version</dt>
    <dd>@Environment.OSVersion.Version</dd>

    <dt>OS Version ServicePack</dt>
    <dd>@Environment.OSVersion.ServicePack</dd>

</div>
```

このコンポーネントを Blazor Sever と Blazor WebAssembly のそれぞれで実行して、どのように表示されるかを確認してみました。

### Blazor Server 上での実行結果

メンバー                     | 値
----------------------------| ------------------
OperatingSystem.IsBrowser() | False
Framework Description       | .NET 8.0.0
Process Architecture        | X64
OS Architecture             | X64
OS Description              | Microsoft Windows 10.0.22631
OS Platform                 | Win32NT
OS Version                  | 10.0.22631.0
OS Version ServicePack      |

※上記結果は自分の手元の Windows PC で実行した結果です。当然のことながら、Apple Silicon の Mac 上では、プロセッサアーキテクチャをはじめ、異なる表示となります。

### Blazor WebAsembly 上での実行結果

メンバー                     | 値
----------------------------| ------------------
OperatingSystem.IsBrowser() | True
Framework Description       | .NET 8.0.0
Process Architecture        | Wasm
OS Architecture             | Wasm
OS Description              | Browser
OS Platform                 | Other
OS Version                  | 1.0.0.0
OS Version ServicePack      |

### 何で判定できるか/何で判定するのがよいか

結論としては冒頭に書いたとおり、`System.OperatingSystem` クラスの `IsBrowser` 静的メソッドを呼び出しての戻り値で判定すればよいでしょう。

とはいえ、念のため、他の情報源にも目を通しておきます。まずは、`RuntimeInformation` クラスの `OSDescription` が "Browser" であるか否かで判定できそうです。

いっぽうで、`RuntimeInformation` クラスの `ProcessArchitecture` や `OSArchitecture` について、現状ですとブラウザ上での実行時のみ `Wasm` になっています。しかしこのプロセッサアーキテクチャでの判定は止めた方がよいでしょう。というのも、今後将来、サーバー側でも WebAssembly に基づくプラットフォームで実行されるようになると (2023年12月現在では、ASP.NET Core はまだ公式には Wasmtime などのような WebAssembly プラットフォームでの実行はサポートされていない、と認識しています)、サーバー側での実行であっても、プロセッサアーキテクチャが "Wasm" になる可能性が高いと思われるからです。

はじめの結論に戻りますが、`System.OperatingSystem` クラスの `IsBrowser` 静的メソッドにはもうひとつ、面白い特徴があります。**`IsBrowser` メソッドの実装は、なんと、即値**なのです。例えば Blazor WebAssembly 用の System.Runtime.dll 中では、`IsBrowser` メソッドは以下のコードとなっています。

```cs
public sealed class OperatingSystem ...
{
  ...
  public static bool IsBrowser() => true;
  ...
```

実行時にあれこれ条件分岐して判断しているわけではないのですね。

## まとめ

ある Razor コンポーネントの実装内で、実行時に、今現在このコンポーネントはサーバー側で実行されているのかブラウザ上で実行されているのかを、そう多くはないとは言え、判断したい場合がありえます。

そのような場合は、`System.OperatingSystem` クラスの `IsBrowser()` 静的メソッドの戻り値を参照してください。その戻り値が `true` であれば、ブラウザ上で実行されていると判断できます。

_Learn, Practice, Share!_