ASP.NET Core Blazor で JavaScript 関数から .NET メソッドを呼び出す

この記事では、JavaScript から .NET メソッドを呼び出す方法について説明します (JS)。 .NET から JS 関数を呼び出す方法については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

静的 .NET メソッドの呼び出し

JavaScript (JS) から静的 .NET メソッドを呼び出すには、JS 関数 DotNet.invokeMethod または DotNet.invokeMethodAsync を使用します。 メソッドを含むアセンブリの名前、静的 .NET メソッドの識別子、および引数を渡します。

次に例を示します。

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。
  • {ARGUMENTS} プレースホルダーは、メソッドに渡す省略可能なコンマ区切りの引数であり、各引数は JSON シリアル化可能である必要があります。
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethod は操作の結果を返します。 DotNet.invokeMethodAsync は、操作の結果を表す JS Promise を返します。

Blazor Server シナリオをサポートするには、非同期関数 (invokeMethodAsync) が同期バージョン (invokeMethod) より優先されます。

.NET メソッドはパブリックかつ静的であり、[JSInvokable] 属性を持つ必要があります。

次に例を示します。

  • {<T>} プレースホルダーは戻り値の型を示します。これは、値を返すメソッドにのみ必要です。
  • {.NET METHOD ID} プレースホルダーはメソッド識別子です。
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

オープン ジェネリック メソッドを呼び出すことは、静的な .NET メソッドではサポートされていませんが、この記事で後述するインスタンス メソッドではサポートされています。

次の CallDotNetExample1 コンポーネントでは、ReturnArrayAsync C# メソッドによって int 配列が返されます。 [JSInvokable] 属性 がメソッドに適用され、JS によってメソッドが呼び出し可能になります。

Pages/CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

<button> 要素の onclick HTML 属性は、Blazor の @onclick ディレクティブ属性ではなく、click イベントを処理するための JavaScript の onclick イベント ハンドラー割り当てです。 returnArrayAsync JS 関数はハンドラーとして割り当てられます。

次の returnArrayAsync JS 関数では、前の CallDotNetExample1 コンポーネントの ReturnArrayAsync .NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

Trigger .NET static method ボタンが選択されている場合は、ブラウザーの開発者ツール コンソールの出力に配列データが表示されます。 出力の形式はブラウザーによって若干異なります。 次の出力は、Microsoft Edge で使用される形式を示しています。

Array(3) [ 1, 2, 3 ]

既定では、JS 呼び出しの .NET メソッド識別子は .NET メソッド名ですが、[JSInvokable] 属性コンストラクターを使用して別の識別子を指定することもできます。 次の例では、DifferentMethodNameReturnArrayAsync メソッドに割り当てられたメソッド識別子です。

[JSInvokable("DifferentMethodName")]

DotNet.invokeMethod または DotNet.invokeMethodAsync の呼び出しで、DifferentMethodName を呼び出して ReturnArrayAsync .NET メソッドを実行します。

  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');

注意

このセクションの ReturnArrayAsync メソッドの例では、明示的な C# async および await キーワードを使用せずに Task の結果を返します。 async および await を使用したメソッドのコーディングは、await キーワードを使用して非同期操作の値を返す一般的なメソッドです。

async および await キーワードで構成される ReturnArrayAsync メソッド:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

詳細については、C# ガイドの「Async および Await を使用した非同期プログラミング」を参照してください。

インスタンス .NET メソッドの呼び出し

JavaScript (JS) からインスタンス .NET メソッドを呼び出す場合:

  • インスタンスを DotNetObjectReference でラップし、その上で Create を呼び出すことにより、JS を参照して .NET インスタンスを渡します。
  • 渡された DotNetObjectReferenceinvokeMethod または invokeMethodAsync を使用して、JS から .NET インスタンス メソッドを呼び出します。 .NET インスタンスは、JS から他の .NET メソッドを呼び出すときに引数として渡すこともできます。
  • DotNetObjectReference の破棄。

コンポーネント インスタンスの例

次の sayHello1 JS 関数では DotNetObjectReference を受け取り、invokeMethodAsync を呼び出してコンポーネントの GetHelloMethod .NET メソッドを呼び出します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の CallDotNetExample2 コンポーネントの場合:

  • コンポーネントには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。
  • Trigger .NET instance method ボタンを選択すると、JS 関数 sayHello1DotNetObjectReference と一緒に呼び出されます。
  • sayHello1:
    • GetHelloMessage を呼び出し、メッセージ結果を受信します。
    • メッセージの結果を呼び出し元の TriggerDotNetInstanceMethod メソッドに返します。
  • resultsayHello1 から返されたメッセージがユーザーに表示されます。
  • メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Pages/CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(this);
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

インスタンス メソッドに引数を渡すには:

  1. .NET メソッドの呼び出しにパラメーターを追加します。 次の例では、名前がメソッドに渡されます。 必要に応じて、追加のパラメーターを一覧に追加します。

    <script>
      window.sayHello2 = (dotNetHelper, name) => {
        return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
      };
    </script>
    
  2. .NET メソッドにパラメーター リストを指定します。

    Pages/CallDotNetExample3.razor:

    @page "/call-dotnet-example-3"
    @implements IDisposable
    @inject IJSRuntime JS
    
    <h1>Call .NET Example 3</h1>
    
    <p>
        <label>
            Name: <input @bind="name" />
        </label>
    </p>
    
    <p>
        <button @onclick="TriggerDotNetInstanceMethod">
            Trigger .NET instance method
        </button>
    </p>
    
    <p>
        @result
    </p>
    
    @code {
        private string name;
        private string result;
        private DotNetObjectReference<CallDotNetExample3> objRef;
    
        public async Task TriggerDotNetInstanceMethod()
        {
            objRef = DotNetObjectReference.Create(this);
            result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
        }
    
        [JSInvokable]
        public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
    
        public void Dispose()
        {
            objRef?.Dispose();
        }
    }
    

クラス インスタンスの例

次の sayHello1 JS 関数:

  • 渡された DotNetObjectReferenceGetHelloMessage .NET メソッドを呼び出します。
  • GetHelloMessage から sayHello1 の呼び出し元にメッセージを返します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の HelloHelper クラスには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。 HelloHelper が作成されると、Name プロパティの名前が GetHelloMessage からのメッセージを返すために使用されます。

