ASP.NET Core Blazor의 JavaScript 함수에서 .NET 메서드 호출
참고 항목
이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
Important
이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.
현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
이 문서에서는 JavaScript(JS)에서 .NET 메서드를 호출하는 방법을 설명합니다.
.NET에서 JS 함수를 호출하는 방법에 대한 자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
정적 .NET 메서드 호출
JavaScript(JS)에서 정적 .NET 메서드를 호출하려면 JS 함수를 사용합니다.
DotNet.invokeMethodAsync
(권장): 서버 쪽 및 클라이언트 쪽 구성 요소 모두에 대한 비동기입니다.DotNet.invokeMethod
: 클라이언트 쪽 구성 요소에 대해서만 동기식입니다.
메서드를 포함하는 어셈블리의 이름, 정적 .NET 메서드 식별자 및 인수를 전달합니다.
다음 예제에서
{ASSEMBLY NAME}
자리 표시자는 앱의 어셈블리 이름입니다.{.NET METHOD ID}
자리 표시자는 .NET 메서드 식별자입니다.{ARGUMENTS}
자리 표시자는 메서드에 전달할 선택적 쉼표로 구분된 인수이며, 각 인수는 JSON 직렬화 가능해야 합니다.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
는 JS Promise
작업의 결과를 나타내는 값을 반환합니다. DotNet.invokeMethod
(클라이언트 쪽 구성 요소)는 작업의 결과를 반환합니다.
Important
서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(invokeMethodAsync
invokeMethod
)를 사용하는 것이 좋습니다.
.NET 메서드는 퍼블릭이고 정적이며 [JSInvokable]
특성이 있어야 합니다.
다음 예제에서
{<T>}
자리 표시자는 값을 반환하는 메서드에만 필요한 반환 형식을 나타냅니다.{.NET METHOD ID}
자리 표시자는 메서드 식별자입니다.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
참고 항목
개방형 제네릭 메서드 호출은 정적 .NET 메서드에서 지원되지 않지만, 인스턴스 메서드에서는 지원됩니다. 자세한 내용은 .NET 제네릭 클래스 메서드 호출 섹션을 참조하세요.
다음 구성 요소에서 C# 메서드는 ReturnArrayAsync
배열을 반환합니다 int
. [JSInvokable]
특성은 메서드에 적용되어 메서드를 JS에서 호출 가능하게 만듭니다.
CallDotnet1.razor
:
@page "/call-dotnet-1"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 1</PageTitle>
<h1>Call .NET Example 1</h1>
<p>
<button id="btn">Trigger .NET static method</button>
</p>
<p>
See the result in the developer tools console.
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet1.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<int[]> ReturnArrayAsync() =>
Task.FromResult(new int[] { 11, 12, 13 });
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
함수는 addHandlers
JS 단추에 click
이벤트를 추가합니다. returnArrayAsync
JS 함수는 처리기로서 할당됩니다.
이 함수는 returnArrayAsync
JS 구성 요소의 ReturnArrayAsync
.NET 메서드를 호출하여 결과를 브라우저의 웹 개발자 도구 콘솔에 기록합니다. BlazorSample
은 앱의 어셈블리 이름입니다.
CallDotnet1.razor
:
@page "/call-dotnet-1"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 1</PageTitle>
<h1>Call .NET Example 1</h1>
<p>
<button id="btn">Trigger .NET static method</button>
</p>
<p>
See the result in the developer tools console.
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet1.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<int[]> ReturnArrayAsync() =>
Task.FromResult(new int[] { 11, 12, 13 });
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotnet1.razor.js
:
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", returnArrayAsync);
}
함수는 addHandlers
JS 단추에 click
이벤트를 추가합니다. returnArrayAsync
JS 함수는 처리기로서 할당됩니다.
이 함수는 returnArrayAsync
JS 구성 요소의 ReturnArrayAsync
.NET 메서드를 호출하여 결과를 브라우저의 웹 개발자 도구 콘솔에 기록합니다. BlazorSample
은 앱의 어셈블리 이름입니다.
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 });
}
}
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 });
}
}
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 });
}
}
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 함수는 구성 요소의 ReturnArrayAsync
.NET 메서드를 호출하여 결과를 브라우저의 웹 개발자 도구 콘솔에 기록합니다. BlazorSample
은 앱의 어셈블리 이름입니다.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
Trigger .NET static method
단추를 선택하면 브라우저의 개발자 도구 콘솔 출력이 배열 데이터를 표시합니다. 출력 형식은 브라우저마다 약간씩 다릅니다. 다음 출력은 Microsoft Edge에서 사용하는 형식을 보여 줍니다.
Array(3) [ 11, 12, 13 ]
함수를 호출 invokeMethodAsync
할 때 데이터를 인수로 전달하여 .NET 메서드에 데이터를 전달합니다.
.NET에 데이터를 전달하는 방법을 보여 주려면 다음에서 JS메서드가 ReturnArrayAsync
호출되는 메서드에 시작 위치를 전달합니다.
export function returnArrayAsync() {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', 14)
.then(data => {
console.log(data);
});
}
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', 14)
.then(data => {
console.log(data);
});
};
</script>
구성 요소의 호출 가능한 ReturnArrayAsync
메서드는 시작 위치를 수신하고 해당 위치에서 배열을 생성합니다. 배열은 콘솔에 로깅하기 위해 반환됩니다.
[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition) =>
Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
앱을 다시 컴파일하고 브라우저를 새로 고친 후 단추를 선택하면 브라우저 콘솔에 다음 출력이 표시됩니다.
Array(3) [ 14, 15, 16 ]
호출에 대한 JS .NET 메서드 식별자는 .NET 메서드 이름이지만 특성 생성자를 사용하여 [JSInvokable]
다른 식별자를 지정할 수 있습니다. 다음 예제에서 DifferentMethodName
은 ReturnArrayAsync
메서드에 대해 할당된 메서드 식별자입니다.
[JSInvokable("DifferentMethodName")]
(서버 쪽 또는 클라이언트 쪽 구성 요소) 또는 DotNet.invokeMethod
(클라이언트 쪽 구성 요소에만 해당)에 대한 호출 DotNet.invokeMethodAsync
에서 .NET 메서드를 ReturnArrayAsync
실행하기 위한 호출DifferentMethodName
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(클라이언트 쪽 구성 요소만 해당)
참고 항목
이 단원의 ReturnArrayAsync
메서드 예제에서는 명시적 C# async
및 await
키워드를 사용하지 않고 Task의 결과를 반환합니다. async
및 await
를 사용하여 메서드를 코딩하는 방식은 await
키워드를 사용하여 비동기 작업의 값을 반환하는 메서드에서 일반적입니다.
async
및 await
키워드로 구성된 ReturnArrayAsync
메서드:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync() =>
await Task.FromResult(new int[] { 11, 12, 13 });
자세한 내용은 C# 가이드에서 async 및 await를 사용한 비동기 프로그래밍을 참조하세요.
.NET에 전달할 JavaScript 개체 및 데이터 참조 만들기
개체 참조를 JS 만드는 데 사용되는 .NET jsObject
에 전달할 수 있도록 개체 참조를 JS 생성하는 호출 DotNet.createJSObjectReference(jsObject)
입니다JS Object
. 다음 예제에서는 직렬화할 수 없는 window
개체에 대한 참조를 .NET에 전달합니다. 이 개체는 ReceiveWindowObject
C# 메서드에서 이를 IJSObjectReference로 받습니다.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject',
DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
...
}
앞의 예시에서 {ASSEMBLY NAME}
자리 표시자는 앱의 네임스페이스입니다.
참고 항목
앞의 예제에서는 개체에 JSObjectReference
대한 참조 window
가 저장 JS되지 않으므로 삭제할 필요가 없습니다.
참조 JSObjectReference
를 유지 관리하려면 클라이언트에서 메모리가 누출 JS 되는 것을 방지하기 위해 삭제해야 합니다. 다음 예제에서는 참조를 캡처 JSObjectReference
하기 위해 앞의 코드를 리팩터링한 다음 참조를 삭제하기 위한 DotNet.disposeJSObjectReference()
호출을 수행합니다.
var jsObjectReference = DotNet.createJSObjectReference(window);
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);
DotNet.disposeJSObjectReference(jsObjectReference);
앞의 예시에서 {ASSEMBLY NAME}
자리 표시자는 앱의 네임스페이스입니다.
DotNet.createJSStreamReference(streamReference)
을 호출하여 JS 스트림 참조를 .NET에 전달할 수 있도록 합니다. 여기서 streamReference
은 ArrayBuffer
, Blob
또는 JS 스트림 참조를 만드는 데 사용되는 형식화된 배열(예: Uint8Array
또는 Float32Array
)입니다.
인스턴스 .NET 메서드 호출
JavaScript(JS)에서 인스턴스 .NET 메서드를 호출하려면
DotNetObjectReference에서 인스턴스를 래핑하고 Create를 호출하여 .NET 인스턴스를 JS에 대한 참조로 전달합니다.
전달DotNetObjectReference된 인스턴스에서 JS .NET 인스턴스 메서드를 사용하여
invokeMethodAsync
(권장) 또는invokeMethod
(클라이언트 쪽 구성 요소만) 호출합니다. 인스턴스 .NET 메서드 및 인수의 식별자를 전달합니다. JS에서 다른 .NET 메서드를 호출할 때 .NET 인스턴스를 인수로 전달할 수도 있습니다.다음 예제에서
dotNetHelper
는 .입니다 DotNetObjectReference.{.NET METHOD ID}
자리 표시자는 .NET 메서드 식별자입니다.{ARGUMENTS}
자리 표시자는 메서드에 전달할 선택적 쉼표로 구분된 인수이며, 각 인수는 JSON 직렬화 가능해야 합니다.
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
참고 항목
invokeMethodAsync
인스턴스invokeMethod
메서드를 호출할 때 어셈블리 이름 매개 변수를 허용하지 않습니다.invokeMethodAsync
는 JSPromise
작업의 결과를 나타내는 값을 반환합니다.invokeMethod
(클라이언트 쪽 구성 요소만) 작업의 결과를 반환합니다.Important
서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(
invokeMethodAsync
invokeMethod
)를 사용하는 것이 좋습니다.DotNetObjectReference를 삭제합니다.
이 문서의 다음 섹션에서는 인스턴스 .NET 메서드를 호출하는 다양한 방법을 보여 줍니다.
JavaScript 호출 가능한 .NET 메서드를 트리밍하지 않습니다.
이 섹션은 AOT(미리 실행) 컴파일 및 런타임 다시 연결을 사용하도록 설정된 클라이언트 쪽 앱에 적용됩니다.
다음 섹션의 몇 가지 예제는 클래스 인스턴스 접근 방식을 기반으로 합니다. 여기서 특성으로 [JSInvokable]
표시된 JavaScript 호출 가능한 .NET 메서드는 구성 요소가 아닌 Razor 클래스의 멤버입니다. 이러한 .NET 메서드가 구성 요소에 Razor 있는 경우 런타임 다시 연결/트리밍으로부터 보호됩니다. .NET 메서드가 구성 요소 외부에서 Razor 트리밍되는 것을 방지하려면 다음 예제와 DynamicDependency
같이 클래스 생성자의 특성을 사용하여 메서드를 구현합니다.
using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;
public class ExampleClass {
[DynamicDependency(nameof(ExampleJSInvokableMethod))]
public ExampleClass()
{
}
[JSInvokable]
public string ExampleJSInvokableMethod()
{
...
}
}
자세한 내용은 트리밍을 위한 .NET 라이브러리 준비: DynamicDependency를 참조 하세요.
개별 JavaScript 함수에 DotNetObjectReference
전달
이 섹션의 예제에서는 개별 JS(JavaScript) 함수에 DotNetObjectReference를 전달하는 방법을 보여 줍니다.
다음 sayHello1
JS 함수는 DotNetObjectReference를 수신하고 invokeMethodAsync
를 호출하여 구성 요소의 GetHelloMessage
.NET 메서드를 호출합니다.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
다음 구성 요소:
- 구성 요소에는
GetHelloMessage
라는 JS 호출 가능 .NET 메서드가 있습니다. Trigger .NET instance method
단추를 선택하면 JS 함수sayHello1
은 DotNetObjectReference를 사용하여 호출됩니다.sayHello1
:GetHelloMessage
를 호출하고 메시지 결과를 받습니다.- 호출하는
TriggerDotNetInstanceMethod
메서드에 메시지 결과를 반환합니다.
result
의sayHello1
에서 반환된 메시지는 사용자에게 표시됩니다.- 메모리 누수를 방지하고 가비지 수집을 허용하기 위해 DotNetObjectReference에서 만든 .NET 개체 참조는
Dispose
메서드에서 삭제됩니다.
CallDotnet2.razor
:
@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 2</PageTitle>
<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<CallDotnet2>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello1", objRef);
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose() => objRef?.Dispose();
}
CallDotnet2.razor
:
@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 2</PageTitle>
<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<CallDotnet2>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello1", objRef);
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose() => objRef?.Dispose();
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
public void Dispose()
{
objRef?.Dispose();
}
}
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
다음 지침을 사용하여 인스턴스 메서드에 인수를 전달합니다.
.NET 메서드 호출에 매개 변수를 추가합니다. 다음 예제에서는 이름이 메서드에 전달됩니다. 필요에 따라 목록에 추가 매개 변수를 추가합니다.
<script>
window.sayHello2 = (dotNetHelper, name) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
};
</script>
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
.NET 메서드에 매개 변수 목록을 제공합니다.
CallDotnet3.razor
:
@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 3</PageTitle>
<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<CallDotnet3>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose() => objRef?.Dispose();
}
CallDotnet3.razor
:
@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 3</PageTitle>
<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<CallDotnet3>? objRef;
protected override void OnInitialized() =>
objRef = DotNetObjectReference.Create(this);
public async Task TriggerDotNetInstanceMethod() =>
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose() => objRef?.Dispose();
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
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;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
public async Task TriggerDotNetInstanceMethod()
{
result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
}
[JSInvokable]
public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";
public void Dispose()
{
objRef?.Dispose();
}
}
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
여러 JavaScript 함수가 있는 클래스에 DotNetObjectReference
전달
이 섹션의 예제에서는 여러 함수가 있는 JS(JavaScript) 클래스에 DotNetObjectReference를 전달하는 방법을 보여 줍니다.
DotNetObjectReference를 만들고 OnAfterRenderAsync
수명 주기 메서드에서 사용할 여러 함수의 JS 클래스로 전달합니다. 다음 예제와 같이 .NET 코드가 DotNetObjectReference를 삭제하는지 확인합니다.
다음 구성 요소에서 단추는 Trigger JS function
's @onclick
지시문 특성이 JSonclick
아닌 Blazor속성을 설정하여 함수를 호출 JS 합니다.
CallDotNetExampleOneHelper.razor
:
@page "/call-dotnet-example-one-helper"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET Example</PageTitle>
<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>
<p>
<label>
Message: <input @bind="name" />
</label>
</p>
<p>
<button id="sayHelloBtn">
Trigger JS function <code>sayHello</code>
</button>
</p>
<p>
<button id="welcomeVisitorBtn">
Trigger JS function <code>welcomeVisitor</code>
</button>
</p>
@code {
private IJSObjectReference? module;
private string? name;
private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotNetExampleOneHelper.razor.js");
dotNetHelper = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("GreetingHelpers.setDotNetHelper",
dotNetHelper);
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
[JSInvokable]
public string GetWelcomeMessage() => $"Welcome, {name}!";
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
dotNetHelper?.Dispose();
}
}
앞의 예에서:
JS
는 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.- 변수 이름
dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다. - 구성 요소는 가비지 수집을 허용하고 메모리 누수 방지를 위해 DotNetObjectReference를 명시적으로 삭제해야 합니다.
CallDotNetExampleOneHelper.razor.js
:
export class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
export function addHandlers() {
const sayHelloBtn = document.getElementById("sayHelloBtn");
sayHelloBtn.addEventListener("click", GreetingHelpers.sayHello);
const welcomeVisitorBtn = document.getElementById("welcomeVisitorBtn");
welcomeVisitorBtn.addEventListener("click", GreetingHelpers.welcomeVisitor);
}
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS
<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>
<p>
<label>
Message: <input @bind="name" />
</label>
</p>
<p>
<button onclick="GreetingHelpers.sayHello()">
Trigger JS function <code>sayHello</code>
</button>
</p>
<p>
<button onclick="GreetingHelpers.welcomeVisitor()">
Trigger JS function <code>welcomeVisitor</code>
</button>
</p>
@code {
private string? name;
private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper",
dotNetHelper);
}
}
[JSInvokable]
public string GetHelloMessage() => $"Hello, {name}!";
[JSInvokable]
public string GetWelcomeMessage() => $"Welcome, {name}!";
public void Dispose()
{
dotNetHelper?.Dispose();
}
}
앞의 예에서:
JS
는 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.- 변수 이름
dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다. - 구성 요소는 가비지 수집을 허용하고 메모리 누수 방지를 위해 DotNetObjectReference를 명시적으로 삭제해야 합니다.
<script>
class GreetingHelpers {
static dotNetHelper;
static setDotNetHelper(value) {
GreetingHelpers.dotNetHelper = value;
}
static async sayHello() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
alert(`Message from .NET: "${msg}"`);
}
static async welcomeVisitor() {
const msg =
await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
alert(`Message from .NET: "${msg}"`);
}
}
window.GreetingHelpers = GreetingHelpers;
</script>
앞의 예에서:
GreetingHelpers
클래스는 Blazor가 JS interop에 대한 클래스를 찾을 수 있도록 하는 클래스를 전역적으로 정의하기 위해window
개체에 추가됩니다.- 변수 이름
dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
.NET 제네릭 클래스 메서드 호출
JS(JavaScript) 함수는 .NET 제네릭 클래스 메서드를 호출할 수 있습니다. 여기서 JS 함수는 제네릭 클래스의 .NET 메서드를 호출합니다.
다음 제네릭 형식 클래스(GenericType<TValue>
)에서:
- 클래스에는 단일 제네릭
Value
속성이 있는 단일 형식 매개 변수(TValue
)가 있습니다. - 클래스에는 특성으로 표시된 두 개의 제네릭이 아닌 메서드가
[JSInvokable]
있으며, 각각 제네릭 형식 매개 변수가 다음과 같습니다newValue
.Update
는newValue
의Value
값을 동기적으로 업데이트합니다.UpdateAsync
는 대기할 때 현재 컨텍스트로 비동기적으로 전환되는 Task.Yield로 대기할 수 있는 작업을 만든 후newValue
의Value
값을 비동기적으로 업데이트합니다.
- 각 클래스 메서드는 콘솔에
TValue
형식 및Value
값을 씁니다. 콘솔에 쓰는 것은 데모용으로만 제공됩니다. 프로덕션 앱은 일반적으로 앱 로깅을 위해 콘솔에 쓰지 않습니다. 자세한 내용은 ASP.NET Core Blazor 로깅과 .NET Core 및 ASP.NET Core의 로깅을 참조하세요.
참고 항목
열린 제네릭 형식 및 메서드 는 형식 자리 표시자에 대한 형식을 지정하지 않습니다. 반대로 닫힌 제네릭은 모든 형식 자리 표시자에 대해 형식을 제공합니다. 이 섹션의 예제에서는 닫힌 제네릭을 보여 주지만 개방형 제네릭을 사용하여 interop 인스턴스 메서드를 호출하는 JS 것이 지원됩니다. 이 문서의 앞부분에서 설명한 정적 .NET 메서드 호출에는 개방형 제네릭을 사용할 수 없습니다.
자세한 내용은 다음 문서를 참조하세요.
GenericType.cs
:
using Microsoft.JSInterop;
public class GenericType<TValue>
{
public TValue? Value { get; set; }
[JSInvokable]
public void Update(TValue newValue)
{
Value = newValue;
Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}");
}
[JSInvokable]
public async void UpdateAsync(TValue newValue)
{
await Task.Yield();
Value = newValue;
Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
}
}
다음 invokeMethodsAsync
함수에서
- 제네릭 형식 클래스의
Update
및UpdateAsync
메서드는 문자열과 숫자를 나타내는 인수를 사용하여 호출됩니다. - 클라이언트 쪽 구성 요소는 .NET 메서드를 .NET 메서드와
invokeMethod
동기적으로 호출할 수 있습니다.syncInterop
는 클라이언트에서 interop이 JS 발생하는지 여부를 나타내는 부울 값을 받습니다.syncInterop
이true
인 경우invokeMethod
는 안전하게 호출됩니다. 값syncInterop
이면false
interop이 서버 쪽 구성 요소에서 실행되므로 비동기 함수invokeMethodAsync
만 호출 JS 됩니다. - 데모용으로 DotNetObjectReference 함수 호출(
invokeMethod
또는invokeMethodAsync
), 호출된 .NET 메서드(Update
또는UpdateAsync
), 인수가 콘솔에 기록됩니다. 인수는 난수를 사용하여 .NET 메서드 호출에 JS 함수 호출을 일치시키도록 허용합니다(.NET 쪽의 콘솔에도 기록됨). 프로덕션 코드는 일반적으로 클라이언트 또는 서버의 콘솔에 쓰지 않습니다. 프로덕션 앱은 일반적으로 앱 로깅을 사용합니다. 자세한 내용은 ASP.NET Core Blazor 로깅과 .NET Core 및 ASP.NET Core의 로깅을 참조하세요.
<script>
const randomInt = () => Math.floor(Math.random() * 99999);
window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
var n = randomInt();
console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);
n = randomInt();
console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);
if (syncInterop) {
n = randomInt();
console.log(`JS: invokeMethod:Update('string ${n}')`);
dotNetHelper1.invokeMethod('Update', `string ${n}`);
}
n = randomInt();
console.log(`JS: invokeMethodAsync:Update(${n})`);
await dotNetHelper2.invokeMethodAsync('Update', n);
n = randomInt();
console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);
if (syncInterop) {
n = randomInt();
console.log(`JS: invokeMethod:Update(${n})`);
dotNetHelper2.invokeMethod('Update', n);
}
};
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
다음은 GenericsExample
구성 요소에 대한 설명입니다.
Invoke Interop
단추가 선택되면 JS 함수invokeMethodsAsync
가 호출됩니다.- DotNetObjectReference 형식 쌍이 만들어지고
GenericType
인스턴스에 대한 JS 함수에string
및int
로 전달됩니다.
GenericsExample.razor
:
@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS
<p>
<button @onclick="InvokeInterop">Invoke Interop</button>
</p>
<ul>
<li>genericType1: @genericType1?.Value</li>
<li>genericType2: @genericType2?.Value</li>
</ul>
@code {
private GenericType<string> genericType1 = new() { Value = "string 0" };
private GenericType<int> genericType2 = new() { Value = 0 };
private DotNetObjectReference<GenericType<string>>? objRef1;
private DotNetObjectReference<GenericType<int>>? objRef2;
protected override void OnInitialized()
{
objRef1 = DotNetObjectReference.Create(genericType1);
objRef2 = DotNetObjectReference.Create(genericType2);
}
public async Task InvokeInterop()
{
var syncInterop = OperatingSystem.IsBrowser();
await JS.InvokeVoidAsync(
"invokeMethodsAsync", syncInterop, objRef1, objRef2);
}
public void Dispose()
{
objRef1?.Dispose();
objRef2?.Dispose();
}
}
@page "/generics-example"
@implements IDisposable
@inject IJSRuntime JS
<p>
<button @onclick="InvokeInterop">Invoke Interop</button>
</p>
<ul>
<li>genericType1: @genericType1?.Value</li>
<li>genericType2: @genericType2?.Value</li>
</ul>
@code {
private GenericType<string> genericType1 = new() { Value = "string 0" };
private GenericType<int> genericType2 = new() { Value = 0 };
private DotNetObjectReference<GenericType<string>>? objRef1;
private DotNetObjectReference<GenericType<int>>? objRef2;
protected override void OnInitialized()
{
objRef1 = DotNetObjectReference.Create(genericType1);
objRef2 = DotNetObjectReference.Create(genericType2);
}
public async Task InvokeInterop()
{
var syncInterop = OperatingSystem.IsBrowser();
await JS.InvokeVoidAsync(
"invokeMethodsAsync", syncInterop, objRef1, objRef2);
}
public void Dispose()
{
objRef1?.Dispose();
objRef2?.Dispose();
}
}
앞의 예제에서 JS
은(는) 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.
다음은 클라이언트 쪽 구성 요소에서 단추를 선택할 때 이전 예제의 Invoke Interop
일반적인 출력을 보여 줍니다.
JS: invokeMethodAsync:Update('string 37802')
.NET: Update: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Update: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Update: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: string 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995
앞의 예제가 서버 쪽 구성 요소에서 구현되는 경우 동기 호출 invokeMethod
은 방지됩니다. 서버 쪽 구성 요소의 경우 동기 버전()에 대해 비동기 함수(invokeMethodAsync
invokeMethod
)를 사용하는 것이 좋습니다.
서버 쪽 구성 요소의 일반적인 출력:
JS: invokeMethodAsync:Update('string 34809')
.NET: Update: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: string 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652
앞의 출력 예제에서는 스레드 예약 및 메서드 실행 속도를 비롯한 여러 요인에 따라 비동기 메서드가 임의 순서로 실행되고 완료되는 것을 보여 줍니다. 비동기 메서드 호출의 완료 순서를 안정적으로 예측할 수는 없습니다.
클래스 인스턴스 예제
다음 sayHello1
JS 함수는 다음 작업을 수행합니다.
- 전달된 DotNetObjectReference에 대해
GetHelloMessage
.NET 메서드를 호출합니다. GetHelloMessage
에서sayHello1
호출자로 메시지를 반환합니다.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
다음 HelloHelper
클래스에는 GetHelloMessage
이라는 JS 호출 가능 .NET 메서드가 있습니다. HelloHelper
가 만들어지면 Name
속성의 이름이 GetHelloMessage
에서 메시지를 반환하는 데 사용됩니다.
HelloHelper.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class HelloHelper(string? name)
{
public string? Name { get; set; } = name ?? "No Name";
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class HelloHelper(string? name)
{
public string? Name { get; set; } = name ?? "No Name";
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string? name)
{
Name = name ?? "No Name";
}
public string? Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string? name)
{
Name = name ?? "No Name";
}
public string? Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;
public class HelloHelper
{
public HelloHelper(string name)
{
Name = name;
}
public string Name { get; set; }
[JSInvokable]
public string GetHelloMessage() => $"Hello, {Name}!";
}
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 Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses3(IJSRuntime js)
{
private readonly IJSRuntime js = js;
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses3(IJSRuntime js)
{
private readonly IJSRuntime js = js;
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses3
{
private readonly IJSRuntime js;
public JsInteropClasses3(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
return await js.InvokeAsync<string>("sayHello1", objRef);
}
}
메모리 누수 및 가비지 수집을 허용하기 위해 개체 참조가 using var
구문으로 범위를 벗어날 때, DotNetObjectReference에서 만든 .NET 개체 참조가 삭제됩니다.
Trigger .NET instance method
다음 구성 요소 JsInteropClasses3.CallHelloHelperGetHelloMessage
에서 단추를 선택하면 값name
으로 호출됩니다.
CallDotnet4.razor
:
@page "/call-dotnet-4"
@inject IJSRuntime JS
<PageTitle>Call .NET 4</PageTitle>
<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;
protected override void OnInitialized() =>
jsInteropClasses = new JsInteropClasses3(JS);
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotnet4.razor
:
@page "/call-dotnet-4"
@inject IJSRuntime JS
<PageTitle>Call .NET 4</PageTitle>
<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;
protected override void OnInitialized() =>
jsInteropClasses = new JsInteropClasses3(JS);
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@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;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@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;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
if (jsInteropClasses is not null)
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@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;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
CallDotNetExample4.razor
:
@page "/call-dotnet-example-4"
@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;
protected override void OnInitialized()
{
jsInteropClasses = new JsInteropClasses3(JS);
}
private async Task TriggerDotNetInstanceMethod()
{
result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
}
}
다음 그림은 Name
필드에 이름 Amy Pond
가 있는 렌더링된 구성 요소를 보여 줍니다. 이 단추를 선택하면 Hello, Amy Pond!
가 UI에 표시됩니다.
JsInteropClasses3
클래스에 표시된 이전 패턴은 구성 요소에서 완전히 구현될 수도 있습니다.
CallDotnet5.razor
:
@page "/call-dotnet-5"
@inject IJSRuntime JS
<PageTitle>Call .NET 5</PageTitle>
<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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotnet5.razor
:
@page "/call-dotnet-5"
@inject IJSRuntime JS
<PageTitle>Call .NET 5</PageTitle>
<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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
CallDotNetExample5.razor
:
@page "/call-dotnet-example-5"
@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;
public async Task TriggerDotNetInstanceMethod()
{
using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
result = await JS.InvokeAsync<string>("sayHello1", objRef);
}
}
메모리 누수 및 가비지 수집을 허용하기 위해 개체 참조가 using var
구문으로 범위를 벗어날 때, DotNetObjectReference에서 만든 .NET 개체 참조가 삭제됩니다.
구성 요소에서 표시하는 출력은 Hello, Amy Pond!
필드에 이름이 Amy Pond
제공될 name
때입니다.
위의 구성 요소에서 .NET 개체 참조는 삭제됩니다. 클래스 또는 구성 요소가 DotNetObjectReference를 삭제하지 않으면 전달된 DotNetObjectReference에서 dispose
를 호출하여 클라이언트에서 삭제합니다.
window.{JS FUNCTION NAME} = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
dotNetHelper.dispose();
}
앞의 예에서:
{JS FUNCTION NAME}
자리 표시자는 JS 함수의 이름입니다.- 변수 이름
dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다. {.NET METHOD ID}
자리 표시자는 .NET 메서드 식별자입니다.
구성 요소 인스턴스 .NET 메서드 도우미 클래스
도우미 클래스는 .NET 인스턴스 메서드를 Action으로 호출할 수 있습니다. 도우미 클래스는 정적 .NET 메서드를 사용할 수 없는 시나리오에서 유용합니다.
- 동일한 형식의 여러 구성 요소가 동일한 페이지에 렌더링되는 경우
- 여러 사용자가 동시에 동일한 구성 요소를 사용하는 서버 쪽 앱에서.
다음 예제에서
- 구성 요소에는 여러
ListItem1
구성 요소가 포함되어 있습니다. - 각
ListItem1
구성 요소는 메시지와 단추로 구성됩니다. ListItem1
구성 요소 단추를 선택하면 해당ListItem1
의UpdateMessage
메서드가 목록 항목 텍스트를 변경하고 단추를 숨깁니다.
다음 MessageUpdateInvokeHelper
클래스는 클래스를 인스턴스화할 때 지정된 Action을 호출하기 위해 JS 호출 가능 .NET 메서드인 UpdateMessageCaller
를 유지 관리합니다.
MessageUpdateInvokeHelper.cs
:
using Microsoft.JSInterop;
namespace BlazorSample;
public class MessageUpdateInvokeHelper(Action action)
{
private readonly Action action = action;
[JSInvokable]
public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class MessageUpdateInvokeHelper(Action action)
{
private readonly Action action = action;
[JSInvokable]
public void UpdateMessageCaller() => action.Invoke();
}
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using System;
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
using System;
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
다음 updateMessageCaller
JS 함수는 UpdateMessageCaller
.NET 메서드를 호출합니다.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
다음 ListItem1
구성 요소는 부모 구성 요소에서 여러 번 사용할 수 있는 공유 구성 요소로, HTML 목록(<ul>...</ul>
또는 <ol>...</ol>
)에 대해 목록 항목(<li>...</li>
)을 만듭니다. 각 ListItem1
구성 요소 인스턴스는 Action이 해당 UpdateMessage
메서드로 설정된 MessageUpdateInvokeHelper
의 인스턴스를 설정합니다.
ListItem1
구성 요소의 InteropCall
단추를 선택하면 MessageUpdateInvokeHelper
인스턴스에 대해 생성된 DotNetObjectReference를 사용하여 updateMessageCaller
가 호출됩니다. 이렇게 하면 프레임워크가 해당 ListItem1
의 MessageUpdateInvokeHelper
인스턴스에서 UpdateMessageCaller
를 호출할 수 있습니다. 전달된 DotNetObjectReference는 JS(dotNetHelper.dispose()
)에서 삭제됩니다.
ListItem1.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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
@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();
}
}
@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();
}
}
message
가 UpdateMessage
에 설정된 경우 UI를 업데이트하기 위해 StateHasChanged
가 호출됩니다. StateHasChanged
가 호출되지 않은 경우에는 Action이 호출될 때 UI가 업데이트되어야 한다는 것을 Blazor에서 알 수 없습니다.
다음 부모 구성 요소에는 각각 구성 요소의 인스턴스인 4개의 목록 항목이 ListItem1
포함됩니다.
CallDotnet6.razor
:
@page "/call-dotnet-6"
<PageTitle>Call .NET 6</PageTitle>
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotnet6.razor
:
@page "/call-dotnet-6"
<PageTitle>Call .NET 6</PageTitle>
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem1 />
<ListItem1 />
<ListItem1 />
<ListItem1 />
</ul>
다음 이미지는 두 번째 InteropCall
단추를 선택한 후 렌더링된 부모 구성 요소를 보여 줍니다.
- 두 번째
ListItem1
구성 요소는UpdateMessage Called!
메시지를 표시했습니다. InteropCall
단추의 CSSdisplay
속성이none
으로 설정되어 있기 때문에 두 번째ListItem1
구성 요소에 대한 단추는 표시되지 않습니다.
요소 속성에 할당된 DotNetObjectReference
에서 호출한 구성 요소 인스턴스 .NET 메서드
DotNetObjectReference를 HTML 요소의 속성에 할당하면 구성 요소 인스턴스에서 .NET 메서드를 호출할 수 있습니다.
- 요소 참조가 캡처됩니다(ElementReference).
- 구성 요소의
OnAfterRender{Async}
메서드에서 JavaScript(JS) 함수는 요소 참조 및 구성 요소 인스턴스와 함께 DotNetObjectReference로 호출됩니다. JS 함수는 속성의 DotNetObjectReference 요소에 연결됩니다. - JS에서 요소 이벤트가 호출될 때(예:
onclick
)는 요소의 연결된 DotNetObjectReference를 사용하여 .NET 메서드를 호출합니다.
구성 요소 인스턴스 .NET 메서드 도우미 클래스 섹션에 설명된 방법과 마찬가지로 이 방법은 정적 .NET 메서드를 사용할 수 없는 시나리오에서 유용합니다.
- 동일한 형식의 여러 구성 요소가 동일한 페이지에 렌더링되는 경우
- 여러 사용자가 동시에 동일한 구성 요소를 사용하는 서버 쪽 앱에서.
- .NET 메서드는 JS 이벤트(예:
onclick
)가 아닌 Blazor 이벤트(예:@onclick
)에서 호출됩니다.
다음 예제에서
- 구성 요소에는 공유 구성 요소인 여러
ListItem2
구성 요소가 포함되어 있습니다. - 각
ListItem2
구성 요소는<span>
목록 항목 메시지와display
CSS 속성이inline-block
으로 설정된 두 번째<span>
로 구성됩니다. ListItem2
구성 요소 목록 항목이 선택된 경우,ListItem2
의UpdateMessage
메서드는display
속성을none
으로 설정하여 첫 번째<span>
의 목록 항목 텍스트를 변경하고 두 번째<span>
를 숨깁니다.
다음 assignDotNetHelper
JS 함수는 명명dotNetHelper
된 속성의 DotNetObjectReference 요소에 할당합니다. 다음 interopCall
JS 함수 DotNetObjectReference 는 전달된 요소에 대해 .NET 메서드 UpdateMessage
를 호출합니다.
ListItem2.razor.js
:
export function assignDotNetHelper(element, dotNetHelper) {
element.dotNetHelper = dotNetHelper;
}
export async function interopCall(element) {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
ListItem2.razor.js
:
export function assignDotNetHelper(element, dotNetHelper) {
element.dotNetHelper = dotNetHelper;
}
export async function interopCall(element) {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
<script>
window.assignDotNetHelper = (element, dotNetHelper) => {
element.dotNetHelper = dotNetHelper;
}
window.interopCall = async (element) => {
await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
}
</script>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
앞의 예제에서 변수 이름 dotNetHelper
는 임의이며 원하는 이름으로 변경할 수 있습니다.
다음 ListItem2
구성 요소는 부모 구성 요소에서 여러 번 사용할 수 있는 공유 구성 요소로, HTML 목록(<ul>...</ul>
또는 <ol>...</ol>
)에 대해 목록 항목(<li>...</li>
)을 만듭니다.
각 ListItem2
구성 요소 인스턴스는 요소 참조(목록 항목의 첫 번째 <span>
요소)와 구성 요소 인스턴스를 사용하여 OnAfterRenderAsync
에서 assignDotNetHelper
JS 함수를 DotNetObjectReference로 호출합니다.
ListItem2
구성 요소의 메시지인 <span>
가 선택된 경우, interopCall
이 호출되어 <span>
요소를 매개 변수(this
)로서 전달하며 이 매개 변수가 UpdateMessage
.NET 메서드를 호출합니다. UpdateMessage
에서는 message
가 설정되고 두 번째 <span>
의 display
속성이 업데이트된 경우 StateHasChanged
가 호출되어 UI를 업데이트합니다. StateHasChanged
가 호출되지 않으면 메서드가 호출될 때 UI가 업데이트되어야 한다는 것을 Blazor에서 알 수 없습니다.
구성 요소가 삭제되면 DotNetObjectReference가 삭제됩니다.
ListItem2.razor
:
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async void CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
@implements IAsyncDisposable
<li>
<span style="font-weight:bold;color:@color" @ref="elementRef"
@onclick="CallJSToInvokeDotnet">
@message
</span>
<span style="display:@display">
Not Updated Yet!
</span>
</li>
@code {
private IJSObjectReference? module;
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
private string color = "initial";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/ListItem2.razor.js");
objRef = DotNetObjectReference.Create(this);
await module.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
public async void CallJSToInvokeDotnet()
{
if (module is not null)
{
await module.InvokeVoidAsync("interopCall", elementRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
color = "MediumSeaGreen";
StateHasChanged();
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
objRef?.Dispose();
}
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2>? objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS
<li>
<span @ref="elementRef" onclick="interopCall(this)">@message</span>
<span style="display:@display">Not Updated Yet!</span>
</li>
@code {
private DotNetObjectReference<ListItem2> objRef;
private ElementReference elementRef;
private string display = "inline-block";
private string message = "Select one of these list items.";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
objRef = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
}
}
[JSInvokable]
public void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
public void Dispose() => objRef?.Dispose();
}
다음 부모 구성 요소에는 각각 구성 요소의 인스턴스인 4개의 목록 항목이 ListItem2
포함됩니다.
CallDotnet7.razor
:
@page "/call-dotnet-7"
<PageTitle>Call .NET 7</PageTitle>
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotnet7.razor
:
@page "/call-dotnet-7"
<PageTitle>Call .NET 7</PageTitle>
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
CallDotNetExample7.razor
:
@page "/call-dotnet-example-7"
<h1>Call .NET Example 7</h1>
<ul>
<ListItem2 />
<ListItem2 />
<ListItem2 />
<ListItem2 />
</ul>
클라이언트 쪽 구성 요소의 동기 JS interop
이 섹션은 클라이언트 쪽 구성 요소에만 적용됩니다.
JS interop 호출은 호출된 코드가 동기 또는 비동기인지 여부에 관계없이 비동기입니다. 호출은 구성 요소가 서버 쪽 및 클라이언트 쪽 렌더링 모드에서 호환되도록 비동기적입니다. 서버에서 모든 JS interop 호출은 네트워크 연결을 통해 전송되므로 비동기적이어야 합니다.
구성 요소가 WebAssembly에서만 실행된다는 것을 확실히 알고 있는 경우 동기 JS interop 호출을 하도록 선택할 수 있습니다. 이렇게 하면 비동기 호출을 수행하는 것보다 오버헤드가 약간 감소하며 결과를 기다리는 동안 중간 상태가 없기 때문에 렌더링 주기가 감소할 수 있습니다.
클라이언트 쪽 구성 요소에서 JavaScript에서 .NET으로 동기 호출을 하려면 대신 사용합니다 DotNet.invokeMethod
DotNet.invokeMethodAsync
.
동기 호출이 작동하는 경우는 다음과 같습니다.
JavaScript 위치
JavaScript 위치에 대한 문서에서 설명하는 방법을 사용하여 JavaScript(JS) 코드를 로드합니다.
- 태그에
<head>
스크립트 로드(일반적으로 권장되지 않음) <body>
태그에서 스크립트 로드- 구성 요소와 함께 배치된 외부 JavaScript 파일(
.js
)에서 스크립트 로드 - 외부 JavaScript 파일(
.js
)에서 스크립트 로드 - Blazor 시작 전후 스크립트 삽입
- 태그에
<head>
스크립트 로드(일반적으로 권장되지 않음) <body>
태그에서 스크립트 로드- 외부 JavaScript 파일(
.js
)에서 스크립트 로드 - Blazor 시작 전후 스크립트 삽입
모듈을 사용하여 JS 로드 JS 하는 방법은 JavaScript 모듈 섹션의 JavaScript 격리에 있는 이 문서에 설명되어 있습니다 .
Warning
태그를 <script>
동적으로 업데이트할 수 없으므로 구성 요소가 정적 서버 쪽 렌더링(정적 SSR)을 채택하도록 보장되는 경우에만 구성 요소 파일.razor
()에 태그를 <script>
배치합니다.
Warning
<script>
태그를 동적으로 업데이트할 수 없으므로 구성 요소 파일(.razor
)에 <script>
태그를 넣지 마세요.
JavaScript 모듈에서의 JavaScript 격리
Blazor에서는 표준 JavaScript 모듈(ECMAScript 사양)에서 JavaScript(JS) 격리를 사용하도록 설정합니다. JavaScript 모듈 로드는 다른 유형의 웹앱과 Blazor 동일한 방식으로 작동하며, 앱에서 모듈을 정의하는 방법을 자유롭게 사용자 지정할 수 있습니다. JavaScript 모듈을 사용하는 방법에 대한 가이드는 MDN 웹 문서: JavaScript 모듈을 참조 하세요.
JS 격리는 다음과 같은 이점을 제공합니다.
- 가져온 JS는 전역 네임스페이스를 더 이상 오염시키지 않습니다.
- 라이브러리 및 구성 요소의 소비자는 관련 JS를 가져올 필요가 없습니다.
자세한 내용은 ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출을 참조하세요.
연산자를 사용하여 import()
동적 가져오기는 ASP.NET Core 및 Blazor다음에서 지원됩니다.
if ({CONDITION}) import("/additionalModule.js");
앞의 예제 {CONDITION}
에서 자리 표시자는 모듈을 로드해야 하는지 여부를 결정하는 조건부 검사를 나타냅니다.
브라우저 호환성을 보려면 다음을 사용할 수 있습니다. JavaScript 모듈: 동적 가져오기를 참조하세요.
순환 개체 참조 방지
순환 참조를 포함하는 개체는 다음 중 하나에 대해 클라이언트에서 직렬화될 수 없습니다.
- .NET 메서드 호출
- 반환 형식에 순환 참조가 있는 경우 C#에서 JavaScript 메서드 호출
바이트 배열 지원
Blazor는 최적화된 바이트 배열 JS(JavaScript) interop을 지원하여 바이트 배열이 Base64로 인코딩/디코딩되는 것을 방지합니다. 다음 예제에서는 JS interop을 사용하여 바이트 배열을 .NET에 전달합니다.
sendByteArray
JS 함수를 제공합니다. 함수는 구성 요소의 단추에 의해 호출에 invokeMethodAsync
어셈블리 이름 매개 변수를 포함하고 값을 반환하지 않는 정적으로 호출됩니다.
CallDotnet8.razor.js
:
export function 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, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e]);
DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
.then(str => {
alert(str);
});
}
export function addHandlers() {
const btn = document.getElementById("btn");
btn.addEventListener("click", sendByteArray);
}
<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,0x61,0x69,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>
참고 항목
위치 및 프로덕션 앱에 JS 대한 권장 사항에 대한 일반적인 지침은 ASP.NET Core Blazor 앱의 JavaScript 위치를 참조하세요.
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotnet8.razor
:
@page "/call-dotnet-8"
@using System.Text
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button id="btn">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private IJSObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/Pages/CallDotnet8.razor.js");
await module.InvokeVoidAsync("addHandlers");
}
}
[JSInvokable]
public static Task<string> ReceiveByteArray(byte[] receivedBytes) =>
Task.FromResult(Encoding.UTF8.GetString(receivedBytes, 0,
receivedBytes.Length));
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<PageTitle>Call .NET 8</PageTitle>
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">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));
}
}
CallDotNetExample8.razor
:
@page "/call-dotnet-example-8"
@using System.Text
<h1>Call .NET Example 8</h1>
<p>
<button onclick="sendByteArray()">Send Bytes</button>
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">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에서 .NET로 스트리밍
Blazor는 JavaScript에서 .NET으로 직접 데이터를 스트리밍하는 것을 지원합니다. Microsoft.JSInterop.IJSStreamReference
인터페이스를 사용하여 스트림을 요청합니다.
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
는 Stream을 반환하고 다음 매개 변수를 사용합니다.
maxAllowedSize
: JavaScript에서 읽기 작업에 허용된 최대 바이트 수이며, 지정하지 않으면 기본적으로 512,000바이트로 설정됩니다.cancellationToken
: 읽기를 취소하기 위한 CancellationToken입니다.
JavaScript에서:
function streamToDotNet() {
return new Uint8Array(10000000);
}
C# 코드에서:
var dataReference =
await JS.InvokeAsync<IJSStreamReference>("streamToDotNet");
using var dataReferenceStream =
await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000);
var outputPath = Path.Combine(Path.GetTempPath(), "file.txt");
using var outputFileStream = File.OpenWrite(outputPath);
await dataReferenceStream.CopyToAsync(outputFileStream);
앞의 예에서:
JS
는 삽입된 IJSRuntime 인스턴스입니다. IJSRuntime은 Blazor 프레임워크에 의해 등록됩니다.dataReferenceStream
은 현재 사용자의 임시 폴더 경로(GetTempPath)에서 디스크(file.txt
)에 기록됩니다.
ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출에서는 DotNetStreamReference를 사용하여 .NET에서 JavaScript로 스트리밍하는 역방향 작업을 설명합니다.
ASP.NET Core Blazor 파일 업로드에서는 Blazor에서 파일을 업로드하는 방법을 설명합니다. 서버 쪽 구성 요소에서 데이터를 스트리밍 <textarea>
하는 양식 예제는 ASP.NET Core Blazor 양식 문제 해결을 참조하세요.
JavaScript [JSImport]
/[JSExport]
interop
이 섹션은 클라이언트 쪽 구성 요소에 적용됩니다.
인터페이스를 기반으로 IJSRuntime 하는 'interop 메커니즘을 JS 사용하여 Blazor클라이언트 쪽 구성 요소에서 JavaScript(JS)와 상호 작용하는 대신.JS[JSImport]
/[JSExport]
NET 7 이상을 대상으로 하는 앱에서 interop API를 사용할 수 있습니다.
자세한 내용은 ASP.NET Core와의 JavaScript JSImport/JSExport interop를 참조하세요 Blazor.
JavaScript interop 개체 참조 삭제
JavaScript(JS) interop 문서의 예제는 일반적인 개체 삭제 패턴을 보여 줍니다.
이 문서에 설명된 대로 .NET에서 JS.NET을 호출할 때 .NET에서 만든 DotNetObjectReference 메모리를 삭제하거나 JS .NET 메모리가 누출되지 않도록 합니다.
ASP.NET CoreBlazor의 .NET 메서드에서 JavaScript 함수 호출에 설명된 대로 .NET에서 호출 JS 하는 경우 메모리 누수 방지를 위해 .NET에서 만든/
JSObjectReference
IJSObjectReference/IJSInProcessObjectReferenceJS 모든 함수를 JS 삭제합니다.
JS interop 개체 참조는 참조를 만드는 interop 호출의 측면에 JS 있는 식별자에 의해 키가 지정된 맵으로 구현됩니다. .NET 또는 JS 쪽 Blazor 에서 개체 삭제가 시작되면 맵에서 항목을 제거하고 개체에 대한 다른 강력한 참조가 없는 한 개체를 가비지 수집할 수 있습니다.
최소한 .NET 관리 메모리 누수 방지를 위해 항상 .NET 쪽에서 만든 개체를 삭제합니다.
구성 요소 삭제 중 DOM 정리 작업
자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.
회로가 없는 JavaScript interop 호출
자세한 내용은 ASP.NET Core Blazor JavaScript 상호 운용성(JS)을 참조하세요.
추가 리소스
- ASP.NET Core Blazor의 .NET 메서드에서 JavaScript 함수 호출
InteropComponent.razor
예제(dotnet/AspNetCore
GitHub 리포지토리main
분기): 분기는main
ASP.NET Core의 다음 릴리스에 대한 제품 단위의 현재 개발을 나타냅니다. 다른 릴리스에 대한 분기를 선택하려면(예release/{VERSION}
: 자리 표시자가 릴리스 버전인 경우{VERSION}
) 분기 또는 태그 드롭다운 목록에서 분기를 선택합니다. 더 이상 존재하지 않는 분기의 경우 태그 탭을 사용하여 API(예:v7.0.0
)를 찾습니다.- DOM과의 상호 작용
- Blazor샘플 GitHub 리포지토리()(
dotnet/blazor-samples
다운로드 방법) - ASP.NET Core Blazor 앱 에서 오류 처리(JavaScript interop 섹션)
- 위협 완화: 브라우저에서 호출된 .NET 메서드
ASP.NET Core