Llamada a métodos de .NET desde funciones de JavaScript en ASP.NET Core Blazor
En este artículo se explica cómo invocar métodos de .NET desde JavaScript (JS).
Para obtener información sobre cómo llamar a funciones de JS desde .NET, vea Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core.
Invocación de un método de .NET estático
Para invocar un método de .NET estático desde JavaScript (JS), use las funciones de JS:
DotNet.invokeMethodAsync
(Recomendado): asincrónico para las aplicaciones Blazor Server y Blazor WebAssembly.DotNet.invokeMethod
: solo sincrónico para las aplicaciones Blazor WebAssembly.
Pase el nombre del ensamblado que contiene el método, el identificador del método estático de .NET y cualquier argumento.
En el ejemplo siguiente:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método , y cada uno de ellos debe ser serializable con JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
devuelve un JS Promise
que representa el resultado de la operación. DotNet.invokeMethod
(solo Blazor WebAssembly) devuelve el resultado de la operación.
Importante
La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
El método de .NET debe ser público y estático, y debe tener el atributo [JSInvokable]
.
En el ejemplo siguiente:
- El marcador de posición
{<T>}
indica el tipo de valor devuelto, que solo es necesario para los métodos que devuelven un valor. - El marcador de posición
{.NET METHOD ID}
es el identificador del método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Nota
La llamada a métodos genéricos abiertos no se admite con métodos estáticos de .NET, pero se admite con métodos de instancia. Para obtener más información, vea la sección Llamada a métodos de clase genérica de .NET.
En el componente CallDotNetExample1
siguiente, el método de C# ReturnArrayAsync
devuelve una matriz int
. El atributo [JSInvokable]
se aplica al método, lo que hace que el método sea invocable por 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 });
}
}
El atributo HTML onclick
del elemento <button>
es la asignación del controlador de eventos onclick
de JavaScript para procesar eventos click
, y no el atributo de directiva @onclick
de Blazor. La función returnArrayAsync
JS se asigna como controlador.
La siguiente función returnArrayAsync
JS llama al método de .NET ReturnArrayAsync
del componente CallDotNetExample1
anterior y registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
Cuando se selecciona el botón Trigger .NET static method
, la salida de la consola de las herramientas de desarrollo del explorador muestra los datos de la matriz. El formato de la salida difiere ligeramente entre los exploradores. En la salida siguiente se muestra el formato utilizado por Microsoft Edge:
Array(3) [ 1, 2, 3 ]
De forma predeterminada, el identificador de método de .NET para la llamada de JS es el nombre del método de .NET, pero puede especificar un identificador distinto mediante el constructor del atributo [JSInvokable]
. En el ejemplo siguiente, DifferentMethodName
es el identificador del método asignado para el método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
En la llamada a DotNet.invokeMethodAsync
o DotNet.invokeMethod
(solo Blazor WebAssembly), llame a DifferentMethodName
para ejecutar el método de .NET ReturnArrayAsync
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(solo Blazor WebAssembly)
Nota
El ejemplo de método ReturnArrayAsync
de esta sección devuelve el resultado de un Task sin el uso de las palabras clave async
y await
de C# explícitas. La codificación de métodos con async
y await
es típica de los métodos que usan la palabra clave await
para devolver el valor de las operaciones asincrónicas.
El método ReturnArrayAsync
compuesto con las palabras clave async
y await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
return await Task.FromResult(new int[] { 1, 2, 3 });
}
Para obtener más información, vea Programación asincrónica con async y await en la guía de C#.
Invocación de un método de .NET de instancia
Para invocar un método de .NET de instancia desde JavaScript (JS):
- Pase la instancia de .NET por referencia a JS encapsulando la instancia en un DotNetObjectReference y llamando a Create en ella.
- Invoque un método de instancia de .NET desde JS mediante
invokeMethodAsync
oinvokeMethod
(solo Blazor WebAssembly) desde la clase DotNetObjectReference pasada. La instancia de .NET también se puede pasar como argumento al invocar otros métodos de .NET desde JS. - Deseche DotNetObjectReference.
En las siguientes secciones de este artículo muestran varios enfoques para invocar un método de .NET de instancia:
- Pasar
DotNetObjectReference
a una función de JavaScript individual - Pasar
DotNetObjectReference
a una clase con varias funciones de JavaScript - Llamada a métodos de clase genérica de .NET
- Ejemplos de instancias de clase
- Clase auxiliar del método de .NET de la instancia de componente
Pasar DotNetObjectReference
a una función de JavaScript individual
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una función JavaScript (JS) individual.
La función sayHello1
de JS siguiente recibe DotNetObjectReference y llama a invokeMethodAsync
para invocar el método de .NET GetHelloMessage
de un componente:
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para el componente CallDotNetExample2
siguiente:
- El componente tiene un método de .NET invocable por JS llamado
GetHelloMessage
. - Cuando se selecciona el botón
Trigger .NET instance method
, se llama a la funciónsayHello1
de JS con DotNetObjectReference. sayHello1
:- Llama a
GetHelloMessage
y recibe el resultado del mensaje. - Devuelve el resultado del mensaje al método
TriggerDotNetInstanceMethod
que realiza la llamada.
- Llama a
- El mensaje devuelto de
sayHello1
enresult
se muestra al usuario. - Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para pasar argumentos al método de instancia:
Agregue parámetros a la invocación del método de .NET. En el ejemplo siguiente, se pasa un nombre al método. Agregue parámetros adicionales a la lista según sea necesario.
<script> window.sayHello2 = (dotNetHelper, name) => { return dotNetHelper.invokeMethodAsync('GetHelloMessage', name); }; </script>
En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.Proporcione la lista de parámetros al método de .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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Pasar DotNetObjectReference
a una clase con varias funciones de JavaScript
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una clase JavaScript (JS) con varias funciones.
Cree y pase DotNetObjectReference desde el método de ciclo de vida OnAfterRenderAsync
a una clase JS para usar varias funciones. Asegúrese de que el código .NET elimina DotNetObjectReference, como se muestra en el ejemplo siguiente.
En el componente CallDotNetExampleOneHelper
siguiente, los botones Trigger JS function
llaman a funciones JS estableciendo la propiedad JSonclick
, no el atributo de directiva @onclick
de Blazor.
Pages/CallDotNetExampleOneHelper.razor
:
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@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 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();
}
}
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El componente debe eliminar explícitamente DotNetObjectReference para permitir la recolección de elementos no utilizados y evitar una pérdida de memoria.
<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>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior:
- La clase
GreetingHelpers
se agrega al objetowindow
para definir globalmente la clase, lo que permite a Blazor buscar la clase para la interoperabilidad de JS. - En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Llamada a métodos de clase genérica de .NET
Las funciones JavaScript (JS) pueden llamar a métodos de clase genérica de .NET, donde una función JS llama a un método .NET de una clase genérica.
En la clase de tipo genérico siguiente (GenericType<TValue>
):
- La clase tiene un único parámetro de tipo (
TValue
) con una sola propiedadValue
genérica. - La clase tiene dos métodos no genéricos marcados con el atributo
[JSInvokable]
, cada uno con un parámetro de tipo genérico denominadonewValue
:Update
actualiza sincrónicamente el valor deValue
desdenewValue
.UpdateAsync
actualiza de asincrónicamente el valor deValue
desdenewValue
después de crear una tarea por la que se puede esperar con Task.Yield, que vuelve a suspenderse asincrónicamente al contexto actual cuando se espera por dicho elemento.
- Cada uno de los métodos de clase escribe el tipo de
TValue
y el valor deValue
en la consola. La escritura en la consola solo tiene fines de demostración. Las aplicaciones de producción normalmente evitan escribir en la consola en favor del registro de la aplicación. Para obtener más información, consulte Registro de Blazor en ASP.NET Core y Registro en .NET Core y ASP.NET Core.
Nota
Los tipos y métodos genéricos abiertos no especifican tipos para los marcadores de posición de tipo. Por el contrario, los genéricos cerrados suministran tipos para todos los marcadores de posición de tipo. Los ejemplos de esta sección muestran genéricos cerrados, pero la invocación de interoperabilidad de JS de métodos de instancia con genéricos abiertos se admite. El uso de genéricos abiertos no se admite para las invocaciones de métodos estáticos de .NET, que se describieron anteriormente en este artículo.
Para más información, consulte los siguientes artículos.
- Clases y métodos genéricos (documentación de C#)
- Clases genéricas (Guía de programación de C#)
- Genéricos en .NET (documentación de .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}");
}
}
En la siguiente función invokeMethodsAsync
:
- Los métodos
Update
yUpdateAsync
de la clase de tipo genérico se llaman con argumentos que representan cadenas y números. - Las aplicaciones Blazor WebAssembly admiten llamar a métodos de .NET sincrónicamente con
invokeMethod
.syncInterop
recibe un valor booleano que indica si la inteoperabilidad de JS se está produciendo en una aplicación Blazor WebAssembly. CuandosyncInterop
estrue
, se llama ainvokeMethod
de forma segura. Si el valor desyncInterop
esfalse
, solo se llama a la función asincrónicainvokeMethodAsync
porque la inteoperabilidad de JS se ejecuta en una aplicación Blazor Server. - Con fines de demostración, la llamada de función DotNetObjectReference (
invokeMethod
oinvokeMethodAsync
), el método de .NET denominado (Update
oUpdateAsync
) y el argumento se escriben en la consola. Los argumentos usan un número aleatorio para permitir que la función JS llame a la invocación del método .NET (también escrita en la consola en el lado de .NET). Normalmente, el código de producción no escribe en la consola, ni en el cliente ni en el servidor. Las aplicaciones de producción normalmente se basan en el registro de la aplicación. Para obtener más información, consulte Registro de Blazor en ASP.NET Core y Registro en .NET Core y 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>
Nota
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el componente GenericsExample
siguiente:
- Se llama a la función JS
invokeMethodsAsync
cuando se selecciona el botónInvoke Interop
. - Se crea un par de tipos DotNetObjectReference y se pasan a la función JS para las instancias de
GenericType
comostring
yint
.
Pages/GenericsExample.razor
:
@page "/generics-example"
@using System.Runtime.InteropServices
@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 };
public async Task InvokeInterop()
{
var syncInterop =
RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
await JS.InvokeVoidAsync(
"invokeMethodsAsync",
syncInterop,
DotNetObjectReference.Create(genericType1),
DotNetObjectReference.Create(genericType2));
}
}
En el ejemplo anterior, JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.
A continuación se muestra la salida típica del ejemplo anterior cuando se selecciona el botón Invoke Interop
en una aplicación Blazor WebAssembly:
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
Si el ejemplo anterior se implementa en una aplicación Blazor Server, se evitan las llamadas sincrónicas con invokeMethod
. La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
Salida típica de una aplicación Blazor Server:
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
Los ejemplos de salida anteriores muestran que los métodos asincrónicos se ejecutan y completan en un orden arbitrario en función de varios factores, incluida la programación de subprocesos y la velocidad de ejecución del método. No es posible predecir de forma confiable el orden de finalización de las llamadas a métodos asincrónicos.
Ejemplos de instancias de clase
La siguiente función sayHello1
JS:
- Llama al método de .NET
GetHelloMessage
en la clase DotNetObjectReference pasada. - Devuelve el mensaje de
GetHelloMessage
al autor de la llamadasayHello1
.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
La clase HelloHelper
siguiente tiene un método de .NET invocable por JS llamado GetHelloMessage
. Cuando se crea HelloHelper
, el nombre de la propiedad Name
se usa para devolver un mensaje de GetHelloMessage
.
HelloHelper.cs
:
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}!";
}
El método CallHelloHelperGetHelloMessage
de la clase JsInteropClasses3
siguiente invoca la función sayHello1
de JS con una nueva instancia de HelloHelper
.
JsInteropClasses3.cs
:
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
Cuando se selecciona el botón Trigger .NET instance method
en el componente CallDotNetExample4
siguiente, se llama a JsInteropClasses3.CallHelloHelperGetHelloMessage
con el valor de name
.
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();
}
}
En la imagen siguiente se muestra el componente representado con el nombre Amy Pond
en el campo Name
. Una vez seleccionado el botón, Hello, Amy Pond!
se muestra en la interfaz de usuario:
El patrón anterior que se muestra en la clase JsInteropClasses3
también se puede implementar íntegramente en un componente.
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
La salida mostrada por el componente CallDotNetExample5
es Hello, Amy Pond!
cuando el nombre Amy Pond
se proporciona en el campo Name
.
En el componente CallDotNetExample5
anterior, se elimina la referencia al objeto de .NET. Si una clase o un componente no elimina DotNetObjectReference, deséchelo del cliente llamando a dispose
en la clase DotNetObjectReference pasada:
window.jsFunction = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
dotNetHelper.dispose();
}
En el ejemplo anterior:
- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET.
Clase auxiliar del método de .NET de la instancia de componente
Una clase auxiliar puede invocar un método de instancia de .NET como Action. Las clases auxiliares son útiles en los siguientes escenarios:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En una aplicación de Blazor Server, donde varios usuarios usan simultáneamente el mismo componente.
En el ejemplo siguiente:
- El componente
CallDotNetExample6
contiene varios componentesListItem
, que es un componente compartido en la carpetaShared
de la aplicación. - Cada componente
ListItem
consta de un mensaje y un botón. - Cuando se selecciona un botón de componente
ListItem
, el métodoUpdateMessage
de ese objetoListItem
cambia el texto del elemento de lista y oculta el botón.
La clase MessageUpdateInvokeHelper
siguiente mantiene un método de .NET invocable por JS, UpdateMessageCaller
, para invocar el elemento Action especificado cuando se crea una instancia de la clase. BlazorSample
es el nombre del ensamblado de la aplicación.
MessageUpdateInvokeHelper.cs
:
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable("BlazorSample")]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
La siguiente función updateMessageCaller
JS invoca el método de .NET UpdateMessageCaller
. BlazorSample
es el nombre del ensamblado de la aplicación.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
El componente ListItem
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
). Cada instancia de componente ListItem
establece una instancia de MessageUpdateInvokeHelper
con un elemento Action establecido en su método UpdateMessage
.
Cuando se selecciona un botón InteropCall
del componente ListItem
, se invoca a updateMessageCaller
con un elemento DotNetObjectReference creado para la instancia MessageUpdateInvokeHelper
. Esto permite que el marco llame a UpdateMessageCaller
en esa instancia MessageUpdateInvokeHelper
de ListItem
. La clase DotNetObjectReference pasada se elimina en 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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
Se llama a StateHasChanged
para actualizar la interfaz de usuario cuando message
se establece en UpdateMessage
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna manera de saber que la interfaz de usuario debe actualizarse cuando se invoca a Action.
El siguiente componente primario CallDotNetExample6
incluye cuatro elementos de lista, y cada uno de ellos es una instancia del componente ListItem
.
Pages/CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem />
<ListItem />
<ListItem />
<ListItem />
</ul>
En la imagen siguiente se muestra el componente primario CallDotNetExample6
representado después de seleccionar el segundo botón InteropCall
:
- El segundo componente
ListItem
ha mostrado el mensajeUpdateMessage Called!
. - El botón
InteropCall
del segundo componenteListItem
no es visible porque la propiedaddisplay
de CSS del botón está establecida ennone
.
Interoperabilidad JS sincrónica en aplicaciones Blazor WebAssembly
Esta sección solo se aplica a las aplicaciones .
De forma predeterminada, las llamadas de interoperabilidad de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asincrónicas de forma predeterminada para asegurarse de que los componentes son compatibles en ambos modelos Blazor de hospedaje, Blazor Server y Blazor WebAssembly. En Blazor Server, todas las llamadas de interoperabilidad de JS deben ser asincrónicas porque se envían a través de una conexión de red.
Si sabe con certeza que la aplicación solo se ejecuta en Blazor WebAssembly, puede optar por realizar llamadas de interoperabilidad de JS sincrónicas. Esto tiene una sobrecarga ligeramente menor que la realización de llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras se esperan los resultados.
Para hacer una llamada sincrónica desde JavaScript a .NET en las aplicaciones de Blazor WebAssembly, use DotNet.invokeMethod
en lugar de DotNet.invokeMethodAsync
.
Las llamadas sincrónicas funcionan si:
- La aplicación se ejecuta en Blazor WebAssembly, no en Blazor Server.
- La función llamada devuelve un valor de forma sincrónica. La función no es un método
async
y no devuelve un valor Task de .NET oPromise
de JavaScript.
Ubicación de JavaScript
Cargue código de JavaScript (JS) mediante cualquiera de los enfoques descritos en el artículo de información general sobre interoperabilidad de JS:
- Cargar un script en el marcado
<head>
(en general, no se recomienda) - Cargar un script en el marcado
- Carga de un script desde un archivo externo (
.js
) - Inserción de un script después del inicio de
Para obtener información sobre cómo aislar scripts en los módulos , consulte la sección Aislamiento de JavaScript en módulos de JavaScript.
Advertencia
No coloque una etiqueta <script>
en un archivo de componente (.razor
), porque la etiqueta <script>
no se puede actualizar dinámicamente.
Aislamiento de JavaScript en módulos de JavaScript
Blazor permite el aislamiento de JavaScript (JS) en Blazor estándar (JS).
El aislamiento de JS proporciona las siguientes ventajas:
- El JS importado no contamina el espacio de nombres global.
- No es necesario que los consumidores de una biblioteca y los componentes importen el código de JS relacionado.
Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Evitar referencias de objetos circulares
Los objetos que contienen referencias circulares no se pueden serializar en el cliente para:
- Llamadas de método .NET.
- Llamadas de método JavaScript desde C# cuando el tipo de valor devuelto tiene referencias circulares.
Compatibilidad con matrices de bytes
Blazor admite la interoperabilidad de JavaScript de matriz de bytes optimizada (JS) que evita la codificación o descodificación de matrices de bytes en Base64. En el ejemplo siguiente se usa la interoperabilidad de JS para pasar una matriz de bytes a .NET.
Proporcione una función sendByteArray
de JS. Un botón del componente llama a la función y no devuelve un valor:
<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>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
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 ©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));
}
}
Para obtener información sobre el uso de una matriz de bytes al llamar a JavaScript desde .NET, vea Llamada a funciones de JavaScript desde métodos de .NET en ASP.NET Core Blazor.
Transmisión de JavaScript a .NET
Blazor admite el streaming de datos directamente desde JavaScript a .NET. Los flujos se solicitan mediante la interfaz Microsoft.JSInterop.IJSStreamReference
.
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
devuelve un objeto Stream y usa los siguientes parámetros:
maxAllowedSize
: número máximo de bytes permitidos para la operación de lectura de JavaScript, cuyo valor predeterminado es 512 000 bytes si no se especifica.cancellationToken
: un objeto CancellationToken para cancelar la lectura.
En JavaScript:
function streamToDotNet() {
return new Uint8Array(10000000);
}
En código de 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);
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.dataReferenceStream
se escribe en el disco (file.txt
) en la ruta de la carpeta temporal del usuario actual (GetTempPath).
En Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core se describe la operación inversa, el streaming desde .NET a JavaScript mediante un objeto DotNetStreamReference.
En Cargas de archivos de Blazor en ASP.NET Core se describe cómo cargar un archivo en Blazor.
Límites de tamaño en las llamadas de interoperabilidad de JavaScript
Esta sección solo se aplica a las aplicaciones Blazor Server. En Blazor WebAssembly, el marco no impone límites en cuanto al tamaño de las entradas y salidas de las llamadas de interoperabilidad de JavaScript (JS).
En Blazor Server, las llamadas de interoperabilidad de JS presentan un tamaño limitado por el tamaño máximo de los mensajes SignalR entrantes que se permite para los métodos del concentrador. Esto se aplica por medio de HubOptions.MaximumReceiveMessageSize (valor predeterminado: 32 KB). Los mensajes de JS a .NET SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente.
Cuando el registro de SignalR no está establecido en SignalR o Seguimiento, solo aparece un error relativo al tamaño del mensaje en la consola de herramientas de desarrollo del explorador:
Error: Conexión desconectada con el error "Error: El servidor ha devuelto un error al cerrarse: Conexión cerrada con un error.".
Cuando el registro del lado servidor de se establece en Depurar o Seguimiento, el registro del lado servidor inicia una excepción relativa a un error del tamaño del mensaje.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Error:
System.IO.InvalidDataException: Se ha superado el tamaño máximo del mensaje de 32768B. El tamaño del mensaje se puede configurar en AddHubOptions.
Para aumentar el límite, establezca MaximumReceiveMessageSize en Program.cs
. En el ejemplo siguiente se establece el tamaño máximo del mensaje de recepción en 64 KB (64*1024):
builder.Services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
El aumento del límite de tamaño del mensaje entrante SignalR implica que se necesitan más recursos del servidor y lo expone a más riesgos por parte de un usuario malintencionado. Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.
Tenga en cuenta las instrucciones siguientes al desarrollar código que transfiera un gran volumen de datos entre JS y Blazor en aplicaciones Blazor Server:
- Aproveche la compatibilidad de interoperabilidad de transmisión nativa para transferir datos mayores que el límite de tamaño del mensaje entrante SignalR:
- Sugerencias generales:
- No asigne objetos grandes en código JS y C#.
- Libere la memoria consumida al completar o cancelar el proceso.
- Aplique los requisitos adicionales siguientes por motivos de seguridad:
- Declare el tamaño máximo del archivo o los datos que se pueden pasar.
- Declare la tasa mínima de carga desde el cliente al servidor.
- Después de que el servidor reciba los datos, los datos se pueden:
- Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
- Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.
Recursos adicionales
- Llamada a funciones de JavaScript con métodos de .NET en de ASP.NET CoreBlazor
- Ejemplo de (rama
main
del repositorio de GitHub dotnet/AspNetCore): la ramamain
representa el desarrollo actual de la unidad de producto para la próxima versión de ASP.NET Core. Para seleccionar la rama de una versión diferente (por ejemplo,release/5.0
), use la lista desplegable Switch branches or tags (Cambiar ramas o etiquetas). - Interacción con Document Object Model (DOM)
-
Repositorio de GitHub con ejemplos de Blazor (
dotnet/blazor-samples
)
Para obtener información sobre cómo llamar a funciones de JS desde .NET, vea Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core.
Invocación de un método de .NET estático
Para invocar un método de .NET estático desde JavaScript (JS), use las funciones de JS:
DotNet.invokeMethodAsync
(Recomendado): asincrónico para las aplicaciones Blazor Server y Blazor WebAssembly.DotNet.invokeMethod
: solo sincrónico para las aplicaciones Blazor WebAssembly.
En el ejemplo siguiente:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método , y cada uno de ellos debe ser serializable con JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
devuelve un JS Promise
que representa el resultado de la operación. DotNet.invokeMethod
(solo Blazor WebAssembly) devuelve el resultado de la operación.
Importante
La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
El método de .NET debe ser público y estático, y debe tener el atributo [JSInvokable]
.
En el ejemplo siguiente:
- El marcador de posición
{<T>}
indica el tipo de valor devuelto, que solo es necesario para los métodos que devuelven un valor. - El marcador de posición
{.NET METHOD ID}
es el identificador del método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Nota
La llamada a métodos genéricos abiertos no se admite con métodos estáticos de .NET, pero se admite con métodos de instancia, que se describen más adelante en este artículo.
En el componente CallDotNetExample1
siguiente, el método de C# ReturnArrayAsync
devuelve una matriz int
. El atributo [JSInvokable]
se aplica al método, lo que hace que el método sea invocable por 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 });
}
}
El atributo HTML onclick
del elemento <button>
es la asignación del controlador de eventos onclick
de JavaScript para procesar eventos click
, y no el atributo de directiva @onclick
de Blazor. La función returnArrayAsync
JS se asigna como controlador.
La siguiente función returnArrayAsync
JS llama al método de .NET ReturnArrayAsync
del componente CallDotNetExample1
anterior y registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Cuando se selecciona el botón Trigger .NET static method
, la salida de la consola de las herramientas de desarrollo del explorador muestra los datos de la matriz. El formato de la salida difiere ligeramente entre los exploradores. En la salida siguiente se muestra el formato utilizado por Microsoft Edge:
Array(3) [ 1, 2, 3 ]
De forma predeterminada, el identificador de método de .NET para la llamada de JS es el nombre del método de .NET, pero puede especificar un identificador distinto mediante el constructor del atributo [JSInvokable]
. En el ejemplo siguiente, DifferentMethodName
es el identificador del método asignado para el método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
En la llamada a DotNet.invokeMethodAsync
o DotNet.invokeMethod
(solo Blazor WebAssembly), llame a DifferentMethodName
para ejecutar el método de .NET ReturnArrayAsync
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(solo Blazor WebAssembly)
Nota
El ejemplo de método ReturnArrayAsync
de esta sección devuelve el resultado de un Task sin el uso de las palabras clave async
y await
de C# explícitas. La codificación de métodos con async
y await
es típica de los métodos que usan la palabra clave await
para devolver el valor de las operaciones asincrónicas.
El método ReturnArrayAsync
compuesto con las palabras clave async
y await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
return await Task.FromResult(new int[] { 1, 2, 3 });
}
Para obtener más información, vea Programación asincrónica con async y await en la guía de C#.
Invocación de un método de .NET de instancia
Para invocar un método de .NET de instancia desde JavaScript (JS):
- Pase la instancia de .NET por referencia a JS encapsulando la instancia en un DotNetObjectReference y llamando a Create en ella.
- Invoque un método de instancia de .NET desde JS mediante
invokeMethodAsync
oinvokeMethod
(solo Blazor WebAssembly) desde la clase DotNetObjectReference pasada. La instancia de .NET también se puede pasar como argumento al invocar otros métodos de .NET desde JS. - Deseche DotNetObjectReference.
En las siguientes secciones de este artículo muestran varios enfoques para invocar un método de .NET de instancia:
- Ejemplos de instancias de componente
- Ejemplos de instancias de clase
- Clase auxiliar del método de .NET de la instancia de componente
Ejemplos de instancias de componente
La siguiente función sayHello1
JS recibe DotNetObjectReference y llama a invokeMethodAsync
para invocar el método de .NET GetHelloMessage
de un componente.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Para el componente CallDotNetExample2
siguiente:
- El componente tiene un método de .NET invocable por JS llamado
GetHelloMessage
. - Cuando se selecciona el botón
Trigger .NET instance method
, se llama a la funciónsayHello1
de JS con DotNetObjectReference. sayHello1
:- Llama a
GetHelloMessage
y recibe el resultado del mensaje. - Devuelve el resultado del mensaje al método
TriggerDotNetInstanceMethod
que realiza la llamada.
- Llama a
- El mensaje devuelto de
sayHello1
enresult
se muestra al usuario. - Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método
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();
}
}
Para pasar argumentos al método de instancia:
Agregue parámetros a la invocación del método de .NET. En el ejemplo siguiente, se pasa un nombre al método. Agregue parámetros adicionales a la lista según sea necesario.
<script> window.sayHello2 = (dotNetHelper, name) => { return dotNetHelper.invokeMethodAsync('GetHelloMessage', name); }; </script>
Proporcione la lista de parámetros al método de .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();
}
}
Ejemplos de instancias de clase
La siguiente función sayHello1
JS:
- Llama al método de .NET
GetHelloMessage
en la clase DotNetObjectReference pasada. - Devuelve el mensaje de
GetHelloMessage
al autor de la llamadasayHello1
.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
La clase HelloHelper
siguiente tiene un método de .NET invocable por JS llamado GetHelloMessage
. Cuando se crea HelloHelper
, el nombre de la propiedad Name
se usa para devolver un mensaje de 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}!";
}
El método CallHelloHelperGetHelloMessage
de la clase JsInteropClasses3
siguiente invoca la función sayHello1
de JS con una nueva instancia de HelloHelper
.
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();
}
}
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
Cuando se selecciona el botón Trigger .NET instance method
en el componente CallDotNetExample4
siguiente, se llama a JsInteropClasses3.CallHelloHelperGetHelloMessage
con el valor de name
.
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();
}
}
En la imagen siguiente se muestra el componente representado con el nombre Amy Pond
en el campo Name
. Una vez seleccionado el botón, Hello, Amy Pond!
se muestra en la interfaz de usuario:
El patrón anterior que se muestra en la clase JsInteropClasses3
también se puede implementar íntegramente en un componente.
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();
}
}
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
La salida mostrada por el componente CallDotNetExample5
es Hello, Amy Pond!
cuando el nombre Amy Pond
se proporciona en el campo Name
.
En el componente CallDotNetExample5
anterior, se elimina la referencia al objeto de .NET. Si una clase o un componente no elimina DotNetObjectReference, deséchelo del cliente llamando a dispose
en la clase DotNetObjectReference pasada:
window.jsFunction = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
dotNetHelper.dispose();
}
En el ejemplo anterior:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET.
Clase auxiliar del método de .NET de la instancia de componente
Una clase auxiliar puede invocar un método de instancia de .NET como Action. Las clases auxiliares son útiles en los siguientes escenarios:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En una aplicación de Blazor Server, donde varios usuarios usan simultáneamente el mismo componente.
En el ejemplo siguiente:
- El componente
CallDotNetExample6
contiene varios componentesListItem
, que es un componente compartido en la carpetaShared
de la aplicación. - Cada componente
ListItem
consta de un mensaje y un botón. - Cuando se selecciona un botón de componente
ListItem
, el métodoUpdateMessage
de ese objetoListItem
cambia el texto del elemento de lista y oculta el botón.
La clase MessageUpdateInvokeHelper
siguiente mantiene un método de .NET invocable por JS, UpdateMessageCaller
, para invocar el elemento Action especificado cuando se crea una instancia de la clase. BlazorSample
es el nombre del ensamblado de la aplicación.
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();
}
}
La siguiente función updateMessageCaller
JS invoca el método de .NET UpdateMessageCaller
. BlazorSample
es el nombre del ensamblado de la aplicación.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
El componente ListItem
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
). Cada instancia de componente ListItem
establece una instancia de MessageUpdateInvokeHelper
con un elemento Action establecido en su método UpdateMessage
.
Cuando se selecciona un botón InteropCall
del componente ListItem
, se invoca a updateMessageCaller
con un elemento DotNetObjectReference creado para la instancia MessageUpdateInvokeHelper
. Esto permite que el marco llame a UpdateMessageCaller
en esa instancia MessageUpdateInvokeHelper
de ListItem
. La clase DotNetObjectReference pasada se elimina en 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();
}
}
Se llama a StateHasChanged
para actualizar la interfaz de usuario cuando message
se establece en UpdateMessage
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna manera de saber que la interfaz de usuario debe actualizarse cuando se invoca a Action.
El siguiente componente primario CallDotNetExample6
incluye cuatro elementos de lista, y cada uno de ellos es una instancia del componente ListItem
.
Pages/CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem />
<ListItem />
<ListItem />
<ListItem />
</ul>
En la imagen siguiente se muestra el componente primario CallDotNetExample6
representado después de seleccionar el segundo botón InteropCall
:
- El segundo componente
ListItem
ha mostrado el mensajeUpdateMessage Called!
. - El botón
InteropCall
del segundo componenteListItem
no es visible porque la propiedaddisplay
de CSS del botón está establecida ennone
.
Ubicación de JavaScript
Cargue código de JavaScript (JS) mediante cualquiera de los enfoques descritos en el artículo de información general sobre interoperabilidad de JS:
- Cargar un script en el marcado
<head>
(en general, no se recomienda) - Cargar un script en el marcado
- Carga de un script desde un archivo JavaScript externo () colocado con un componente
- Carga de un script desde un archivo externo (
.js
) - Inserción de un script después del inicio de
Para obtener información sobre cómo aislar scripts en los módulos , consulte la sección Aislamiento de JavaScript en módulos de JavaScript.
Advertencia
No coloque una etiqueta <script>
en un archivo de componente (.razor
), porque la etiqueta <script>
no se puede actualizar dinámicamente.
Evitar referencias de objetos circulares
Los objetos que contienen referencias circulares no se pueden serializar en el cliente para:
- Llamadas de método .NET.
- Llamadas de método JavaScript desde C# cuando el tipo de valor devuelto tiene referencias circulares.
Límites de tamaño en las llamadas de interoperabilidad de JavaScript
Esta sección solo se aplica a las aplicaciones Blazor Server. En Blazor WebAssembly, el marco no impone límites en cuanto al tamaño de las entradas y salidas de las llamadas de interoperabilidad de JavaScript (JS).
En Blazor Server, las llamadas de interoperabilidad de JS presentan un tamaño limitado por el tamaño máximo de los mensajes SignalR entrantes que se permite para los métodos del concentrador. Esto se aplica por medio de HubOptions.MaximumReceiveMessageSize (valor predeterminado: 32 KB). Los mensajes de JS a .NET SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente.
Cuando el registro de SignalR no está establecido en SignalR o Seguimiento, solo aparece un error relativo al tamaño del mensaje en la consola de herramientas de desarrollo del explorador:
Error: Conexión desconectada con el error "Error: El servidor ha devuelto un error al cerrarse: Conexión cerrada con un error.".
Cuando el registro del lado servidor de se establece en Depurar o Seguimiento, el registro del lado servidor inicia una excepción relativa a un error del tamaño del mensaje.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Error:
System.IO.InvalidDataException: Se ha superado el tamaño máximo del mensaje de 32768B. El tamaño del mensaje se puede configurar en AddHubOptions.
Para aumentar el límite, establezca MaximumReceiveMessageSize en Startup.ConfigureServices
:
services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
El aumento del límite de tamaño del mensaje entrante SignalR implica que se necesitan más recursos del servidor y lo expone a más riesgos por parte de un usuario malintencionado. Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.
Una opción para leer cargas grandes consiste en enviar el contenido en fragmentos más pequeños y procesar la carga como Stream. Se puede usar al leer cargas grandes de JSON o si los datos están disponibles en JS como bytes sin formato. Para obtener un ejemplo en el que se muestra el envío de cargas binarias de gran tamaño en Blazor Server que usa técnicas similares a las del Blazor Server, vea la InputFile
.
Nota:
Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Tenga en cuenta las instrucciones siguientes al desarrollar código que transfiera un gran volumen de datos entre JS y Blazor en aplicaciones Blazor Server:
- Segmente los datos en partes más pequeñas y envíe los segmentos de datos secuencialmente hasta que el servidor reciba todos los datos.
- No asigne objetos grandes en código JS y C#.
- No bloquee el subproceso de interfaz de usuario principal durante períodos largos al enviar o recibir datos.
- Libere la memoria consumida al completar o cancelar el proceso.
- Aplique los requisitos adicionales siguientes por motivos de seguridad:
- Declare el tamaño máximo del archivo o los datos que se pueden pasar.
- Declare la tasa mínima de carga desde el cliente al servidor.
- Después de que el servidor reciba los datos, los datos se pueden:
- Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
- Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.
Aislamiento de JavaScript en módulos de JavaScript
Blazor permite el aislamiento de JavaScript (JS) en Blazor estándar (JS).
El aislamiento de JS proporciona las siguientes ventajas:
- El JS importado no contamina el espacio de nombres global.
- No es necesario que los consumidores de una biblioteca y los componentes importen el código de JS relacionado.
Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Recursos adicionales
- Llamada a funciones de JavaScript con métodos de .NET en de ASP.NET CoreBlazor
- Ejemplo de (rama
main
del repositorio de GitHub dotnet/AspNetCore): la ramamain
representa el desarrollo actual de la unidad de producto para la próxima versión de ASP.NET Core. Para seleccionar la rama de una versión diferente (por ejemplo,release/5.0
), use la lista desplegable Switch branches or tags (Cambiar ramas o etiquetas). - Interacción con Document Object Model (DOM)
-
Repositorio de GitHub con ejemplos de Blazor (
dotnet/blazor-samples
)
Para obtener información sobre cómo llamar a funciones de JS desde .NET, vea Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core.
Invocación de un método de .NET estático
Para invocar un método de .NET estático desde JavaScript (JS), use las funciones de JS:
DotNet.invokeMethodAsync
(Recomendado): asincrónico para las aplicaciones Blazor Server y Blazor WebAssembly.DotNet.invokeMethod
: solo sincrónico para las aplicaciones Blazor WebAssembly.
En el ejemplo siguiente:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método , y cada uno de ellos debe ser serializable con JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
devuelve un JS Promise
que representa el resultado de la operación. DotNet.invokeMethod
(solo Blazor WebAssembly) devuelve el resultado de la operación.
Importante
La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
El método de .NET debe ser público y estático, y debe tener el atributo [JSInvokable]
.
En el ejemplo siguiente:
- El marcador de posición
{<T>}
indica el tipo de valor devuelto, que solo es necesario para los métodos que devuelven un valor. - El marcador de posición
{.NET METHOD ID}
es el identificador del método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Nota
La llamada a métodos genéricos abiertos no se admite con métodos estáticos de .NET, pero se admite con métodos de instancia, que se describen más adelante en este artículo.
En el componente CallDotNetExample1
siguiente, el método de C# ReturnArrayAsync
devuelve una matriz int
. El atributo [JSInvokable]
se aplica al método, lo que hace que el método sea invocable por 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 });
}
}
El atributo HTML onclick
del elemento <button>
es la asignación del controlador de eventos onclick
de JavaScript para procesar eventos click
, y no el atributo de directiva @onclick
de Blazor. La función returnArrayAsync
JS se asigna como controlador.
La siguiente función returnArrayAsync
JS llama al método de .NET ReturnArrayAsync
del componente CallDotNetExample1
anterior y registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Cuando se selecciona el botón Trigger .NET static method
, la salida de la consola de las herramientas de desarrollo del explorador muestra los datos de la matriz. El formato de la salida difiere ligeramente entre los exploradores. En la salida siguiente se muestra el formato utilizado por Microsoft Edge:
Array(3) [ 1, 2, 3 ]
De forma predeterminada, el identificador de método de .NET para la llamada de JS es el nombre del método de .NET, pero puede especificar un identificador distinto mediante el constructor del atributo [JSInvokable]
. En el ejemplo siguiente, DifferentMethodName
es el identificador del método asignado para el método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
En la llamada a DotNet.invokeMethodAsync
o DotNet.invokeMethod
(solo Blazor WebAssembly), llame a DifferentMethodName
para ejecutar el método de .NET ReturnArrayAsync
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(solo Blazor WebAssembly)
Nota
El ejemplo de método ReturnArrayAsync
de esta sección devuelve el resultado de un Task sin el uso de las palabras clave async
y await
de C# explícitas. La codificación de métodos con async
y await
es típica de los métodos que usan la palabra clave await
para devolver el valor de las operaciones asincrónicas.
El método ReturnArrayAsync
compuesto con las palabras clave async
y await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
return await Task.FromResult(new int[] { 1, 2, 3 });
}
Para obtener más información, vea Programación asincrónica con async y await en la guía de C#.
Invocación de un método de .NET de instancia
Para invocar un método de .NET de instancia desde JavaScript (JS):
- Pase la instancia de .NET por referencia a JS encapsulando la instancia en un DotNetObjectReference y llamando a Create en ella.
- Invoque un método de instancia de .NET desde JS mediante
invokeMethodAsync
oinvokeMethod
(solo Blazor WebAssembly) desde la clase DotNetObjectReference pasada. La instancia de .NET también se puede pasar como argumento al invocar otros métodos de .NET desde JS. - Deseche DotNetObjectReference.
En las siguientes secciones de este artículo muestran varios enfoques para invocar un método de .NET de instancia:
- Ejemplos de instancias de componente
- Ejemplos de instancias de clase
- Clase auxiliar del método de .NET de la instancia de componente
Ejemplos de instancias de componente
La siguiente función sayHello1
JS recibe DotNetObjectReference y llama a invokeMethodAsync
para invocar el método de .NET GetHelloMessage
de un componente.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Para el componente CallDotNetExample2
siguiente:
- El componente tiene un método de .NET invocable por JS llamado
GetHelloMessage
. - Cuando se selecciona el botón
Trigger .NET instance method
, se llama a la funciónsayHello1
de JS con DotNetObjectReference. sayHello1
:- Llama a
GetHelloMessage
y recibe el resultado del mensaje. - Devuelve el resultado del mensaje al método
TriggerDotNetInstanceMethod
que realiza la llamada.
- Llama a
- El mensaje devuelto de
sayHello1
enresult
se muestra al usuario. - Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método
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();
}
}
Para pasar argumentos al método de instancia:
Agregue parámetros a la invocación del método de .NET. En el ejemplo siguiente, se pasa un nombre al método. Agregue parámetros adicionales a la lista según sea necesario.
<script> window.sayHello2 = (dotNetHelper, name) => { return dotNetHelper.invokeMethodAsync('GetHelloMessage', name); }; </script>
Proporcione la lista de parámetros al método de .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();
}
}
Ejemplos de instancias de clase
La siguiente función sayHello1
JS:
- Llama al método de .NET
GetHelloMessage
en la clase DotNetObjectReference pasada. - Devuelve el mensaje de
GetHelloMessage
al autor de la llamadasayHello1
.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
La clase HelloHelper
siguiente tiene un método de .NET invocable por JS llamado GetHelloMessage
. Cuando se crea HelloHelper
, el nombre de la propiedad Name
se usa para devolver un mensaje de 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}!";
}
El método CallHelloHelperGetHelloMessage
de la clase JsInteropClasses3
siguiente invoca la función sayHello1
de JS con una nueva instancia de HelloHelper
.
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();
}
}
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
Cuando se selecciona el botón Trigger .NET instance method
en el componente CallDotNetExample4
siguiente, se llama a JsInteropClasses3.CallHelloHelperGetHelloMessage
con el valor de name
.
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();
}
}
En la imagen siguiente se muestra el componente representado con el nombre Amy Pond
en el campo Name
. Una vez seleccionado el botón, Hello, Amy Pond!
se muestra en la interfaz de usuario:
El patrón anterior que se muestra en la clase JsInteropClasses3
también se puede implementar íntegramente en un componente.
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();
}
}
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
La salida mostrada por el componente CallDotNetExample5
es Hello, Amy Pond!
cuando el nombre Amy Pond
se proporciona en el campo Name
.
En el componente CallDotNetExample5
anterior, se elimina la referencia al objeto de .NET. Si una clase o un componente no elimina DotNetObjectReference, deséchelo del cliente llamando a dispose
en la clase DotNetObjectReference pasada:
window.jsFunction = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
dotNetHelper.dispose();
}
En el ejemplo anterior:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET.
Clase auxiliar del método de .NET de la instancia de componente
Una clase auxiliar puede invocar un método de instancia de .NET como Action. Las clases auxiliares son útiles en los siguientes escenarios:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En una aplicación de Blazor Server, donde varios usuarios usan simultáneamente el mismo componente.
En el ejemplo siguiente:
- El componente
CallDotNetExample6
contiene varios componentesListItem
, que es un componente compartido en la carpetaShared
de la aplicación. - Cada componente
ListItem
consta de un mensaje y un botón. - Cuando se selecciona un botón de componente
ListItem
, el métodoUpdateMessage
de ese objetoListItem
cambia el texto del elemento de lista y oculta el botón.
La clase MessageUpdateInvokeHelper
siguiente mantiene un método de .NET invocable por JS, UpdateMessageCaller
, para invocar el elemento Action especificado cuando se crea una instancia de la clase. BlazorSample
es el nombre del ensamblado de la aplicación.
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();
}
}
La siguiente función updateMessageCaller
JS invoca el método de .NET UpdateMessageCaller
. BlazorSample
es el nombre del ensamblado de la aplicación.
Dentro de la etiqueta </body>
de cierre de wwwroot/index.html
(Blazor WebAssembly) o Pages/_Host.cshtml
(Blazor Server):
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
El componente ListItem
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
). Cada instancia de componente ListItem
establece una instancia de MessageUpdateInvokeHelper
con un elemento Action establecido en su método UpdateMessage
.
Cuando se selecciona un botón InteropCall
del componente ListItem
, se invoca a updateMessageCaller
con un elemento DotNetObjectReference creado para la instancia MessageUpdateInvokeHelper
. Esto permite que el marco llame a UpdateMessageCaller
en esa instancia MessageUpdateInvokeHelper
de ListItem
. La clase DotNetObjectReference pasada se elimina en 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();
}
}
Se llama a StateHasChanged
para actualizar la interfaz de usuario cuando message
se establece en UpdateMessage
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna manera de saber que la interfaz de usuario debe actualizarse cuando se invoca a Action.
El siguiente componente primario CallDotNetExample6
incluye cuatro elementos de lista, y cada uno de ellos es una instancia del componente ListItem
.
Pages/CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem />
<ListItem />
<ListItem />
<ListItem />
</ul>
En la imagen siguiente se muestra el componente primario CallDotNetExample6
representado después de seleccionar el segundo botón InteropCall
:
- El segundo componente
ListItem
ha mostrado el mensajeUpdateMessage Called!
. - El botón
InteropCall
del segundo componenteListItem
no es visible porque la propiedaddisplay
de CSS del botón está establecida ennone
.
Ubicación de JavaScript
Cargue código de JavaScript (JS) mediante cualquiera de los enfoques descritos en el artículo de información general sobre interoperabilidad de JS:
- Cargar un script en el marcado
<head>
(en general, no se recomienda) - Cargar un script en el marcado
- Carga de un script desde un archivo externo (
.js
) - Inserción de un script después del inicio de
Advertencia
No coloque una etiqueta <script>
en un archivo de componente (.razor
), porque la etiqueta <script>
no se puede actualizar dinámicamente.
Evitar referencias de objetos circulares
Los objetos que contienen referencias circulares no se pueden serializar en el cliente para:
- Llamadas de método .NET.
- Llamadas de método JavaScript desde C# cuando el tipo de valor devuelto tiene referencias circulares.
Límites de tamaño en las llamadas de interoperabilidad de JavaScript
Esta sección solo se aplica a las aplicaciones Blazor Server. En Blazor WebAssembly, el marco no impone límites en cuanto al tamaño de las entradas y salidas de las llamadas de interoperabilidad de JavaScript (JS).
En Blazor Server, las llamadas de interoperabilidad de JS presentan un tamaño limitado por el tamaño máximo de los mensajes SignalR entrantes que se permite para los métodos del concentrador. Esto se aplica por medio de HubOptions.MaximumReceiveMessageSize (valor predeterminado: 32 KB). Los mensajes de JS a .NET SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente.
Cuando el registro de SignalR no está establecido en SignalR o Seguimiento, solo aparece un error relativo al tamaño del mensaje en la consola de herramientas de desarrollo del explorador:
Error: Conexión desconectada con el error "Error: El servidor ha devuelto un error al cerrarse: Conexión cerrada con un error.".
Cuando el registro del lado servidor de se establece en Depurar o Seguimiento, el registro del lado servidor inicia una excepción relativa a un error del tamaño del mensaje.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Error:
System.IO.InvalidDataException: Se ha superado el tamaño máximo del mensaje de 32768B. El tamaño del mensaje se puede configurar en AddHubOptions.
Para aumentar el límite, establezca MaximumReceiveMessageSize en Startup.ConfigureServices
:
services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
El aumento del límite de tamaño del mensaje entrante SignalR implica que se necesitan más recursos del servidor y lo expone a más riesgos por parte de un usuario malintencionado. Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.
Una opción para leer cargas grandes consiste en enviar el contenido en fragmentos más pequeños y procesar la carga como Stream. Se puede usar al leer cargas grandes de JSON o si los datos están disponibles en JS como bytes sin formato. Para obtener un ejemplo en el que se muestra el envío de cargas binarias de gran tamaño en Blazor Server que usa técnicas similares a las del Blazor Server, vea la InputFile
.
Nota:
Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta de una versión específica, use la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, vea Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).
Tenga en cuenta las instrucciones siguientes al desarrollar código que transfiera un gran volumen de datos entre JS y Blazor en aplicaciones Blazor Server:
- Segmente los datos en partes más pequeñas y envíe los segmentos de datos secuencialmente hasta que el servidor reciba todos los datos.
- No asigne objetos grandes en código JS y C#.
- No bloquee el subproceso de interfaz de usuario principal durante períodos largos al enviar o recibir datos.
- Libere la memoria consumida al completar o cancelar el proceso.
- Aplique los requisitos adicionales siguientes por motivos de seguridad:
- Declare el tamaño máximo del archivo o los datos que se pueden pasar.
- Declare la tasa mínima de carga desde el cliente al servidor.
- Después de que el servidor reciba los datos, los datos se pueden:
- Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
- Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.
Recursos adicionales
- Llamada a funciones de JavaScript con métodos de .NET en de ASP.NET CoreBlazor
- Ejemplo de (rama
main
del repositorio de GitHub dotnet/AspNetCore): la ramamain
representa el desarrollo actual de la unidad de producto para la próxima versión de ASP.NET Core. Para seleccionar la rama de una versión diferente (por ejemplo,release/5.0
), use la lista desplegable Switch branches or tags (Cambiar ramas o etiquetas). - Interacción con Document Object Model (DOM)
-
Repositorio de GitHub con ejemplos de Blazor (
dotnet/blazor-samples
)
Para obtener información sobre cómo llamar a funciones de JS desde .NET, vea Llamada a funciones de JavaScript con métodos de .NET en Blazor de ASP.NET Core.
Invocación de un método de .NET estático
Para invocar un método de .NET estático desde JavaScript (JS), use las funciones de JS:
DotNet.invokeMethodAsync
(Recomendado): asincrónico para las aplicaciones Blazor Server y Blazor WebAssembly.DotNet.invokeMethod
: solo sincrónico para las aplicaciones Blazor WebAssembly.
Pase el nombre del ensamblado que contiene el método, el identificador del método estático de .NET y cualquier argumento.
En el ejemplo siguiente:
- El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET. - El marcador de posición
{ARGUMENTS}
son argumentos opcionales separados por comas que se pasan al método , y cada uno de ellos debe ser serializable con JSON.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});
DotNet.invokeMethodAsync
devuelve un JS Promise
que representa el resultado de la operación. DotNet.invokeMethod
(solo Blazor WebAssembly) devuelve el resultado de la operación.
Importante
La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
El método de .NET debe ser público y estático, y debe tener el atributo [JSInvokable]
.
En el ejemplo siguiente:
- El marcador de posición
{<T>}
indica el tipo de valor devuelto, que solo es necesario para los métodos que devuelven un valor. - El marcador de posición
{.NET METHOD ID}
es el identificador del método.
@code {
[JSInvokable]
public static Task{<T>} {.NET METHOD ID}()
{
...
}
}
Nota
La llamada a métodos genéricos abiertos no se admite con métodos estáticos de .NET, pero se admite con métodos de instancia. Para obtener más información, vea la sección Llamada a métodos de clase genérica de .NET.
En el componente CallDotNetExample1
siguiente, el método de C# ReturnArrayAsync
devuelve una matriz int
. El atributo [JSInvokable]
se aplica al método, lo que hace que el método sea invocable por 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 });
}
}
El atributo HTML onclick
del elemento <button>
es la asignación del controlador de eventos onclick
de JavaScript para procesar eventos click
, y no el atributo de directiva @onclick
de Blazor. La función returnArrayAsync
JS se asigna como controlador.
La siguiente función returnArrayAsync
JS llama al método de .NET ReturnArrayAsync
del componente CallDotNetExample1
anterior y registra el resultado en la consola de herramientas para desarrolladores web del explorador. BlazorSample
es el nombre del ensamblado de la aplicación.
<script>
window.returnArrayAsync = () => {
DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
.then(data => {
console.log(data);
});
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
Cuando se selecciona el botón Trigger .NET static method
, la salida de la consola de las herramientas de desarrollo del explorador muestra los datos de la matriz. El formato de la salida difiere ligeramente entre los exploradores. En la salida siguiente se muestra el formato utilizado por Microsoft Edge:
Array(3) [ 1, 2, 3 ]
De forma predeterminada, el identificador de método de .NET para la llamada de JS es el nombre del método de .NET, pero puede especificar un identificador distinto mediante el constructor del atributo [JSInvokable]
. En el ejemplo siguiente, DifferentMethodName
es el identificador del método asignado para el método ReturnArrayAsync
:
[JSInvokable("DifferentMethodName")]
En la llamada a DotNet.invokeMethodAsync
o DotNet.invokeMethod
(solo Blazor WebAssembly), llame a DifferentMethodName
para ejecutar el método de .NET ReturnArrayAsync
:
DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
DotNet.invokeMethod('BlazorSample', 'DifferentMethodName');
(solo Blazor WebAssembly)
Nota
El ejemplo de método ReturnArrayAsync
de esta sección devuelve el resultado de un Task sin el uso de las palabras clave async
y await
de C# explícitas. La codificación de métodos con async
y await
es típica de los métodos que usan la palabra clave await
para devolver el valor de las operaciones asincrónicas.
El método ReturnArrayAsync
compuesto con las palabras clave async
y await
:
[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
return await Task.FromResult(new int[] { 1, 2, 3 });
}
Para obtener más información, vea Programación asincrónica con async y await en la guía de C#.
Invocación de un método de .NET de instancia
Para invocar un método de .NET de instancia desde JavaScript (JS):
- Pase la instancia de .NET por referencia a JS encapsulando la instancia en un DotNetObjectReference y llamando a Create en ella.
- Invoque un método de instancia de .NET desde JS mediante
invokeMethodAsync
oinvokeMethod
(solo Blazor WebAssembly) desde la clase DotNetObjectReference pasada. La instancia de .NET también se puede pasar como argumento al invocar otros métodos de .NET desde JS. - Deseche DotNetObjectReference.
En las siguientes secciones de este artículo muestran varios enfoques para invocar un método de .NET de instancia:
- Pasar
DotNetObjectReference
a una función de JavaScript individual - Pasar
DotNetObjectReference
a una clase con varias funciones de JavaScript - Llamada a métodos de clase genérica de .NET
- Ejemplos de instancias de clase
- Clase auxiliar del método de .NET de la instancia de componente
Pasar DotNetObjectReference
a una función de JavaScript individual
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una función JavaScript (JS) individual.
La función sayHello1
de JS siguiente recibe DotNetObjectReference y llama a invokeMethodAsync
para invocar el método de .NET GetHelloMessage
de un componente:
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para el componente CallDotNetExample2
siguiente:
- El componente tiene un método de .NET invocable por JS llamado
GetHelloMessage
. - Cuando se selecciona el botón
Trigger .NET instance method
, se llama a la funciónsayHello1
de JS con DotNetObjectReference. sayHello1
:- Llama a
GetHelloMessage
y recibe el resultado del mensaje. - Devuelve el resultado del mensaje al método
TriggerDotNetInstanceMethod
que realiza la llamada.
- Llama a
- El mensaje devuelto de
sayHello1
enresult
se muestra al usuario. - Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para pasar argumentos al método de instancia:
Agregue parámetros a la invocación del método de .NET. En el ejemplo siguiente, se pasa un nombre al método. Agregue parámetros adicionales a la lista según sea necesario.
<script> window.sayHello2 = (dotNetHelper, name) => { return dotNetHelper.invokeMethodAsync('GetHelloMessage', name); }; </script>
En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.Proporcione la lista de parámetros al método de .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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Pasar DotNetObjectReference
a una clase con varias funciones de JavaScript
En el ejemplo de esta sección enseña cómo pasar DotNetObjectReference a una clase JavaScript (JS) con varias funciones.
Cree y pase DotNetObjectReference desde el método de ciclo de vida OnAfterRenderAsync
a una clase JS para usar varias funciones. Asegúrese de que el código .NET elimina DotNetObjectReference, como se muestra en el ejemplo siguiente.
En el componente CallDotNetExampleOneHelper
siguiente, los botones Trigger JS function
llaman a funciones JS estableciendo la propiedad JSonclick
, no el atributo de directiva @onclick
de Blazor.
Pages/CallDotNetExampleOneHelper.razor
:
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@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 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();
}
}
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El componente debe eliminar explícitamente DotNetObjectReference para permitir la recolección de elementos no utilizados y evitar una pérdida de memoria.
<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>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior:
- La clase
GreetingHelpers
se agrega al objetowindow
para definir globalmente la clase, lo que permite a Blazor buscar la clase para la interoperabilidad de JS. - En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Llamada a métodos de clase genérica de .NET
Las funciones JavaScript (JS) pueden llamar a métodos de clase genérica de .NET, donde una función JS llama a un método .NET de una clase genérica.
En la clase de tipo genérico siguiente (GenericType<TValue>
):
- La clase tiene un único parámetro de tipo (
TValue
) con una sola propiedadValue
genérica. - La clase tiene dos métodos no genéricos marcados con el atributo
[JSInvokable]
, cada uno con un parámetro de tipo genérico denominadonewValue
:Update
actualiza sincrónicamente el valor deValue
desdenewValue
.UpdateAsync
actualiza de asincrónicamente el valor deValue
desdenewValue
después de crear una tarea por la que se puede esperar con Task.Yield, que vuelve a suspenderse asincrónicamente al contexto actual cuando se espera por dicho elemento.
- Cada uno de los métodos de clase escribe el tipo de
TValue
y el valor deValue
en la consola. La escritura en la consola solo tiene fines de demostración. Las aplicaciones de producción normalmente evitan escribir en la consola en favor del registro de la aplicación. Para obtener más información, consulte Registro de Blazor en ASP.NET Core y Registro en .NET Core y ASP.NET Core.
Nota
Los tipos y métodos genéricos abiertos no especifican tipos para los marcadores de posición de tipo. Por el contrario, los genéricos cerrados suministran tipos para todos los marcadores de posición de tipo. Los ejemplos de esta sección muestran genéricos cerrados, pero la invocación de interoperabilidad de JS de métodos de instancia con genéricos abiertos se admite. El uso de genéricos abiertos no se admite para las invocaciones de métodos estáticos de .NET, que se describieron anteriormente en este artículo.
Para más información, consulte los siguientes artículos.
- Clases y métodos genéricos (documentación de C#)
- Clases genéricas (Guía de programación de C#)
- Genéricos en .NET (documentación de .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}");
}
}
En la siguiente función invokeMethodsAsync
:
- Los métodos
Update
yUpdateAsync
de la clase de tipo genérico se llaman con argumentos que representan cadenas y números. - Las aplicaciones Blazor WebAssembly admiten llamar a métodos de .NET sincrónicamente con
invokeMethod
.syncInterop
recibe un valor booleano que indica si la inteoperabilidad de JS se está produciendo en una aplicación Blazor WebAssembly. CuandosyncInterop
estrue
, se llama ainvokeMethod
de forma segura. Si el valor desyncInterop
esfalse
, solo se llama a la función asincrónicainvokeMethodAsync
porque la inteoperabilidad de JS se ejecuta en una aplicación Blazor Server. - Con fines de demostración, la llamada de función DotNetObjectReference (
invokeMethod
oinvokeMethodAsync
), el método de .NET denominado (Update
oUpdateAsync
) y el argumento se escriben en la consola. Los argumentos usan un número aleatorio para permitir que la función JS llame a la invocación del método .NET (también escrita en la consola en el lado de .NET). Normalmente, el código de producción no escribe en la consola, ni en el cliente ni en el servidor. Las aplicaciones de producción normalmente se basan en el registro de la aplicación. Para obtener más información, consulte Registro de Blazor en ASP.NET Core y Registro en .NET Core y 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>
Nota
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el componente GenericsExample
siguiente:
- Se llama a la función JS
invokeMethodsAsync
cuando se selecciona el botónInvoke Interop
. - Se crea un par de tipos DotNetObjectReference y se pasan a la función JS para las instancias de
GenericType
comostring
yint
.
Pages/GenericsExample.razor
:
@page "/generics-example"
@using System.Runtime.InteropServices
@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 };
public async Task InvokeInterop()
{
var syncInterop =
RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
await JS.InvokeVoidAsync(
"invokeMethodsAsync",
syncInterop,
DotNetObjectReference.Create(genericType1),
DotNetObjectReference.Create(genericType2));
}
}
En el ejemplo anterior, JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.
A continuación se muestra la salida típica del ejemplo anterior cuando se selecciona el botón Invoke Interop
en una aplicación Blazor WebAssembly:
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
Si el ejemplo anterior se implementa en una aplicación Blazor Server, se evitan las llamadas sincrónicas con invokeMethod
. La función asincrónica (invokeMethodAsync
) se prefiere a la versión sincrónica (invokeMethod
) para admitir escenarios de Blazor Server.
Salida típica de una aplicación Blazor Server:
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
Los ejemplos de salida anteriores muestran que los métodos asincrónicos se ejecutan y completan en un orden arbitrario en función de varios factores, incluida la programación de subprocesos y la velocidad de ejecución del método. No es posible predecir de forma confiable el orden de finalización de las llamadas a métodos asincrónicos.
Ejemplos de instancias de clase
La siguiente función sayHello1
JS:
- Llama al método de .NET
GetHelloMessage
en la clase DotNetObjectReference pasada. - Devuelve el mensaje de
GetHelloMessage
al autor de la llamadasayHello1
.
<script>
window.sayHello1 = (dotNetHelper) => {
return dotNetHelper.invokeMethodAsync('GetHelloMessage');
};
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
La clase HelloHelper
siguiente tiene un método de .NET invocable por JS llamado GetHelloMessage
. Cuando se crea HelloHelper
, el nombre de la propiedad Name
se usa para devolver un mensaje de GetHelloMessage
.
HelloHelper.cs
:
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}!";
}
El método CallHelloHelperGetHelloMessage
de la clase JsInteropClasses3
siguiente invoca la función sayHello1
de JS con una nueva instancia de HelloHelper
.
JsInteropClasses3.cs
:
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
Cuando se selecciona el botón Trigger .NET instance method
en el componente CallDotNetExample4
siguiente, se llama a JsInteropClasses3.CallHelloHelperGetHelloMessage
con el valor de name
.
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();
}
}
En la imagen siguiente se muestra el componente representado con el nombre Amy Pond
en el campo Name
. Una vez seleccionado el botón, Hello, Amy Pond!
se muestra en la interfaz de usuario:
El patrón anterior que se muestra en la clase JsInteropClasses3
también se puede implementar íntegramente en un componente.
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();
}
}
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
Para evitar una pérdida de memoria y permitir la recolección de elementos no utilizados, la referencia de objeto de .NET creada por DotNetObjectReference se elimina en el método Dispose
.
La salida mostrada por el componente CallDotNetExample5
es Hello, Amy Pond!
cuando el nombre Amy Pond
se proporciona en el campo Name
.
En el componente CallDotNetExample5
anterior, se elimina la referencia al objeto de .NET. Si una clase o un componente no elimina DotNetObjectReference, deséchelo del cliente llamando a dispose
en la clase DotNetObjectReference pasada:
window.jsFunction = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}');
dotNetHelper.dispose();
}
En el ejemplo anterior:
- En el ejemplo anterior, el nombre de la variable
dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido. - El marcador de posición
{ASSEMBLY NAME}
es el nombre de ensamblado de la aplicación. - El marcador de posición
{.NET METHOD ID}
es el identificador del método de .NET.
Clase auxiliar del método de .NET de la instancia de componente
Una clase auxiliar puede invocar un método de instancia de .NET como Action. Las clases auxiliares son útiles en los siguientes escenarios:
- Cuando se representan varios componentes del mismo tipo en la misma página.
- En una aplicación de Blazor Server, donde varios usuarios usan simultáneamente el mismo componente.
En el ejemplo siguiente:
- El componente
CallDotNetExample6
contiene varios componentesListItem
, que es un componente compartido en la carpetaShared
de la aplicación. - Cada componente
ListItem
consta de un mensaje y un botón. - Cuando se selecciona un botón de componente
ListItem
, el métodoUpdateMessage
de ese objetoListItem
cambia el texto del elemento de lista y oculta el botón.
La clase MessageUpdateInvokeHelper
siguiente mantiene un método de .NET invocable por JS, UpdateMessageCaller
, para invocar el elemento Action especificado cuando se crea una instancia de la clase. BlazorSample
es el nombre del ensamblado de la aplicación.
MessageUpdateInvokeHelper.cs
:
using Microsoft.JSInterop;
public class MessageUpdateInvokeHelper
{
private Action action;
public MessageUpdateInvokeHelper(Action action)
{
this.action = action;
}
[JSInvokable("BlazorSample")]
public void UpdateMessageCaller()
{
action.Invoke();
}
}
La siguiente función updateMessageCaller
JS invoca el método de .NET UpdateMessageCaller
. BlazorSample
es el nombre del ensamblado de la aplicación.
<script>
window.updateMessageCaller = (dotNetHelper) => {
dotNetHelper.invokeMethodAsync('BlazorSample', 'UpdateMessageCaller');
dotNetHelper.dispose();
}
</script>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
En el ejemplo anterior, el nombre de la variable dotNetHelper
es arbitrario y se puede cambiar a cualquier nombre preferido.
El componente ListItem
siguiente es un componente compartido que se puede usar varias veces en un componente primario y crea elementos de lista (<li>...</li>
) para una lista HTML (<ul>...</ul>
o <ol>...</ol>
). Cada instancia de componente ListItem
establece una instancia de MessageUpdateInvokeHelper
con un elemento Action establecido en su método UpdateMessage
.
Cuando se selecciona un botón InteropCall
del componente ListItem
, se invoca a updateMessageCaller
con un elemento DotNetObjectReference creado para la instancia MessageUpdateInvokeHelper
. Esto permite que el marco llame a UpdateMessageCaller
en esa instancia MessageUpdateInvokeHelper
de ListItem
. La clase DotNetObjectReference pasada se elimina en 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()
{
if (messageUpdateInvokeHelper is not null)
{
await JS.InvokeVoidAsync("updateMessageCaller",
DotNetObjectReference.Create(messageUpdateInvokeHelper));
}
}
private void UpdateMessage()
{
message = "UpdateMessage Called!";
display = "none";
StateHasChanged();
}
}
Se llama a StateHasChanged
para actualizar la interfaz de usuario cuando message
se establece en UpdateMessage
. Si no se llama a StateHasChanged
, Blazor no tiene ninguna manera de saber que la interfaz de usuario debe actualizarse cuando se invoca a Action.
El siguiente componente primario CallDotNetExample6
incluye cuatro elementos de lista, y cada uno de ellos es una instancia del componente ListItem
.
Pages/CallDotNetExample6.razor
:
@page "/call-dotnet-example-6"
<h1>Call .NET Example 6</h1>
<ul>
<ListItem />
<ListItem />
<ListItem />
<ListItem />
</ul>
En la imagen siguiente se muestra el componente primario CallDotNetExample6
representado después de seleccionar el segundo botón InteropCall
:
- El segundo componente
ListItem
ha mostrado el mensajeUpdateMessage Called!
. - El botón
InteropCall
del segundo componenteListItem
no es visible porque la propiedaddisplay
de CSS del botón está establecida ennone
.
Interoperabilidad JS sincrónica en aplicaciones Blazor WebAssembly
Esta sección solo se aplica a las aplicaciones .
De forma predeterminada, las llamadas de interoperabilidad de JS son asincrónicas, independientemente de si el código al que se llama es sincrónico o asincrónico. Las llamadas son asincrónicas de forma predeterminada para asegurarse de que los componentes son compatibles en ambos modelos Blazor de hospedaje, Blazor Server y Blazor WebAssembly. En Blazor Server, todas las llamadas de interoperabilidad de JS deben ser asincrónicas porque se envían a través de una conexión de red.
Si sabe con certeza que la aplicación solo se ejecuta en Blazor WebAssembly, puede optar por realizar llamadas de interoperabilidad de JS sincrónicas. Esto tiene una sobrecarga ligeramente menor que la realización de llamadas asincrónicas y puede dar lugar a menos ciclos de representación porque no hay ningún estado intermedio mientras se esperan los resultados.
Para hacer una llamada sincrónica desde JavaScript a .NET en las aplicaciones de Blazor WebAssembly, use DotNet.invokeMethod
en lugar de DotNet.invokeMethodAsync
.
Las llamadas sincrónicas funcionan si:
- La aplicación se ejecuta en Blazor WebAssembly, no en Blazor Server.
- La función llamada devuelve un valor de forma sincrónica. La función no es un método
async
y no devuelve un valor Task de .NET oPromise
de JavaScript.
Ubicación de JavaScript
Cargue código de JavaScript (JS) mediante cualquiera de los enfoques descritos en el artículo de información general sobre interoperabilidad de JS:
- Cargar un script en el marcado
<head>
(en general, no se recomienda) - Cargar un script en el marcado
- Carga de un script desde un archivo externo (
.js
) - Inserción de un script después del inicio de
Para obtener información sobre cómo aislar scripts en los módulos , consulte la sección Aislamiento de JavaScript en módulos de JavaScript.
Advertencia
No coloque una etiqueta <script>
en un archivo de componente (.razor
), porque la etiqueta <script>
no se puede actualizar dinámicamente.
Aislamiento de JavaScript en módulos de JavaScript
Blazor permite el aislamiento de JavaScript (JS) en Blazor estándar (JS).
El aislamiento de JS proporciona las siguientes ventajas:
- El JS importado no contamina el espacio de nombres global.
- No es necesario que los consumidores de una biblioteca y los componentes importen el código de JS relacionado.
Para más información, vea Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
Evitar referencias de objetos circulares
Los objetos que contienen referencias circulares no se pueden serializar en el cliente para:
- Llamadas de método .NET.
- Llamadas de método JavaScript desde C# cuando el tipo de valor devuelto tiene referencias circulares.
Compatibilidad con matrices de bytes
Blazor admite la interoperabilidad de JavaScript de matriz de bytes optimizada (JS) que evita la codificación o descodificación de matrices de bytes en Base64. En el ejemplo siguiente se usa la interoperabilidad de JS para pasar una matriz de bytes a .NET.
Proporcione una función sendByteArray
de JS. Un botón del componente llama a la función y no devuelve un valor:
<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>
Nota:
Para una guía general sobre la ubicación de JS y nuestras recomendaciones para aplicaciones de producción, consulte Interoperabilidad de ASP.NET Core Blazor JavaScript (Interoperabilidad de JS).
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 ©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));
}
}
Para obtener información sobre el uso de una matriz de bytes al llamar a JavaScript desde .NET, vea Llamada a funciones de JavaScript desde métodos de .NET en ASP.NET Core Blazor.
Transmisión de JavaScript a .NET
Blazor admite el streaming de datos directamente desde JavaScript a .NET. Los flujos se solicitan mediante la interfaz Microsoft.JSInterop.IJSStreamReference
.
Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync
devuelve un objeto Stream y usa los siguientes parámetros:
maxAllowedSize
: número máximo de bytes permitidos para la operación de lectura de JavaScript, cuyo valor predeterminado es 512 000 bytes si no se especifica.cancellationToken
: un objeto CancellationToken para cancelar la lectura.
En JavaScript:
function streamToDotNet() {
return new Uint8Array(10000000);
}
En código de 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);
En el ejemplo anterior:
JS
es una instancia de IJSRuntime insertada. El marco Blazor registra IJSRuntime.dataReferenceStream
se escribe en el disco (file.txt
) en la ruta de la carpeta temporal del usuario actual (GetTempPath).
En Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core se describe la operación inversa, el streaming desde .NET a JavaScript mediante un objeto DotNetStreamReference.
En Cargas de archivos de Blazor en ASP.NET Core se describe cómo cargar un archivo en Blazor.
Límites de tamaño en las llamadas de interoperabilidad de JavaScript
Esta sección solo se aplica a las aplicaciones Blazor Server. En Blazor WebAssembly, el marco no impone límites en cuanto al tamaño de las entradas y salidas de las llamadas de interoperabilidad de JavaScript (JS).
En Blazor Server, las llamadas de interoperabilidad de JS presentan un tamaño limitado por el tamaño máximo de los mensajes SignalR entrantes que se permite para los métodos del concentrador. Esto se aplica por medio de HubOptions.MaximumReceiveMessageSize (valor predeterminado: 32 KB). Los mensajes de JS a .NET SignalR mayores que MaximumReceiveMessageSize producen un error. El marco no impone ningún límite de tamaño para un mensaje SignalR desde el concentrador a un cliente.
Cuando el registro de SignalR no está establecido en SignalR o Seguimiento, solo aparece un error relativo al tamaño del mensaje en la consola de herramientas de desarrollo del explorador:
Error: Conexión desconectada con el error "Error: El servidor ha devuelto un error al cerrarse: Conexión cerrada con un error.".
Cuando el registro del lado servidor de se establece en Depurar o Seguimiento, el registro del lado servidor inicia una excepción relativa a un error del tamaño del mensaje.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Error:
System.IO.InvalidDataException: Se ha superado el tamaño máximo del mensaje de 32768B. El tamaño del mensaje se puede configurar en AddHubOptions.
Para aumentar el límite, establezca MaximumReceiveMessageSize en Program.cs
. En el ejemplo siguiente se establece el tamaño máximo del mensaje de recepción en 64 KB (64*1024):
builder.Services.AddServerSideBlazor()
.AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024);
El aumento del límite de tamaño del mensaje entrante SignalR implica que se necesitan más recursos del servidor y lo expone a más riesgos por parte de un usuario malintencionado. Además, la lectura de una gran cantidad de contenido en la memoria, como cadenas o matrices de bytes, también puede dar lugar a que las asignaciones funcionen de forma deficiente con el recolector de elementos no utilizados, lo que puede reducir significativamente el rendimiento.
Tenga en cuenta las instrucciones siguientes al desarrollar código que transfiera un gran volumen de datos entre JS y Blazor en aplicaciones Blazor Server:
- Aproveche la compatibilidad de interoperabilidad de transmisión nativa para transferir datos mayores que el límite de tamaño del mensaje entrante SignalR:
- Sugerencias generales:
- No asigne objetos grandes en código JS y C#.
- Libere la memoria consumida al completar o cancelar el proceso.
- Aplique los requisitos adicionales siguientes por motivos de seguridad:
- Declare el tamaño máximo del archivo o los datos que se pueden pasar.
- Declare la tasa mínima de carga desde el cliente al servidor.
- Después de que el servidor reciba los datos, los datos se pueden:
- Almacenar temporalmente en un búfer de memoria hasta que se recopilen todos los segmentos.
- Consumir inmediatamente. Por ejemplo, los datos se pueden almacenar inmediatamente en una base de datos o escribir en el disco a medida que se reciba cada segmento.
Recursos adicionales
- Llamada a funciones de JavaScript con métodos de .NET en de ASP.NET CoreBlazor
- Ejemplo de (rama
main
del repositorio de GitHub dotnet/AspNetCore): la ramamain
representa el desarrollo actual de la unidad de producto para la próxima versión de ASP.NET Core. Para seleccionar la rama de una versión diferente (por ejemplo,release/5.0
), use la lista desplegable Switch branches or tags (Cambiar ramas o etiquetas). - Interacción con Document Object Model (DOM)
-
Repositorio de GitHub con ejemplos de Blazor (
dotnet/blazor-samples
)