HelloHelper.cs:

using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}

次の JsInteropClasses3 クラスの CallHelloHelperGetHelloMessage メソッドでは、HelloHelper の新しいインスタンスで JS 関数 sayHello1 を呼び出します。

JsInteropClasses3.cs:

using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3 : IDisposable
{
    private readonly IJSRuntime js;
    private DotNetObjectReference<HelloHelper> objRef;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));

        return js.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

次の CallDotNetExample4 コンポーネントで Trigger .NET instance method ボタンを選択すると、name の値で JsInteropClasses3.CallHelloHelperGetHelloMessage が呼び出されます。

Pages/CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    private async Task TriggerDotNetInstanceMethod()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }

    public void Dispose()
    {
        jsInteropClasses?.Dispose();
    }
}

次の図は、Name フィールドに Amy Pond という名前でレンダリングされたコンポーネントを示しています。 ボタンを選択すると、UI に Hello, Amy Pond! が表示されます。

レンダリングされた 'CallDotNetExample4' コンポーネントの例

JsInteropClasses3 クラスに示されている上記のパターンは、完全にコンポーネントに実装することもできます。

Pages/CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<HelloHelper> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Name フィールドに名前 Amy Pond が指定されている場合、CallDotNetExample5 コンポーネントによって表示される出力は Hello, Amy Pond! です。

前の CallDotNetExample5 コンポーネントでは、.NET オブジェクト参照は破棄されます。 クラスまたはコンポーネントが DotNetObjectReference を破棄しない場合は、渡された DotNetObjectReferencedispose を呼び出して、クライアントから破棄します。

window.jsFunction = (dotnetHelper) => {
  dotnetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
  dotnetHelper.dispose();
}

前の例の場合:

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。

コンポーネント インスタンス .NET メソッド ヘルパー クラス

ヘルパー クラスでは、.NET インスタンス メソッドを Action として呼び出すことができます。 ヘルパー クラスは、次のシナリオで役立ちます。

  • 同じ種類の複数のコンポーネントが同じページにレンダリングされる場合。
  • Blazor Server アプリで、複数のユーザーが同じコンポーネントを同時に使用する場合。

次に例を示します。

  • CallDotNetExample6 コンポーネントには、アプリの Shared フォルダー内の共有コンポーネントであるいくつかの ListItem コンポーネントが含まれています。
  • ListItem コンポーネントは、メッセージとボタンで構成されます。
  • ListItem コンポーネント ボタンが選択されると、その ListItemUpdateMessage メソッドによってリスト項目のテキストが変更され、ボタンが非表示になります。

次の MessageUpdateInvokeHelper クラスでは、クラスがインスタンス化されるときに指定された Action を呼び出すために、JS 呼び出し可能な .NET メソッド UpdateMessageCaller が保持されます。 BlazorSample はアプリのアセンブリ名です。

MessageUpdateInvokeHelper.cs:

using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable("BlazorSample")]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

次の updateMessageCaller JS 関数では、UpdateMessageCaller .NET メソッドが呼び出されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.updateMessageCaller = (dotnetHelper) => {
    dotnetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
    dotnetHelper.dispose();
  }
</script>

次の ListItem コンポーネントは、親コンポーネントで何度でも使用できる共有コンポーネントであり、HTML リスト (<ul>...</ul> または <ol>...</ol>) のリスト項目 (<li>...</li>) を作成します。 各 ListItem コンポーネント インスタンスは、Action がその UpdateMessage メソッドに設定された MessageUpdateInvokeHelper のインスタンスを確立します。

ListItem コンポーネントの InteropCall ボタンが選択された場合、MessageUpdateInvokeHelper インスタンス用に作成された DotNetObjectReferenceupdateMessageCaller が呼び出されます。 これにより、フレームワークはその ListItemMessageUpdateInvokeHelper インスタンスで UpdateMessageCaller を呼び出すことができます。 渡された DotNetObjectReference は JS (dotnetHelper.dispose()) で破棄されます。

Shared/ListItem.razor:

@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

messageUpdateMessage で設定されている場合、UI を更新するために StateHasChanged が呼び出されます。 StateHasChanged が呼び出されていない場合、Blazor には Action が呼び出されたときに UI を更新する必要があることを知る方法はありません。

次の CallDotNetExample6 親コンポーネントには、それぞれ ListItem コンポーネントのインスタンスである 4 つのリスト項目が含まれています。

Pages/CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem />
    <ListItem />
    <ListItem />
    <ListItem />
</ul>

次の図は、2 番目の InteropCall ボタンが選択された後にレンダリングされた CallDotNetExample6 親コンポーネントを示しています。

  • 2 番目の ListItem コンポーネントによって UpdateMessage Called! メッセージが表示されました。
  • ボタンの CSS display プロパティが none に設定されているため、2 番目の ListItem コンポーネントの InteropCall ボタンは表示されません。

レンダリングされた 'CallDotNetExample6' コンポーネントの例

JavaScript の場所

JS 相互運用の概要に関する記事で説明されている方法のいずれかを使用して、JavaScript (JS) コードを読み込みます。

JS モジュールでのスクリプトの分離の詳細については、「JavaScript モジュールでの JavaScript の分離」セクションを参照してください。

警告

<script> タグは動的に更新できないため、<script> タグをコンポーネント ファイル (.razor) 内に配置しないでください。

循環オブジェクト参照の回避

循環参照を含むオブジェクトは、次のいずれに対しても、クライアントでシリアル化することはできません。

  • .NET メソッドの呼び出し。
  • 戻り値の型に循環参照がある場合の、C# からの JavaScript メソッドの呼び出し。

バイト配列のサポート

Blazor では、Base64 へのバイト配列のエンコードおよびデコードを回避する、最適化されたバイト配列 JS 相互運用がサポートされています。 次の例では、JS 相互運用を使用してバイト配列を .NET に渡します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) の終了 </body> タグの内部で、sendByteArray JS 関数を提供します。 関数はコンポーネントのボタンによって呼び出され、値は返されません。

<script>
  window.sendByteArray = () => {
    const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
      0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
      0x20,0x43,0x61,0x70,0x74,0x69,0x61,0x6e,0x2e,0x20,0x4e,
      0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
    DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
      .then(str => {
        alert(str);
      });
  };
</script>

Pages/CallDotNetExample7.razor:

@page "/call-dotnet-example-7"
@using System.Text

<h1>Call .NET Example 7</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote &copy;2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

.NET から JavaScript を呼び出す際にバイト配列を使用する方法については、ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す を参照してください。

JavaScript 相互運用呼び出しのサイズ制限

"このセクションは、Blazor Server アプリにのみ適用されます。Blazor WebAssembly の場合、フレームワークでは JavaScript (JS) 相互運用機能の入力と出力のサイズに制限を課しません。 "

Blazor Server では、ハブ メソッドで許可される SignalR 受信メッセージの最大サイズによって、JS 相互運用呼び出しのサイズが制限されます。これは、HubOptions.MaximumReceiveMessageSize によって適用されます (既定値: 32 KB)。 JS から .NET への SignalR メッセージが MaximumReceiveMessageSize より大きい場合は、エラーがスローされます。 このフレームワークでは、ハブからクライアントへの SignalR メッセージのサイズが制限されることはありません。

SignalR のログがDebug または Trace に設定されていない場合、メッセージ サイズのエラーはブラウザーの開発者ツール コンソールにのみ表示されます。

エラー :次のエラーで接続が切断されました。"エラー: サーバーが終了時にエラーを返しました:接続はエラーで終了しました。"

SignalR サーバー側のログ Debug または Trace に設定されている場合、サーバー側のログには、メッセージ サイズ エラーの InvalidDataException が表示されます。

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

エラー:

System.IO.InvalidDataException:メッセージの最大サイズ 32,768 B を超えました。 メッセージのサイズは、AddHubOptions で構成できます。

制限値を増やすには、Startup.ConfigureServicesMaximumReceiveMessageSize を設定します。 次の例では、受信メッセージの最大サイズを 64 KB (64 * 1024) に設定します。

services.AddServerSideBlazor()
   .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR 受信メッセージ サイズの制限値を増やすと、より多くのサーバー リソースが必要になり、悪意のあるユーザーからのより大きなリスクにサーバーがさらされます。 また、大量のコンテンツを文字列またはバイト配列としてメモリに読み取ると、ガベージ コレクターがうまく機能しない割り当てが発生する可能性もあり、その結果、パフォーマンスがさらに低下します。

大きなペイロードを読み取るための 1 つの選択肢は、小さいチャンクでコンテンツを送信し、ペイロードを Stream として処理することです。 これは、大量の JSON ペイロードを読み取る場合、またはデータを JS で生バイトとして利用できる場合に使用できます。 Blazor Server で大規模なバイナリ ペイロードを送信する、InputFile コンポーネントに似た手法を使用する方法の例については、Binary Submit サンプル アプリを参照してください。

注意

ASP.NET Core 参照ソースへのドキュメント リンクを使用すると、リポジトリの main ブランチが読み込まれます。このブランチは、ASP.NET Core の次回リリースに向けて行われている製品単位の現在の開発を表します。 別のリリースのブランチを選択するには、 [Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使用して、そのブランチを選択します。 たとえば、ASP.NET Core 5.0 リリースの場合は、release/5.0 ブランチを選択します。

Blazor Server アプリで JS と Blazor の間で大量のデータを転送するコードを開発するときは、次のガイダンスを考慮してください。

  • データをより小さな部分にスライスし、すべてのデータがサーバーによって受信されるまでデータ セグメントを順番に送信します。
  • JS および C# コードで大きなオブジェクトを割り当てないでください。
  • データを送受信するときに、メイン UI スレッドを長時間ブロックしないでください。
  • プロセスの完了時またはキャンセル時に、消費していたメモリを解放します。
  • セキュリティ上の理由から、次の追加要件を適用します。
    • 渡すことのできるファイルまたはデータの最大サイズを宣言します。
    • クライアントからサーバーへの最小アップロード レートを宣言します。
  • データがサーバーによって受信されたら、データは:
    • すべてのセグメントが収集されるまで、一時的にメモリ バッファーに格納できます。
    • 直ちに消費できます。 たとえば、データは、データベースに直ちに格納することも、セグメントを受信するたびにディスクに書き込むこともできます。

JavaScript モジュールでの JavaScript の分離

Blazor では、標準の JavaScript モジュールに JavaScript (JS) を分離できます (ECMAScript の仕様)。

JS を分離すると、次のようなベネフィットがあります。

  • インポートされる JS によって、グローバル名前空間が汚染されなくなります。
  • ライブラリおよびコンポーネントのコンシューマーは、関連する JS をインポートする必要がありません。

詳細については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

その他の技術情報

この記事では、JavaScript から .NET メソッドを呼び出す方法について説明します (JS)。 .NET から JS 関数を呼び出す方法については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

静的 .NET メソッドの呼び出し

JavaScript (JS) から静的 .NET メソッドを呼び出すには、JS 関数 DotNet.invokeMethod または DotNet.invokeMethodAsync を使用します。 メソッドを含むアセンブリの名前、静的 .NET メソッドの識別子、および引数を渡します。

次に例を示します。

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。
  • {ARGUMENTS} プレースホルダーは、メソッドに渡す省略可能なコンマ区切りの引数であり、各引数は JSON シリアル化可能である必要があります。
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethod は操作の結果を返します。 DotNet.invokeMethodAsync は、操作の結果を表す JS Promise を返します。

Blazor Server シナリオをサポートするには、非同期関数 (invokeMethodAsync) が同期バージョン (invokeMethod) より優先されます。

.NET メソッドはパブリックかつ静的であり、[JSInvokable] 属性を持つ必要があります。

次に例を示します。

  • {<T>} プレースホルダーは戻り値の型を示します。これは、値を返すメソッドにのみ必要です。
  • {.NET METHOD ID} プレースホルダーはメソッド識別子です。
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

オープン ジェネリック メソッドを呼び出すことは、静的な .NET メソッドではサポートされていませんが、この記事で後述するインスタンス メソッドではサポートされています。

次の CallDotNetExample1 コンポーネントでは、ReturnArrayAsync C# メソッドによって int 配列が返されます。 [JSInvokable] 属性 がメソッドに適用され、JS によってメソッドが呼び出し可能になります。

Pages/CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

<button> 要素の onclick HTML 属性は、Blazor の @onclick ディレクティブ属性ではなく、click イベントを処理するための JavaScript の onclick イベント ハンドラー割り当てです。 returnArrayAsync JS 関数はハンドラーとして割り当てられます。

次の returnArrayAsync JS 関数では、前の CallDotNetExample1 コンポーネントの ReturnArrayAsync .NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

Trigger .NET static method ボタンが選択されている場合は、ブラウザーの開発者ツール コンソールの出力に配列データが表示されます。 出力の形式はブラウザーによって若干異なります。 次の出力は、Microsoft Edge で使用される形式を示しています。

Array(3) [ 1, 2, 3 ]

既定では、JS 呼び出しの .NET メソッド識別子は .NET メソッド名ですが、[JSInvokable] 属性コンストラクターを使用して別の識別子を指定することもできます。 次の例では、DifferentMethodNameReturnArrayAsync メソッドに割り当てられたメソッド識別子です。

[JSInvokable("DifferentMethodName")]

DotNet.invokeMethod または DotNet.invokeMethodAsync の呼び出しで、DifferentMethodName を呼び出して ReturnArrayAsync .NET メソッドを実行します。

  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');

注意

このセクションの ReturnArrayAsync メソッドの例では、明示的な C# async および await キーワードを使用せずに Task の結果を返します。 async および await を使用したメソッドのコーディングは、await キーワードを使用して非同期操作の値を返す一般的なメソッドです。

async および await キーワードで構成される ReturnArrayAsync メソッド:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

詳細については、C# ガイドの「Async および Await を使用した非同期プログラミング」を参照してください。

インスタンス .NET メソッドの呼び出し

JavaScript (JS) からインスタンス .NET メソッドを呼び出す場合:

  • インスタンスを DotNetObjectReference でラップし、その上で Create を呼び出すことにより、JS を参照して .NET インスタンスを渡します。
  • 渡された DotNetObjectReferenceinvokeMethod または invokeMethodAsync を使用して、JS から .NET インスタンス メソッドを呼び出します。 .NET インスタンスは、JS から他の .NET メソッドを呼び出すときに引数として渡すこともできます。
  • DotNetObjectReference の破棄。

コンポーネント インスタンスの例

次の sayHello1 JS 関数では DotNetObjectReference を受け取り、invokeMethodAsync を呼び出してコンポーネントの GetHelloMethod .NET メソッドを呼び出します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の CallDotNetExample2 コンポーネントの場合:

  • コンポーネントには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。
  • Trigger .NET instance method ボタンを選択すると、JS 関数 sayHello1DotNetObjectReference と一緒に呼び出されます。
  • sayHello1:
    • GetHelloMessage を呼び出し、メッセージ結果を受信します。
    • メッセージの結果を呼び出し元の TriggerDotNetInstanceMethod メソッドに返します。
  • resultsayHello1 から返されたメッセージがユーザーに表示されます。
  • メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Pages/CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(this);
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

インスタンス メソッドに引数を渡すには:

  1. .NET メソッドの呼び出しにパラメーターを追加します。 次の例では、名前がメソッドに渡されます。 必要に応じて、追加のパラメーターを一覧に追加します。

    <script>
      window.sayHello2 = (dotNetHelper, name) => {
        return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
      };
    </script>
    
  2. .NET メソッドにパラメーター リストを指定します。

    Pages/CallDotNetExample3.razor:

    @page "/call-dotnet-example-3"
    @implements IDisposable
    @inject IJSRuntime JS
    
    <h1>Call .NET Example 3</h1>
    
    <p>
        <label>
            Name: <input @bind="name" />
        </label>
    </p>
    
    <p>
        <button @onclick="TriggerDotNetInstanceMethod">
            Trigger .NET instance method
        </button>
    </p>
    
    <p>
        @result
    </p>
    
    @code {
        private string name;
        private string result;
        private DotNetObjectReference<CallDotNetExample3> objRef;
    
        public async Task TriggerDotNetInstanceMethod()
        {
            objRef = DotNetObjectReference.Create(this);
            result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
        }
    
        [JSInvokable]
        public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
    
        public void Dispose()
        {
            objRef?.Dispose();
        }
    }
    

クラス インスタンスの例

次の sayHello1 JS 関数:

  • 渡された DotNetObjectReferenceGetHelloMessage .NET メソッドを呼び出します。
  • GetHelloMessage から sayHello1 の呼び出し元にメッセージを返します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の HelloHelper クラスには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。 HelloHelper が作成されると、Name プロパティの名前が GetHelloMessage からのメッセージを返すために使用されます。

HelloHelper.cs:

using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}

次の JsInteropClasses3 クラスの CallHelloHelperGetHelloMessage メソッドでは、HelloHelper の新しいインスタンスで JS 関数 sayHello1 を呼び出します。

JsInteropClasses3.cs:

using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3 : IDisposable
{
    private readonly IJSRuntime js;
    private DotNetObjectReference<HelloHelper> objRef;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));

        return js.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

次の CallDotNetExample4 コンポーネントで Trigger .NET instance method ボタンを選択すると、name の値で JsInteropClasses3.CallHelloHelperGetHelloMessage が呼び出されます。

Pages/CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    private async Task TriggerDotNetInstanceMethod()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }

    public void Dispose()
    {
        jsInteropClasses?.Dispose();
    }
}

次の図は、Name フィールドに Amy Pond という名前でレンダリングされたコンポーネントを示しています。 ボタンを選択すると、UI に Hello, Amy Pond! が表示されます。

レンダリングされた 'CallDotNetExample4' コンポーネントの例

JsInteropClasses3 クラスに示されている上記のパターンは、完全にコンポーネントに実装することもできます。

Pages/CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<HelloHelper> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Name フィールドに名前 Amy Pond が指定されている場合、CallDotNetExample5 コンポーネントによって表示される出力は Hello, Amy Pond! です。

前の CallDotNetExample5 コンポーネントでは、.NET オブジェクト参照は破棄されます。 クラスまたはコンポーネントが DotNetObjectReference を破棄しない場合は、渡された DotNetObjectReferencedispose を呼び出して、クライアントから破棄します。

window.jsFunction = (dotnetHelper) => {
  dotnetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
  dotnetHelper.dispose();
}

前の例の場合:

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。

コンポーネント インスタンス .NET メソッド ヘルパー クラス

ヘルパー クラスでは、.NET インスタンス メソッドを Action として呼び出すことができます。 ヘルパー クラスは、次のシナリオで役立ちます。

  • 同じ種類の複数のコンポーネントが同じページにレンダリングされる場合。
  • Blazor Server アプリで、複数のユーザーが同じコンポーネントを同時に使用する場合。

次に例を示します。

  • CallDotNetExample6 コンポーネントには、アプリの Shared フォルダー内の共有コンポーネントであるいくつかの ListItem コンポーネントが含まれています。
  • ListItem コンポーネントは、メッセージとボタンで構成されます。
  • ListItem コンポーネント ボタンが選択されると、その ListItemUpdateMessage メソッドによってリスト項目のテキストが変更され、ボタンが非表示になります。

次の MessageUpdateInvokeHelper クラスでは、クラスがインスタンス化されるときに指定された Action を呼び出すために、JS 呼び出し可能な .NET メソッド UpdateMessageCaller が保持されます。 BlazorSample はアプリのアセンブリ名です。

MessageUpdateInvokeHelper.cs:

using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable("BlazorSample")]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

次の updateMessageCaller JS 関数では、UpdateMessageCaller .NET メソッドが呼び出されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.updateMessageCaller = (dotnetHelper) => {
    dotnetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
    dotnetHelper.dispose();
  }
</script>

次の ListItem コンポーネントは、親コンポーネントで何度でも使用できる共有コンポーネントであり、HTML リスト (<ul>...</ul> または <ol>...</ol>) のリスト項目 (<li>...</li>) を作成します。 各 ListItem コンポーネント インスタンスは、Action がその UpdateMessage メソッドに設定された MessageUpdateInvokeHelper のインスタンスを確立します。

ListItem コンポーネントの InteropCall ボタンが選択された場合、MessageUpdateInvokeHelper インスタンス用に作成された DotNetObjectReferenceupdateMessageCaller が呼び出されます。 これにより、フレームワークはその ListItemMessageUpdateInvokeHelper インスタンスで UpdateMessageCaller を呼び出すことができます。 渡された DotNetObjectReference は JS (dotnetHelper.dispose()) で破棄されます。

Shared/ListItem.razor:

@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

messageUpdateMessage で設定されている場合、UI を更新するために StateHasChanged が呼び出されます。 StateHasChanged が呼び出されていない場合、Blazor には Action が呼び出されたときに UI を更新する必要があることを知る方法はありません。

次の CallDotNetExample6 親コンポーネントには、それぞれ ListItem コンポーネントのインスタンスである 4 つのリスト項目が含まれています。

Pages/CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem />
    <ListItem />
    <ListItem />
    <ListItem />
</ul>

次の図は、2 番目の InteropCall ボタンが選択された後にレンダリングされた CallDotNetExample6 親コンポーネントを示しています。

  • 2 番目の ListItem コンポーネントによって UpdateMessage Called! メッセージが表示されました。
  • ボタンの CSS display プロパティが none に設定されているため、2 番目の ListItem コンポーネントの InteropCall ボタンは表示されません。

レンダリングされた 'CallDotNetExample6' コンポーネントの例

JavaScript の場所

JS 相互運用の概要に関する記事で説明されている方法のいずれかを使用して、JavaScript (JS) コードを読み込みます。

JS モジュールでのスクリプトの分離の詳細については、「JavaScript モジュールでの JavaScript の分離」セクションを参照してください。

警告

<script> タグは動的に更新できないため、<script> タグをコンポーネント ファイル (.razor) 内に配置しないでください。

循環オブジェクト参照の回避

循環参照を含むオブジェクトは、次のいずれに対しても、クライアントでシリアル化することはできません。

  • .NET メソッドの呼び出し。
  • 戻り値の型に循環参照がある場合の、C# からの JavaScript メソッドの呼び出し。

JavaScript 相互運用呼び出しのサイズ制限

"このセクションは、Blazor Server アプリにのみ適用されます。Blazor WebAssembly の場合、フレームワークでは JavaScript (JS) 相互運用機能の入力と出力のサイズに制限を課しません。 "

Blazor Server では、ハブ メソッドで許可される SignalR 受信メッセージの最大サイズによって、JS 相互運用呼び出しのサイズが制限されます。これは、HubOptions.MaximumReceiveMessageSize によって適用されます (既定値: 32 KB)。 JS から .NET への SignalR メッセージが MaximumReceiveMessageSize より大きい場合は、エラーがスローされます。 このフレームワークでは、ハブからクライアントへの SignalR メッセージのサイズが制限されることはありません。

SignalR のログがDebug または Trace に設定されていない場合、メッセージ サイズのエラーはブラウザーの開発者ツール コンソールにのみ表示されます。

エラー :次のエラーで接続が切断されました。"エラー: サーバーが終了時にエラーを返しました:接続はエラーで終了しました。"

SignalR サーバー側のログ Debug または Trace に設定されている場合、サーバー側のログには、メッセージ サイズ エラーの InvalidDataException が表示されます。

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

エラー:

System.IO.InvalidDataException:メッセージの最大サイズ 32,768 B を超えました。 メッセージのサイズは、AddHubOptions で構成できます。

制限値を増やすには、Startup.ConfigureServicesMaximumReceiveMessageSize を設定します。 次の例では、受信メッセージの最大サイズを 64 KB (64 * 1024) に設定します。

services.AddServerSideBlazor()
   .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR 受信メッセージ サイズの制限値を増やすと、より多くのサーバー リソースが必要になり、悪意のあるユーザーからのより大きなリスクにサーバーがさらされます。 また、大量のコンテンツを文字列またはバイト配列としてメモリに読み取ると、ガベージ コレクターがうまく機能しない割り当てが発生する可能性もあり、その結果、パフォーマンスがさらに低下します。

大きなペイロードを読み取るための 1 つの選択肢は、小さいチャンクでコンテンツを送信し、ペイロードを Stream として処理することです。 これは、大量の JSON ペイロードを読み取る場合、またはデータを JS で生バイトとして利用できる場合に使用できます。 Blazor Server で大規模なバイナリ ペイロードを送信する、InputFile コンポーネントに似た手法を使用する方法の例については、Binary Submit サンプル アプリを参照してください。

注意

ASP.NET Core 参照ソースへのドキュメント リンクを使用すると、リポジトリの main ブランチが読み込まれます。このブランチは、ASP.NET Core の次回リリースに向けて行われている製品単位の現在の開発を表します。 別のリリースのブランチを選択するには、 [Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使用して、そのブランチを選択します。 たとえば、ASP.NET Core 5.0 リリースの場合は、release/5.0 ブランチを選択します。

Blazor Server アプリで JS と Blazor の間で大量のデータを転送するコードを開発するときは、次のガイダンスを考慮してください。

  • データをより小さな部分にスライスし、すべてのデータがサーバーによって受信されるまでデータ セグメントを順番に送信します。
  • JS および C# コードで大きなオブジェクトを割り当てないでください。
  • データを送受信するときに、メイン UI スレッドを長時間ブロックしないでください。
  • プロセスの完了時またはキャンセル時に、消費していたメモリを解放します。
  • セキュリティ上の理由から、次の追加要件を適用します。
    • 渡すことのできるファイルまたはデータの最大サイズを宣言します。
    • クライアントからサーバーへの最小アップロード レートを宣言します。
  • データがサーバーによって受信されたら、データは:
    • すべてのセグメントが収集されるまで、一時的にメモリ バッファーに格納できます。
    • 直ちに消費できます。 たとえば、データは、データベースに直ちに格納することも、セグメントを受信するたびにディスクに書き込むこともできます。

JavaScript モジュールでの JavaScript の分離

Blazor では、標準の JavaScript モジュールに JavaScript (JS) を分離できます (ECMAScript の仕様)。

JS を分離すると、次のようなベネフィットがあります。

  • インポートされる JS によって、グローバル名前空間が汚染されなくなります。
  • ライブラリおよびコンポーネントのコンシューマーは、関連する JS をインポートする必要がありません。

詳細については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

その他の技術情報

この記事では、JavaScript から .NET メソッドを呼び出す方法について説明します (JS)。 .NET から JS 関数を呼び出す方法については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

静的 .NET メソッドの呼び出し

JavaScript (JS) から静的 .NET メソッドを呼び出すには、JS 関数 DotNet.invokeMethod または DotNet.invokeMethodAsync を使用します。 メソッドを含むアセンブリの名前、静的 .NET メソッドの識別子、および引数を渡します。

次に例を示します。

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。
  • {ARGUMENTS} プレースホルダーは、メソッドに渡す省略可能なコンマ区切りの引数であり、各引数は JSON シリアル化可能である必要があります。
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethod は操作の結果を返します。 DotNet.invokeMethodAsync は、操作の結果を表す JS Promise を返します。

Blazor Server シナリオをサポートするには、非同期関数 (invokeMethodAsync) が同期バージョン (invokeMethod) より優先されます。

.NET メソッドはパブリックかつ静的であり、[JSInvokable] 属性を持つ必要があります。

次に例を示します。

  • {<T>} プレースホルダーは戻り値の型を示します。これは、値を返すメソッドにのみ必要です。
  • {.NET METHOD ID} プレースホルダーはメソッド識別子です。
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

オープン ジェネリック メソッドを呼び出すことは、静的な .NET メソッドではサポートされていませんが、この記事で後述するインスタンス メソッドではサポートされています。

次の CallDotNetExample1 コンポーネントでは、ReturnArrayAsync C# メソッドによって int 配列が返されます。 [JSInvokable] 属性 がメソッドに適用され、JS によってメソッドが呼び出し可能になります。

Pages/CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

<button> 要素の onclick HTML 属性は、Blazor の @onclick ディレクティブ属性ではなく、click イベントを処理するための JavaScript の onclick イベント ハンドラー割り当てです。 returnArrayAsync JS 関数はハンドラーとして割り当てられます。

次の returnArrayAsync JS 関数では、前の CallDotNetExample1 コンポーネントの ReturnArrayAsync .NET メソッドが呼び出され、その結果はブラウザーの Web 開発者ツール コンソールに記録されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

Trigger .NET static method ボタンが選択されている場合は、ブラウザーの開発者ツール コンソールの出力に配列データが表示されます。 出力の形式はブラウザーによって若干異なります。 次の出力は、Microsoft Edge で使用される形式を示しています。

Array(3) [ 1, 2, 3 ]

既定では、JS 呼び出しの .NET メソッド識別子は .NET メソッド名ですが、[JSInvokable] 属性コンストラクターを使用して別の識別子を指定することもできます。 次の例では、DifferentMethodNameReturnArrayAsync メソッドに割り当てられたメソッド識別子です。

[JSInvokable("DifferentMethodName")]

DotNet.invokeMethod または DotNet.invokeMethodAsync の呼び出しで、DifferentMethodName を呼び出して ReturnArrayAsync .NET メソッドを実行します。

  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');

注意

このセクションの ReturnArrayAsync メソッドの例では、明示的な C# async および await キーワードを使用せずに Task の結果を返します。 async および await を使用したメソッドのコーディングは、await キーワードを使用して非同期操作の値を返す一般的なメソッドです。

async および await キーワードで構成される ReturnArrayAsync メソッド:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

詳細については、C# ガイドの「Async および Await を使用した非同期プログラミング」を参照してください。

インスタンス .NET メソッドの呼び出し

JavaScript (JS) からインスタンス .NET メソッドを呼び出す場合:

  • インスタンスを DotNetObjectReference でラップし、その上で Create を呼び出すことにより、JS を参照して .NET インスタンスを渡します。
  • 渡された DotNetObjectReferenceinvokeMethod または invokeMethodAsync を使用して、JS から .NET インスタンス メソッドを呼び出します。 .NET インスタンスは、JS から他の .NET メソッドを呼び出すときに引数として渡すこともできます。
  • DotNetObjectReference の破棄。

コンポーネント インスタンスの例

次の sayHello1 JS 関数では DotNetObjectReference を受け取り、invokeMethodAsync を呼び出してコンポーネントの GetHelloMethod .NET メソッドを呼び出します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の CallDotNetExample2 コンポーネントの場合:

  • コンポーネントには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。
  • Trigger .NET instance method ボタンを選択すると、JS 関数 sayHello1DotNetObjectReference と一緒に呼び出されます。
  • sayHello1:
    • GetHelloMessage を呼び出し、メッセージ結果を受信します。
    • メッセージの結果を呼び出し元の TriggerDotNetInstanceMethod メソッドに返します。
  • resultsayHello1 から返されたメッセージがユーザーに表示されます。
  • メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Pages/CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(this);
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

インスタンス メソッドに引数を渡すには:

  1. .NET メソッドの呼び出しにパラメーターを追加します。 次の例では、名前がメソッドに渡されます。 必要に応じて、追加のパラメーターを一覧に追加します。

    <script>
      window.sayHello2 = (dotNetHelper, name) => {
        return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
      };
    </script>
    
  2. .NET メソッドにパラメーター リストを指定します。

    Pages/CallDotNetExample3.razor:

    @page "/call-dotnet-example-3"
    @implements IDisposable
    @inject IJSRuntime JS
    
    <h1>Call .NET Example 3</h1>
    
    <p>
        <label>
            Name: <input @bind="name" />
        </label>
    </p>
    
    <p>
        <button @onclick="TriggerDotNetInstanceMethod">
            Trigger .NET instance method
        </button>
    </p>
    
    <p>
        @result
    </p>
    
    @code {
        private string name;
        private string result;
        private DotNetObjectReference<CallDotNetExample3> objRef;
    
        public async Task TriggerDotNetInstanceMethod()
        {
            objRef = DotNetObjectReference.Create(this);
            result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
        }
    
        [JSInvokable]
        public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
    
        public void Dispose()
        {
            objRef?.Dispose();
        }
    }
    

クラス インスタンスの例

次の sayHello1 JS 関数:

  • 渡された DotNetObjectReferenceGetHelloMessage .NET メソッドを呼び出します。
  • GetHelloMessage から sayHello1 の呼び出し元にメッセージを返します。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

次の HelloHelper クラスには、GetHelloMessage という名前の JS 呼び出し可能な .NET メソッドが含まれます。 HelloHelper が作成されると、Name プロパティの名前が GetHelloMessage からのメッセージを返すために使用されます。

HelloHelper.cs:

using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}

次の JsInteropClasses3 クラスの CallHelloHelperGetHelloMessage メソッドでは、HelloHelper の新しいインスタンスで JS 関数 sayHello1 を呼び出します。

JsInteropClasses3.cs:

using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3 : IDisposable
{
    private readonly IJSRuntime js;
    private DotNetObjectReference<HelloHelper> objRef;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));

        return js.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

次の CallDotNetExample4 コンポーネントで Trigger .NET instance method ボタンを選択すると、name の値で JsInteropClasses3.CallHelloHelperGetHelloMessage が呼び出されます。

Pages/CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    private async Task TriggerDotNetInstanceMethod()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }

    public void Dispose()
    {
        jsInteropClasses?.Dispose();
    }
}

次の図は、Name フィールドに Amy Pond という名前でレンダリングされたコンポーネントを示しています。 ボタンを選択すると、UI に Hello, Amy Pond! が表示されます。

レンダリングされた 'CallDotNetExample4' コンポーネントの例

JsInteropClasses3 クラスに示されている上記のパターンは、完全にコンポーネントに実装することもできます。

Pages/CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<HelloHelper> objRef;

    public async Task TriggerDotNetInstanceMethod()
    {
        objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    public void Dispose()
    {
        objRef?.Dispose();
    }
}

メモリ リークを回避し、ガベージ コレクションを許可するには、DotNetObjectReference によって作成された .NET オブジェクト参照は Dispose メソッドで破棄されます。

Name フィールドに名前 Amy Pond が指定されている場合、CallDotNetExample5 コンポーネントによって表示される出力は Hello, Amy Pond! です。

前の CallDotNetExample5 コンポーネントでは、.NET オブジェクト参照は破棄されます。 クラスまたはコンポーネントが DotNetObjectReference を破棄しない場合は、渡された DotNetObjectReferencedispose を呼び出して、クライアントから破棄します。

window.jsFunction = (dotnetHelper) => {
  dotnetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
  dotnetHelper.dispose();
}

前の例の場合:

  • {ASSEMBLY NAME} プレースホルダーは、アプリのアセンブリ名です。
  • {.NET METHOD ID} プレースホルダーは .NET メソッド識別子です。

コンポーネント インスタンス .NET メソッド ヘルパー クラス

ヘルパー クラスでは、.NET インスタンス メソッドを Action として呼び出すことができます。 ヘルパー クラスは、次のシナリオで役立ちます。

  • 同じ種類の複数のコンポーネントが同じページにレンダリングされる場合。
  • Blazor Server アプリで、複数のユーザーが同じコンポーネントを同時に使用する場合。

次に例を示します。

  • CallDotNetExample6 コンポーネントには、アプリの Shared フォルダー内の共有コンポーネントであるいくつかの ListItem コンポーネントが含まれています。
  • ListItem コンポーネントは、メッセージとボタンで構成されます。
  • ListItem コンポーネント ボタンが選択されると、その ListItemUpdateMessage メソッドによってリスト項目のテキストが変更され、ボタンが非表示になります。

次の MessageUpdateInvokeHelper クラスでは、クラスがインスタンス化されるときに指定された Action を呼び出すために、JS 呼び出し可能な .NET メソッド UpdateMessageCaller が保持されます。 BlazorSample はアプリのアセンブリ名です。

MessageUpdateInvokeHelper.cs:

using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable("BlazorSample")]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

次の updateMessageCaller JS 関数では、UpdateMessageCaller .NET メソッドが呼び出されます。 BlazorSample はアプリのアセンブリ名です。

wwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) の終了 </body> タグの内部:

<script>
  window.updateMessageCaller = (dotnetHelper) => {
    dotnetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
    dotnetHelper.dispose();
  }
</script>

次の ListItem コンポーネントは、親コンポーネントで何度でも使用できる共有コンポーネントであり、HTML リスト (<ul>...</ul> または <ol>...</ol>) のリスト項目 (<li>...</li>) を作成します。 各 ListItem コンポーネント インスタンスは、Action がその UpdateMessage メソッドに設定された MessageUpdateInvokeHelper のインスタンスを確立します。

ListItem コンポーネントの InteropCall ボタンが選択された場合、MessageUpdateInvokeHelper インスタンス用に作成された DotNetObjectReferenceupdateMessageCaller が呼び出されます。 これにより、フレームワークはその ListItemMessageUpdateInvokeHelper インスタンスで UpdateMessageCaller を呼び出すことができます。 渡された DotNetObjectReference は JS (dotnetHelper.dispose()) で破棄されます。

Shared/ListItem.razor:

@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

messageUpdateMessage で設定されている場合、UI を更新するために StateHasChanged が呼び出されます。 StateHasChanged が呼び出されていない場合、Blazor には Action が呼び出されたときに UI を更新する必要があることを知る方法はありません。

次の CallDotNetExample6 親コンポーネントには、それぞれ ListItem コンポーネントのインスタンスである 4 つのリスト項目が含まれています。

Pages/CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem />
    <ListItem />
    <ListItem />
    <ListItem />
</ul>

次の図は、2 番目の InteropCall ボタンが選択された後にレンダリングされた CallDotNetExample6 親コンポーネントを示しています。

  • 2 番目の ListItem コンポーネントによって UpdateMessage Called! メッセージが表示されました。
  • ボタンの CSS display プロパティが none に設定されているため、2 番目の ListItem コンポーネントの InteropCall ボタンは表示されません。

レンダリングされた 'CallDotNetExample6' コンポーネントの例

JavaScript の場所

JS 相互運用の概要に関する記事で説明されている方法のいずれかを使用して、JavaScript (JS) コードを読み込みます。

警告

<script> タグは動的に更新できないため、<script> タグをコンポーネント ファイル (.razor) 内に配置しないでください。

循環オブジェクト参照の回避

循環参照を含むオブジェクトは、次のいずれに対しても、クライアントでシリアル化することはできません。

  • .NET メソッドの呼び出し。
  • 戻り値の型に循環参照がある場合の、C# からの JavaScript メソッドの呼び出し。

JavaScript 相互運用呼び出しのサイズ制限

"このセクションは、Blazor Server アプリにのみ適用されます。Blazor WebAssembly の場合、フレームワークでは JavaScript (JS) 相互運用機能の入力と出力のサイズに制限を課しません。 "

Blazor Server では、ハブ メソッドで許可される SignalR 受信メッセージの最大サイズによって、JS 相互運用呼び出しのサイズが制限されます。これは、HubOptions.MaximumReceiveMessageSize によって適用されます (既定値: 32 KB)。 JS から .NET への SignalR メッセージが MaximumReceiveMessageSize より大きい場合は、エラーがスローされます。 このフレームワークでは、ハブからクライアントへの SignalR メッセージのサイズが制限されることはありません。

SignalR のログがDebug または Trace に設定されていない場合、メッセージ サイズのエラーはブラウザーの開発者ツール コンソールにのみ表示されます。

エラー :次のエラーで接続が切断されました。"エラー: サーバーが終了時にエラーを返しました:接続はエラーで終了しました。"

SignalR サーバー側のログ Debug または Trace に設定されている場合、サーバー側のログには、メッセージ サイズ エラーの InvalidDataException が表示されます。

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

エラー:

System.IO.InvalidDataException:メッセージの最大サイズ 32,768 B を超えました。 メッセージのサイズは、AddHubOptions で構成できます。

制限値を増やすには、Startup.ConfigureServicesMaximumReceiveMessageSize を設定します。 次の例では、受信メッセージの最大サイズを 64 KB (64 * 1024) に設定します。

services.AddServerSideBlazor()
   .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);

SignalR 受信メッセージ サイズの制限値を増やすと、より多くのサーバー リソースが必要になり、悪意のあるユーザーからのより大きなリスクにサーバーがさらされます。 また、大量のコンテンツを文字列またはバイト配列としてメモリに読み取ると、ガベージ コレクターがうまく機能しない割り当てが発生する可能性もあり、その結果、パフォーマンスがさらに低下します。

大きなペイロードを読み取るための 1 つの選択肢は、小さいチャンクでコンテンツを送信し、ペイロードを Stream として処理することです。 これは、大量の JSON ペイロードを読み取る場合、またはデータを JS で生バイトとして利用できる場合に使用できます。 Blazor Server で大規模なバイナリ ペイロードを送信する、InputFile コンポーネントに似た手法を使用する方法の例については、Binary Submit サンプル アプリを参照してください。

注意

ASP.NET Core 参照ソースへのドキュメント リンクを使用すると、リポジトリの main ブランチが読み込まれます。このブランチは、ASP.NET Core の次回リリースに向けて行われている製品単位の現在の開発を表します。 別のリリースのブランチを選択するには、 [Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使用して、そのブランチを選択します。 たとえば、ASP.NET Core 5.0 リリースの場合は、release/5.0 ブランチを選択します。

Blazor Server アプリで JS と Blazor の間で大量のデータを転送するコードを開発するときは、次のガイダンスを考慮してください。

  • データをより小さな部分にスライスし、すべてのデータがサーバーによって受信されるまでデータ セグメントを順番に送信します。
  • JS および C# コードで大きなオブジェクトを割り当てないでください。
  • データを送受信するときに、メイン UI スレッドを長時間ブロックしないでください。
  • プロセスの完了時またはキャンセル時に、消費していたメモリを解放します。
  • セキュリティ上の理由から、次の追加要件を適用します。
    • 渡すことのできるファイルまたはデータの最大サイズを宣言します。
    • クライアントからサーバーへの最小アップロード レートを宣言します。
  • データがサーバーによって受信されたら、データは:
    • すべてのセグメントが収集されるまで、一時的にメモリ バッファーに格納できます。
    • 直ちに消費できます。 たとえば、データは、データベースに直ちに格納することも、セグメントを受信するたびにディスクに書き込むこともできます。

その他の技術情報