Compartir vía


Enrutamiento y navegación de Blazor de ASP.NET Core

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión de .NET 9 de este artículo.

En este artículo se explica cómo administrar el enrutamiento de solicitudes de la aplicación Blazor y a usar el componente NavLink para crear vínculos de navegación.

Importante

En los ejemplos de código de este artículo se muestran los métodos llamados en Navigation, que es un NavigationManager insertado en clases y componentes.

Enrutamiento estático frente a interactivo

Esta sección es aplicable a Blazor Web App.

Si la representación previa está habilitada, el router Blazor (componente Router, <Router> en Routes.razor) realiza el enrutamiento estático a los componentes durante la representación estática del lado del servidor (SSR estático). Este tipo de enrutamiento se denomina enrutamiento estático.

Cuando se asigna un modo de representación interactivo al componente Routes, el enrutador Blazor se vuelve interactivo después de SSR estático con enrutamiento estático en el servidor. Este tipo de enrutamiento se denomina enrutamiento interactivo.

Los enrutadores estáticos usan el enrutamiento de puntos de conexión y la ruta de acceso de la solicitud HTTP para determinar qué componente se va a representar. Cuando el enrutador se vuelve interactivo, usa la dirección URL del documento (la dirección URL de la barra de direcciones del explorador) para determinar qué componente se va a representar. Esto significa que el enrutador interactivo puede cambiar dinámicamente qué componente se representa si la dirección URL del documento cambia dinámicamente a otra dirección URL interna válida y puede hacerlo sin realizar una solicitud HTTP para capturar nuevo contenido de página.

El enrutamiento interactivo también impide la representación previa porque no se solicita contenido de página nueva desde el servidor con una solicitud de página normal. Para obtener más información, consulta Representación previa de componentes Razor de ASP.NET Core .

Plantillas de ruta

El componente Router permite el enrutamiento a los componentes Razor y se encuentra en el componente Routes de la aplicación (Components/Routes.razor).

El componente Router habilita el enrutamiento a los componentes Razor. El componente Router se usa en el componente App (App.razor).

Cuando se compila un componente Razor (.razor) con una directiva @page, la clase de componente generada recibe un elemento RouteAttribute que especifica la plantilla de ruta del componente.

Cuando se inicia la aplicación, el ensamblado especificado como AppAssembly del enrutador se examina para recopilar información de ruta de los componentes de la aplicación que tienen un elemento RouteAttribute.

En tiempo de ejecución, el componente RouteView:

  • Recibe el elemento RouteData de Router junto con los parámetros de ruta.
  • Representa el componente especificado con su diseño, incluidos los diseños anidados adicionales.

Opcionalmente, se puede especificar un parámetro DefaultLayout con una clase de diseño para los componentes que no tengan especificado un diseño con la directiva @layout. Las plantillas de proyecto de Blazor del marco especifican el componente MainLayout (MainLayout.razor) como diseño predeterminado de la aplicación. Para obtener más información sobre los diseños, consulta Diseños de Blazor en ASP.NET Core.

Los componentes admiten varias plantillas de ruta mediante varias directivas @page. El componente de ejemplo siguiente se carga en las solicitudes de /blazor-route y /different-blazor-route.

BlazorRoute.razor:

@page "/blazor-route"
@page "/different-blazor-route"

<PageTitle>Routing</PageTitle>

<h1>Routing Example</h1>

<p>
    This page is reached at either <code>/blazor-route</code> or 
    <code>/different-blazor-route</code>.
</p>
@page "/blazor-route"
@page "/different-blazor-route"

<PageTitle>Routing</PageTitle>

<h1>Routing Example</h1>

<p>
    This page is reached at either <code>/blazor-route</code> or 
    <code>/different-blazor-route</code>.
</p>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>

Importante

Para que las direcciones URL se resuelvan correctamente, la aplicación debe incluir una etiqueta <base> (ubicación del contenido de <head>) con la ruta de acceso base de la aplicación especificada en el atributo href. Para obtener más información, consulta Hospedaje e implementación de ASP.NET Core Blazor.

Router no interactúa con los valores de cadena de consulta. Para trabajar con cadenas de consulta, consulta la sección Cadena de consulta.

Como alternativa a especificar la plantilla de ruta como literal de cadena con la directiva @page, se pueden especificar plantillas de ruta basadas en constantes con la directiva @attribute.

En el ejemplo siguiente, la directiva @page de un componente se reemplaza por la directiva @attribute y la plantilla de ruta basada en constantes en Constants.CounterRoute, que se establece en otra parte de la aplicación en "/counter”:

- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]

Nota

Con la publicación de ASP.NET Core 5.0.1 y para las versiones 5.x adicionales, el componente Router incluye el parámetro PreferExactMatches establecido en @true. Para más información, vea Migración de ASP.NET Core 3.1 a 5.0.

Foco en un elemento de la navegación

El componente FocusOnNavigate establece el foco de la interfaz de usuario en un elemento basado en un selector CSS después de navegar de una página a otra.

<FocusOnNavigate RouteData="routeData" Selector="h1" />

Cuando el componente Router navega a una página nueva, el componente FocusOnNavigate establece el foco en el encabezado de nivel superior de la página (<h1>). Se trata de una estrategia común para asegurarse de que se anuncia una navegación de página cuando se usa un lector de pantalla.

Suministro de contenido personalizado cuando no se encuentra contenido

El componente Router permite a la aplicación especificar contenido personalizado si no se encuentra contenido para la ruta solicitada.

Establezca contenido personalizado para el parámetro Router del componente NotFound:

<Router ...>
    ...
    <NotFound>
        ...
    </NotFound>
</Router>

Los elementos arbitrarios se admiten como contenido de los parámetros NotFound, como otros componentes interactivos. Para aplicar un diseño predeterminado al contenido de NotFound, vea Diseños de Blazor en ASP.NET Core.

Importante

Los objetos Blazor Web App no usan el parámetro NotFound (marcado <NotFound>...</NotFound>), pero el parámetro es compatible con versiones anteriores para evitar un cambio importante en el marco. La canalización de middleware del lado servidor ASP.NET procesa las solicitudes en el servidor. Usa técnicas del lado servidor para controlar las solicitudes incorrectas. Para obtener más información, vea Modos de representación de ASP.NET CoreBlazor.

Ruta a componentes desde varios ensamblados

Esta sección es aplicable a Blazor Web App.

Use el parámetro AdditionalAssemblies del componente Router y el constructor de convenciones de punto de conexión AddAdditionalAssemblies para descubrir componentes enrutables en ensamblados adicionales. Las siguientes subsecciones explican cuándo y cómo usar cada API.

Enrutamiento estático

Para detectar componentes enrutables de ensamblados adicionales para la representación estática del lado servidor (SSR estático), incluso si el enrutador se convierte más adelante en interactivo para la representación interactiva, los ensamblados deben revelarse al marco Blazor. Llame al método AddAdditionalAssemblies con los ensamblados adicionales encadenados a MapRazorComponents en el archivo Program del proyecto del servidor.

El siguiente ejemplo incluye los componentes enrutables en el ensamblado del proyecto de BlazorSample.Client usando el archivo _Imports.razor del proyecto:

app.MapRazorComponents<App>()
    .AddAdditionalAssemblies(typeof(BlazorSample.Client._Imports).Assembly);

Nota:

La guía anterior también se aplica en escenarios de biblioteca de clases de componentes. Encontrará orientación adicional importante para las bibliotecas de clases y SSR estático en Bibliotecas de clases de ASP.NET Core Razor (RCL) con representación estática del lado del servidor (SSR estático).

Enrutamiento interactivo

Se puede asignar un modo de enrutador interactivo al componente de Routes (Routes.razor) que hace que el enrutador de Blazor se vuelva interactivo tras el SSR estático y el enrutamiento estático en el servidor. Por ejemplo, <Routes @rendermode="InteractiveServer" /> asigna la representación interactiva del lado servidor (SSR interactiva) al componente Routes. El componente Router hereda la representación interactiva del lado del servidor (SSR interactiva) del componente Routes. El enrutador se vuelve interactivo después del enrutamiento estático en el servidor.

La navegación interna para el enrutamiento interactivo no implica solicitar contenido de página nuevo desde el servidor. Por lo tanto, la representación previa no se produce para las solicitudes de página internas. Para más información, consulte Representación previa de componentes Razor de ASP.NET Core .

Si el componente Routes está definido en el proyecto del servidor, el parámetro AdditionalAssemblies del componente Router debería incluir el ensamblado del proyecto .Client. Esto permite que el enrutador funcione correctamente cuando se representa de forma interactiva.

En el siguiente ejemplo, el componente Routes se encuentra en el proyecto de servidor, y el archivo _Imports.razor del proyecto BlazorSample.Client indica el ensamblado para buscar componentes enrutables:

<Router
    AppAssembly="..."
    AdditionalAssemblies="new[] { typeof(BlazorSample.Client._Imports).Assembly }">
    ...
</Router>

Se examinan los ensamblados adicionales, además del ensamblado especificado para AppAssembly.

Nota:

La guía anterior también se aplica en escenarios de biblioteca de clases de componentes.

Como alternativa, los componentes enrutables solo existen en el proyecto .Client con WebAssembly interactivo global o representación automática aplicada, y el componente Routes se define en el proyecto .Client, no en el proyecto servidor. En este caso, no hay ensamblados externos con componentes enrutables, por lo que no es necesario especificar un valor para AdditionalAssemblies.

Esta sección es aplicable a aplicaciones Blazor Server.

Use el parámetro AdditionalAssemblies del componente Router y el constructor de convenciones de punto de conexión AddAdditionalAssemblies para descubrir componentes enrutables en ensamblados adicionales.

En el siguiente ejemplo, Component1 es un componente enrutable definido en una biblioteca de clases de componentes llamada ComponentLibrary:

<Router
    AppAssembly="..."
    AdditionalAssemblies="new[] { typeof(ComponentLibrary.Component1).Assembly }">
    ...
</Router>

Se examinan los ensamblados adicionales, además del ensamblado especificado para AppAssembly.

Parámetros de ruta

El enrutador usa parámetros de ruta para rellenar los parámetros de componente correspondientes con el mismo nombre. Los nombres de parámetros de ruta no distinguen mayúsculas de minúsculas. En el ejemplo siguiente, el parámetro text asigna el valor del segmento de ruta a la propiedad Text del componente. Cuando se realiza una solicitud para /route-parameter-1/amazing, el contenido se representa como Blazor is amazing!.

RouteParameter1.razor:

@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}

Se admiten parámetros opcionales. En el ejemplo siguiente, el parámetro opcional text asigna el valor del segmento de ruta a la propiedad Text del componente. Si el segmento no está presente, el valor de Text se establece en fantastic.

No se admiten parámetros opcionales. En el ejemplo siguiente, se aplican dos directivas @page. La primera directiva permite navegar al componente sin un parámetro, mientras que la segunda directiva asigna el valor del parámetro de ruta {text} a la propiedad Text del componente.

RouteParameter2.razor:

@page "/route-parameter-2/{text?}"

<PageTitle>Route Parameter 2</PageTitle>

<h1>Route Parameter Example 2</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet() => Text = Text ?? "fantastic";
}
@page "/route-parameter-2/{text?}"

<PageTitle>Route Parameter 2</PageTitle>

<h1>Route Parameter Example 2</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet() => Text = Text ?? "fantastic";
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2"
@page "/route-parameter-2/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}

Cuando se usa el método del ciclo de vida OnInitialized{Async} en lugar del método del ciclo de vida OnParametersSet{Async}, la asignación predeterminada de la propiedad Text a fantastic no se produce si el usuario navega dentro del mismo componente. Por ejemplo, esta situación surge cuando el usuario navega de /route-parameter-2/amazing a /route-parameter-2. A medida que la instancia del componente persiste y acepta nuevos parámetros, el método OnInitialized no se invoca de nuevo.

Nota:

Los parámetros de ruta no funcionan con los valores de cadena de consulta. Para trabajar con cadenas de consulta, consulta la sección Cadena de consulta.

Restricciones de ruta

Una restricción de ruta fuerza la coincidencia de tipos en un segmento de ruta a un componente.

En el siguiente ejemplo, la ruta al componente User solo coincide en estos casos:

  • Existe un segmento de ruta Id en la dirección URL de la solicitud.
  • El segmento Id es un tipo de entero (int).

User.razor:

@page "/user/{Id:int}"

<PageTitle>User</PageTitle>

<h1>User Example</h1>

<p>User Id: @Id</p>

@code {
    [Parameter]
    public int Id { get; set; }
}

Nota:

Las restricciones de ruta no funcionan con los valores de cadena de consulta. Para trabajar con cadenas de consulta, consulta la sección Cadena de consulta.

En la siguiente tabla figuran las restricciones de ruta que hay disponibles. Para obtener más información sobre las restricciones de ruta que coinciden con la referencia cultural invariable, consulta la advertencia que aparece después de la tabla.

Restricción Ejemplo Coincidencias de ejemplo Invariable
referencia cultural
coincidencia
bool {active:bool} true, FALSE No
datetime {dob:datetime} 2016-12-31, 2016-12-31 7:32pm
decimal {price:decimal} 49.99, -1,000.01
double {weight:double} 1.234, -1,001.01e8
float {weight:float} 1.234, -1,001.01e8
guid {id:guid} 00001111-aaaa-2222-bbbb-3333cccc4444, {00001111-aaaa-2222-bbbb-3333cccc4444} No
int {id:int} 123456789, -123456789
long {ticks:long} 123456789, -123456789
nonfile {parameter:nonfile} No BlazorSample.styles.css, no favicon.ico

Advertencia

Las restricciones de ruta que comprueban la dirección URL y que se convierten en un tipo CLR (como int o DateTime) usan siempre la referencia cultural invariable. Estas restricciones dan por supuesto que la dirección URL no es localizable.

Las restricciones de ruta también funcionan con parámetros opcionales. En el ejemplo siguiente, Id es obligatorio, pero Option es un parámetro de ruta booleano opcional.

User.razor:

@page "/user/{id:int}/{option:bool?}"

<p>
    Id: @Id
</p>

<p>
    Option: @Option
</p>

@code {
    [Parameter]
    public int Id { get; set; }

    [Parameter]
    public bool Option { get; set; }
}

Evitar la captura de archivos en un parámetro de ruta

La siguiente plantilla de ruta captura accidentalmente las rutas de acceso de recursos estáticos en su parámetro de ruta opcional (Optional). Por ejemplo, se captura la hoja de estilos de la aplicación (.styles.css), que interrumpe los estilos de la aplicación:

@page "/{optional?}"

...

@code {
    [Parameter]
    public string? Optional { get; set; }
}

Para restringir un parámetro de ruta para capturar rutas de acceso que no son de archivo, usa la restricción :nonfile en la plantilla de ruta:

@page "/{optional:nonfile?}"

Enrutamiento con direcciones URL que contienen puntos

Una plantilla de ruta predeterminada del lado servidor supone que si el último segmento de una dirección URL de solicitud contiene un punto (.) es porque se solicita un archivo. Por ejemplo, el enrutador interpreta la dirección URL relativa /example/some.thing como una solicitud de un archivo denominado some.thing. Sin configuración adicional, una aplicación devuelve una respuesta 404: no encontrado si se pretendía enrutar some.thing a un componente con una directiva @page y some.thing es un valor de parámetro de ruta. Para usar una ruta con uno o más parámetros que contengan un punto, la aplicación debe configurar la ruta con una plantilla personalizada.

Ten en cuenta el siguiente componente Example que puede recibir un parámetro de ruta del último segmento de la dirección URL.

Example.razor:

@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string? Param { get; set; }
}
@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string? Param { get; set; }
}
@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string Param { get; set; }
}
@page "/example"
@page "/example/{param}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string Param { get; set; }
}

Para permitir que la aplicación Server de una Blazor WebAssemblysolución hospedada enrute la solicitud con un punto en el parámetro de ruta param, agrega una plantilla de ruta de archivo de reserva con el parámetro opcional en el archivo Program:

app.MapFallbackToFile("/example/{param?}", "index.html");

Para configurar una aplicación Blazor Server para enrutar la solicitud con un punto en el parámetro de ruta param, agrega una plantilla de ruta de página de reserva con el parámetro opcional en el archivo Program:

app.MapFallbackToPage("/example/{param?}", "/_Host");

Para obtener más información, consulta Enrutamiento en ASP.NET Core.

Para permitir que la aplicación Server de una Blazor WebAssemblysolución hospedada enrute la solicitud con un punto en el parámetro de ruta param, agrega una plantilla de ruta de archivo de reserva con el parámetro opcional en Startup.Configure.

Startup.cs:

endpoints.MapFallbackToFile("/example/{param?}", "index.html");

Para configurar una aplicación Blazor Server para enrutar la solicitud con un punto en el parámetro de ruta param, agrega una plantilla de ruta de página de reserva con el parámetro opcional en Startup.Configure.

Startup.cs:

endpoints.MapFallbackToPage("/example/{param?}", "/_Host");

Para obtener más información, consulta Enrutamiento en ASP.NET Core.

Parámetros de ruta de captura general

Los parámetros de ruta de captura general, que capturan rutas de acceso en varios límites de carpeta, se admiten en los componentes.

Los parámetros de ruta de captura general son:

  • Con nombre para que coincida con el nombre del segmento de ruta. La nomenclatura no distingue mayúsculas de minúsculas.
  • Tipo string. El marco no proporciona conversión automática.
  • Al final de la dirección URL.

CatchAll.razor:

@page "/catch-all/{*pageRoute}"

<PageTitle>Catch All</PageTitle>

<h1>Catch All Parameters Example</h1>

<p>Add some URI segments to the route and request the page again.</p>

<p>
    PageRoute: @PageRoute
</p>

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

<PageTitle>Catch All</PageTitle>

<h1>Catch All Parameters Example</h1>

<p>Add some URI segments to the route and request the page again.</p>

<p>
    PageRoute: @PageRoute
</p>

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string PageRoute { get; set; }
}

Para la dirección URL /catch-all/this/is/a/test con una plantilla de ruta de /catch-all/{*pageRoute}, el valor de PageRoute se establece en this/is/a/test.

Las barras diagonales y los segmentos de la ruta de acceso capturada están descodificados. En el caso de una plantilla de ruta de /catch-all/{*pageRoute}, la dirección URL /catch-all/this/is/a%2Ftest%2A produce this/is/a/test*.

Aplicaciones auxiliares de URI y estado de navegación

Usa NavigationManager para administrar los URI y la navegación en el código de C#. NavigationManager proporciona el evento y los métodos que se muestran en la siguiente tabla.

Miembro Descripción
Uri Obtiene el URI absoluto actual.
BaseUri Obtiene el URI base (con una barra diagonal final) que se puede anteponer a las rutas de acceso de URI relativo para generar un URI absoluto. Normalmente, BaseUri corresponde al atributo href del elemento <base> del documento (ubicación del contenido de <head>).
NavigateTo Navega al URI especificado. Si forceLoad es false:
  • Además, la navegación mejorada está disponible en la dirección URL actual, se activa la navegación mejorada de Blazor.
  • De lo contrario, Blazor realiza una recarga de la página completa para la dirección URL solicitada.
Si forceLoad es true:
  • El enrutamiento del lado cliente se omite.
  • Se fuerza al explorador a cargar la nueva página desde el servidor, independientemente de que el identificador URI normalmente lo controle el enrutador interactivo del lado cliente, o no.

Para más información, consulte la sección Navegación mejorada y control de formularios.

Si replace es true, el URI actual en el historial del explorador se reemplaza en lugar de insertar un URI nuevo en la pila del historial.

LocationChanged Evento que se desencadena cuando la ubicación de navegación ha cambiado. Para obtener más información, vea la sección Cambios de ubicación.
ToAbsoluteUri Convierte un URI relativo en un URI absoluto.
ToBaseRelativePath En función del URI base de la aplicación, convierte un URI absoluto en un URI relativo al prefijo de URI base. Para obtener un ejemplo, consulte la sección Generar un URI relativo al prefijo de URI base.
RegisterLocationChangingHandler Registra un controlador para procesar los eventos de navegación entrantes. La llamada a NavigateTo siempre invoca al controlador.
GetUriWithQueryParameter Devuelve un URI construido mediante la actualización de NavigationManager.Uri con un único parámetro agregado, actualizado o quitado. Para obtener más información, consulta la sección Cadenas consulta.
Miembro Descripción
Uri Obtiene el URI absoluto actual.
BaseUri Obtiene el URI base (con una barra diagonal final) que se puede anteponer a las rutas de acceso de URI relativo para generar un URI absoluto. Normalmente, BaseUri corresponde al atributo href del elemento <base> del documento (ubicación del contenido de <head>).
NavigateTo Navega al URI especificado. Si forceLoad es true:
  • El enrutamiento del lado cliente se omite.
  • Se fuerza al explorador a cargar la nueva página desde el servidor, tanto si el URI suele estar controlado por el enrutador del lado cliente como si no.
Si replace es true, el URI actual en el historial del explorador se reemplaza en lugar de insertar un URI nuevo en la pila del historial.
LocationChanged Evento que se desencadena cuando la ubicación de navegación ha cambiado. Para obtener más información, vea la sección Cambios de ubicación.
ToAbsoluteUri Convierte un URI relativo en un URI absoluto.
ToBaseRelativePath En función del URI base de la aplicación, convierte un URI absoluto en un URI relativo al prefijo de URI base. Para obtener un ejemplo, consulte la sección Generar un URI relativo al prefijo de URI base.
RegisterLocationChangingHandler Registra un controlador para procesar los eventos de navegación entrantes. La llamada a NavigateTo siempre invoca al controlador.
GetUriWithQueryParameter Devuelve un URI construido mediante la actualización de NavigationManager.Uri con un único parámetro agregado, actualizado o quitado. Para obtener más información, consulta la sección Cadenas consulta.
Miembro Descripción
Uri Obtiene el URI absoluto actual.
BaseUri Obtiene el URI base (con una barra diagonal final) que se puede anteponer a las rutas de acceso de URI relativo para generar un URI absoluto. Normalmente, BaseUri corresponde al atributo href del elemento <base> del documento (ubicación del contenido de <head>).
NavigateTo Navega al URI especificado. Si forceLoad es true:
  • El enrutamiento del lado cliente se omite.
  • Se fuerza al explorador a cargar la nueva página desde el servidor, tanto si el URI suele estar controlado por el enrutador del lado cliente como si no.
Si replace es true, el URI actual en el historial del explorador se reemplaza en lugar de insertar un URI nuevo en la pila del historial.
LocationChanged Evento que se desencadena cuando la ubicación de navegación ha cambiado. Para obtener más información, vea la sección Cambios de ubicación.
ToAbsoluteUri Convierte un URI relativo en un URI absoluto.
ToBaseRelativePath En función del URI base de la aplicación, convierte un URI absoluto en un URI relativo al prefijo de URI base. Para obtener un ejemplo, consulta la sección Generar un URI relativo al prefijo de URI base.
GetUriWithQueryParameter Devuelve un URI construido mediante la actualización de NavigationManager.Uri con un único parámetro agregado, actualizado o quitado. Para obtener más información, consulta la sección Cadenas consulta.
Miembro Descripción
Uri Obtiene el URI absoluto actual.
BaseUri Obtiene el URI base (con una barra diagonal final) que se puede anteponer a las rutas de acceso de URI relativo para generar un URI absoluto. Normalmente, BaseUri corresponde al atributo href del elemento <base> del documento (ubicación del contenido de <head>).
NavigateTo Navega al URI especificado. Si forceLoad es true:
  • El enrutamiento del lado cliente se omite.
  • Se fuerza al explorador a cargar la nueva página desde el servidor, tanto si el URI suele estar controlado por el enrutador del lado cliente como si no.
LocationChanged Evento que se desencadena cuando la ubicación de navegación ha cambiado.
ToAbsoluteUri Convierte un URI relativo en un URI absoluto.
ToBaseRelativePath En función del URI base de la aplicación, convierte un URI absoluto en un URI relativo al prefijo de URI base. Para obtener un ejemplo, consulta la sección Generar un URI relativo al prefijo de URI base.

Cambios de ubicación

En el caso del evento LocationChanged, LocationChangedEventArgs proporciona la información siguiente sobre los eventos de navegación:

El siguiente componente:

  • Navega al componente Counter de la aplicación (Counter.razor) cuando se selecciona el botón con NavigateTo.
  • Controla el evento de ubicación cambiado mediante la suscripción a NavigationManager.LocationChanged.
    • El método HandleLocationChanged se desenlaza cuando el marco llama a Dispose. Al desenlazar el método se permite la recolección de elementos no utilizados del componente.

    • La implementación del registrador registra la siguiente información cuando se selecciona el botón:

      BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:{PORT}/counter

Navigate.razor:

@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<PageTitle>Navigate</PageTitle>

<h1>Navigate Example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent() => Navigation.NavigateTo("counter");

    protected override void OnInitialized() => 
        Navigation.LocationChanged += HandleLocationChanged;

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e) => 
        Logger.LogInformation("URL of new location: {Location}", e.Location);

    public void Dispose() => Navigation.LocationChanged -= HandleLocationChanged;
}
@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<PageTitle>Navigate</PageTitle>

<h1>Navigate Example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent() => Navigation.NavigateTo("counter");

    protected override void OnInitialized() => 
        Navigation.LocationChanged += HandleLocationChanged;

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e) => 
        Logger.LogInformation("URL of new location: {Location}", e.Location);

    public void Dispose() => Navigation.LocationChanged -= HandleLocationChanged;
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}

Para obtener más información sobre la eliminación de componentes, consulta Ciclo de vida de componentes Razor de ASP.NET Core.

Navegación mejorada y control de formularios

Esta sección es aplicable a Blazor Web App.

Las Blazor Web App permiten dos tipos de enrutamiento para la navegación de páginas y las solicitudes de control de formularios:

  • Navegación normal (navegación entre documentos): se desencadena la recarga de una página completa para la dirección URL de la solicitud.
  • Navegación mejorada (navegación en el mismo documento): Blazor intercepta la solicitud y realiza una solicitud fetch en su lugar. Luego, Blazor aplica una revisión al contenido de respuesta en el DOM de la página. La navegación mejorada y el control de formularios de Blazor evitan la necesidad de que se recarguen las páginas completas y conserva más el estado de las páginas, por lo que las páginas se cargan más rápidamente y normalmente sin perder la posición de desplazamiento del usuario en la página.

La navegación mejorada está disponible cuando:

  • Se usa el script de Blazor Web App (blazor.web.js), no el script de Blazor Server (blazor.server.js) ni el script de Blazor WebAssembly (blazor.webassembly.js).
  • La característica no se deshabilita explícitamente.
  • La dirección URL de destino se encuentra dentro del espacio del identificador URI base interno (la ruta de acceso base de la aplicación).

Si están habilitados el enrutamiento del lado servidor y la navegación mejorada están habilitados, los controladores de cambio de ubicación solo se invocan para la navegaciones mediante programación iniciada desde un entorno de ejecución interactivo. En futuras versiones, otros tipos de navegación, como seguir un vínculo, también podrán invocar controladores de cambio de ubicación.

Cuando se produce una navegación mejorada, normalmente se invocan los LocationChangedcontroladores de eventos registrados con el servidor interactivo y los runtime de WebAssembly. Hay casos en los que es posible que los controladores de cambio de ubicación no intercepten una navegación mejorada. Por ejemplo, el usuario podría cambiar a otra página antes de que un entorno de ejecución interactivo esté disponible. Por lo tanto, es importante que la lógica de la aplicación no dependa de invocar un controlador de cambio de ubicación, ya que no hay ninguna garantía de que el controlador se ejecute.

Al llamar a NavigateTo:

  • Si forceLoad es false, que es el valor predeterminado:
    • Además, la navegación mejorada está disponible en la dirección URL actual, se activa la navegación mejorada de Blazor.
    • De lo contrario, Blazor realiza una recarga de la página completa para la dirección URL solicitada.
  • Si forceLoad es true: Blazor realiza una recarga de toda la página para la dirección URL solicitada, tanto si la navegación mejorada está disponible como si no lo está.

Para actualizar la página actual, llama a NavigationManager.Refresh(bool forceLoad = false), que siempre realiza una navegación mejorada, en caso de que esté disponible. Si la navegación mejorada no está disponible, Blazor realiza una recarga de toda la página.

Navigation.Refresh();

Pasa true al parámetro forceLoad para asegurarte de que siempre se realiza la recarga de una página completa, ni siquiera si la navegación mejorada está disponible:

Navigation.Refresh(true);

La navegación mejorada está activada por defecto, pero puede controlarse jerárquicamente y por enlace mediante el data-enhance-navatributo HTML.

Los siguientes ejemplos deshabilitan la navegación mejorada:

<a href="redirect" data-enhance-nav="false">
    GET without enhanced navigation
</a>
<ul data-enhance-nav="false">
    <li>
        <a href="redirect">GET without enhanced navigation</a>
    </li>
    <li>
        <a href="redirect-2">GET without enhanced navigation</a>
    </li>
</ul>

Si el destino noBlazor es un punto de conexión, no se aplica la navegación mejorada, y el JavaScript del lado del cliente reintenta como una carga de página completa. Esto asegura que no haya confusión en el marco sobre las páginas externas que no deben ser parcheados en una página existente.

Para habilitar la administración mejorada de formularios, agrega el parámetro Enhance a EditForm los formularios o el atributo data-enhance a los formularios HTML (<form>):

<EditForm ... Enhance ...>
    ...
</EditForm>
<form ... data-enhance ...>
    ...
</form>

El control mejorado de formularios no es jerárquico y no fluye a formularios secundarios:

No admitido: no se puede establecer la navegación mejorada en el elemento antecesor de un formulario para habilitar la navegación mejorada para el formulario.

<div ... data-enhance ...>
    <form ...>
        <!-- NOT enhanced -->
    </form>
</div>

Los envíos de formularios mejorados solo funcionan con Blazor puntos de conexión. La publicación de un formulario mejorado en un noBlazor punto de conexión produce un error.

Para deshabilitar la navegación mejorada:

  • Para un EditForm, elimina el parámetro Enhance del elemento del formulario (o configúralo a false: Enhance="false").
  • Para un HTML <form>, elimina el atributo data-enhance del elemento formulario (o configúralo a false: data-enhance="false").

Blazor navegación mejorada y el control de formularios pueden deshacer cambios dinámicos en el DOM si el contenido actualizado no forma parte de la renderización del servidor. Para conservar el contenido de un elemento, utiliza el atributo data-permanent.

En el siguiente ejemplo, el contenido del elemento <div> se actualiza dinámicamente mediante un script cuando se carga la página:

<div data-permanent>
    ...
</div>

Una vez que Blazor se ha iniciado en el cliente, puedes utilizar el evento enhancedload para escuchar las actualizaciones de la página mejorada. Esto permite volver a aplicar cambios en el DOM que pueden haber sido deshechos por una actualización de página mejorada.

Blazor.addEventListener('enhancedload', () => console.log('Enhanced update!'));

Para deshabilitar la navegación mejorada y la administración de formularios de forma global, consulta Inicio de ASP.NET Core Blazor.

La navegación mejorada con representación estática del lado del servidor (SSR estática) requiere una atención especial al cargar JavaScript. Para obtener más información, consulta ASP.NET Core Blazor JavaScript con representación estática del lado servidor.

Generar un URI relativo al prefijo de URI base

En función del URI base de la aplicación, ToBaseRelativePath convierte un URI absoluto en un URI relativo al prefijo de URI base.

Considera el ejemplo siguiente:

try
{
    baseRelativePath = Navigation.ToBaseRelativePath(inputURI);
}
catch (ArgumentException ex)
{
    ...
}

Si el URI base de la aplicación es https://localhost:8000, se obtienen los siguientes resultados:

  • Pasar resultados de https://localhost:8000/segment en inputURI en un baseRelativePath de segment.
  • Pasar resultados de https://localhost:8000/segment1/segment2 en inputURI en un baseRelativePath de segment1/segment2.

Si el URI base de la aplicación no coincide con el URI base de inputURI, se produce un ArgumentException.

Pasar los resultados de https://localhost:8001/segment en inputURI en la siguiente excepción:

System.ArgumentException: 'The URI 'https://localhost:8001/segment' is not contained by the base URI 'https://localhost:8000/'.'

NavigationManager usa laHistory API del explorador para mantener el estado del historial de navegación asociado a cada cambio de ubicación realizado por la aplicación. Mantener el estado del historial es especialmente útil en escenarios de redirección externa como, por ejemplo, al autenticar usuarios con proveedores de identity externos. Para obtener información adicional, consulta la sección Opciones de navegación.

Pasa NavigationOptions a NavigateTo para controlar los siguientes comportamientos:

  • ForceLoad: puentear el enrutamiento de lado del cliente y forzar al navegador a que cargue la nueva página del servidor, tanto si el enrutador de lado del cliente controla el URI como si no. El valor predeterminado es false.
  • ReplaceHistoryEntry: reemplazar la entrada actual en la pila del historial. Si false, se anexa la nueva entrada a la pila del historial. El valor predeterminado es false.
  • HistoryEntryState: obtiene o establece el estado que se va a anexar a la entrada del historial.
Navigation.NavigateTo("/path", new NavigationOptions
{
    HistoryEntryState = "Navigation state"
});

Para obtener información adicional sobre cómo obtener el estado asociado a la entrada del historial de destino en el manejo de los cambios de ubicación, consulta la sección Controlar o impedir cambios de ubicación.

Cadenas de consulta

Utiliza el atributo [SupplyParameterFromQuery] para especificar que un parámetro del componente procede de la cadena de consulta.

Usa el atributo [SupplyParameterFromQuery] con el atributo [Parameter] para especificar que un parámetro de componente de un componente enrutable puede proceder de la cadena de consulta.

Nota

Los parámetros de componente solo pueden recibir valores de parámetro de consulta en componentes enrutables con una directiva @page.

Solo los componentes enrutables reciben directamente parámetros de consulta para evitar la subvertir el flujo de información de arriba abajo y dejar claro el orden de procesamiento de los parámetros, tanto por el marco como por la aplicación. Este diseño evita errores sutiles en el código de la aplicación que se escribió suponiendo un orden de procesamiento de parámetros específico. Puedes definir parámetros en cascada personalizados o asignarlos directamente a parámetros de componentes normales para pasar valores de parámetros de consulta a componentes no enrutables.

Los parámetros de componente proporcionados a partir de la cadena de consulta admiten los tipos siguientes:

  • bool, DateTime, decimal, double, float, Guidint, , long, . string
  • Variantes que admiten un valor NULL de los tipos anteriores.
  • Matrices de los tipos anteriores, con independencia de que admitan un valor NULL o no.

El formato de referencia cultural invariable correcto se aplica al tipo especificado (CultureInfo.InvariantCulture).

Especifica la propiedad Name del atributo [SupplyParameterFromQuery] para usar un nombre de parámetro de consulta diferente al nombre del parámetro de componente. En el ejemplo siguiente, el nombre en C# del parámetro de componente es {COMPONENT PARAMETER NAME}. Se especifica otro nombre de parámetro de consulta para el marcador de posición {QUERY PARAMETER NAME}:

A diferencia de las propiedades de parámetros de componentes ([Parameter]), las propiedades [SupplyParameterFromQuery] se pueden marcar con private además de con public.

[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
private string? {COMPONENT PARAMETER NAME} { get; set; }

Al igual que las propiedades de parámetros de componentes ([Parameter]), las propiedades [SupplyParameterFromQuery] son siempre propiedades public en .NET 6/7. En .NET 8 o posterior, las propiedades [SupplyParameterFromQuery] se pueden marcar con public o private.

[Parameter]
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }

En el ejemplo siguiente con una dirección URL de /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman:

  • La propiedad Filter se resuelve en scifi stars.
  • La propiedad Page se resuelve en 3.
  • La matriz Stars se rellena a partir de parámetros de consulta denominados star (Name = "star") y se resuelve en LeVar Burton y Gary Oldman.

Nota

Los parámetros de cadena de consulta del siguiente componente de página enrutable también funcionan en un componente no enrutable sin una @page directiva (por ejemplo, Search.razor para un componente compartido Search usado en otros componentes).

Search.razor:

@page "/search"

<h1>Search Example</h1>

<p>Filter: @Filter</p>

<p>Page: @Page</p>

@if (Stars is not null)
{
    <p>Stars:</p>

    <ul>
        @foreach (var name in Stars)
        {
            <li>@name</li>
        }
    </ul>
}

@code {
    [SupplyParameterFromQuery]
    private string? Filter { get; set; }

    [SupplyParameterFromQuery]
    private int? Page { get; set; }

    [SupplyParameterFromQuery(Name = "star")]
    private string[]? Stars { get; set; }
}

Search.razor:

@page "/search"

<h1>Search Example</h1>

<p>Filter: @Filter</p>

<p>Page: @Page</p>

@if (Stars is not null)
{
    <p>Stars:</p>

    <ul>
        @foreach (var name in Stars)
        {
            <li>@name</li>
        }
    </ul>
}

@code {
    [Parameter]
    [SupplyParameterFromQuery]
    public string? Filter { get; set; }

    [Parameter]
    [SupplyParameterFromQuery]
    public int? Page { get; set; }

    [Parameter]
    [SupplyParameterFromQuery(Name = "star")]
    public string[]? Stars { get; set; }
}

Usa GetUriWithQueryParameter para agregar, cambiar o quitar uno o varios parámetros de consulta en la dirección URL actual:

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})

Para el ejemplo anterior:

  • El marcador de posición {NAME} especifica el nombre del parámetro de consulta. El marcador de posición {VALUE} especifica el valor como un tipo admitido. Los tipos admitidos se enumeran más adelante en esta sección.
  • Se devuelve una cadena igual a la dirección URL actual con un único parámetro:
    • Se agrega si el nombre del parámetro de consulta no existe en la dirección URL actual.
    • Se actualiza al valor proporcionado si el parámetro de consulta existe en la dirección URL actual.
    • Se quita si el tipo del valor proporcionado admite un valor NULL y el valor es null.
  • El formato de referencia cultural invariable correcto se aplica al tipo especificado (CultureInfo.InvariantCulture).
  • El nombre y el valor del parámetro de consulta se codifican como una dirección URL.
  • Todos los valores con el nombre del parámetro de consulta correspondiente se reemplazan si hay varias instancias del tipo.

Llama a GetUriWithQueryParameters para crear un URI construido a partir de Uri con varios parámetros agregados, actualizados o quitados. En cada valor, el marco usa value?.GetType() para determinar el tipo de tiempo de ejecución para cada parámetro de consulta y selecciona el formato invariable de referencia cultural correcto. El marco genera un error para los tipos no admitidos.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters({PARAMETERS})

El marcador de posición {PARAMETERS} es IReadOnlyDictionary<string, object>.

Pasa una cadena de URI a GetUriWithQueryParameters para generar uno nuevo a partir de un URI proporcionado con varios parámetros agregados, actualizados o quitados. En cada valor, el marco usa value?.GetType() para determinar el tipo de tiempo de ejecución para cada parámetro de consulta y selecciona el formato invariable de referencia cultural correcto. El marco genera un error para los tipos no admitidos. Los tipos admitidos se enumeran más adelante en esta sección.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
  • El marcador de posición {URI} es el URI con o sin una cadena de consulta.
  • El marcador de posición {PARAMETERS} es IReadOnlyDictionary<string, object>.

Los tipos admitidos son idénticos a los de las restricciones de ruta:

  • bool
  • DateTime
  • decimal
  • double
  • float
  • Guid
  • int
  • long
  • string

Los tipos no admitidos incluyen:

  • Variantes que admiten un valor NULL de los tipos anteriores.
  • Matrices de los tipos anteriores, con independencia de que admitan un valor NULL o no.

Advertencia

Con la compresión, que está habilitada de forma predeterminada, evita crear componentes interactivos del lado servidor (autenticados o autorizados) seguros que representen datos de orígenes que no sean de confianza. Los orígenes que no son de confianza incluyen parámetros de ruta, cadenas de consulta, datos de interoperabilidad JS y cualquier otro origen de datos que un usuario de terceros pueda controlar (bases de datos, servicios externos). Para obtener más información, consulta Guía de ASP.NETBlazorSignalR y Guía de mitigación de amenazas de representación interactiva del lado servidor para ASP.NET Core Blazor.

Reemplazo de un valor de parámetro de consulta cuando el parámetro existe

Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
Dirección URL actual Dirección URL generada
scheme://host/?full%20name=David%20Krumholtz&age=42 scheme://host/?full%20name=Morena%20Baccarin&age=42
scheme://host/?fUlL%20nAmE=David%20Krumholtz&AgE=42 scheme://host/?full%20name=Morena%20Baccarin&AgE=42
scheme://host/?full%20name=Jewel%20Staite&age=42&full%20name=Summer%20Glau scheme://host/?full%20name=Morena%20Baccarin&age=42&full%20name=Morena%20Baccarin
scheme://host/?full%20name=&age=42 scheme://host/?full%20name=Morena%20Baccarin&age=42
scheme://host/?full%20name= scheme://host/?full%20name=Morena%20Baccarin

Anexo de un parámetro de consulta y un valor cuando el parámetro no existe

Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
Dirección URL actual Dirección URL generada
scheme://host/?age=42 scheme://host/?age=42&name=Morena%20Baccarin
scheme://host/ scheme://host/?name=Morena%20Baccarin
scheme://host/? scheme://host/?name=Morena%20Baccarin

Eliminación de un parámetro de consulta cuando el valor del parámetro es null

Navigation.GetUriWithQueryParameter("full name", (string)null)
Dirección URL actual Dirección URL generada
scheme://host/?full%20name=David%20Krumholtz&age=42 scheme://host/?age=42
scheme://host/?full%20name=Sally%20Smith&age=42&full%20name=Summer%20Glau scheme://host/?age=42
scheme://host/?full%20name=Sally%20Smith&age=42&FuLl%20NaMe=Summer%20Glau scheme://host/?age=42
scheme://host/?full%20name=&age=42 scheme://host/?age=42
scheme://host/?full%20name= scheme://host/

Adición, actualización y eliminación de parámetros de consulta

En el ejemplo siguiente:

  • name se quita, si está presente.
  • age se agrega con un valor de 25 (int), si no está presente. Si está presente, age se actualiza a un valor de 25.
  • eye color se agrega o actualiza a un valor de green.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["name"] = null,
        ["age"] = (int?)25,
        ["eye color"] = "green"
    })
Dirección URL actual Dirección URL generada
scheme://host/?name=David%20Krumholtz&age=42 scheme://host/?age=25&eye%20color=green
scheme://host/?NaMe=David%20Krumholtz&AgE=42 scheme://host/?age=25&eye%20color=green
scheme://host/?name=David%20Krumholtz&age=42&keepme=true scheme://host/?age=25&keepme=true&eye%20color=green
scheme://host/?age=42&eye%20color=87 scheme://host/?age=25&eye%20color=green
scheme://host/? scheme://host/?age=25&eye%20color=green
scheme://host/ scheme://host/?age=25&eye%20color=green

Compatibilidad con valores enumerables

En el ejemplo siguiente:

  • full name se agrega o actualiza a Morena Baccarin, un valor único.
  • Los parámetros ping se agregan o reemplazan por 35, 16, 87 y 240.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["full name"] = "Morena Baccarin",
        ["ping"] = new int?[] { 35, 16, null, 87, 240 }
    })
Dirección URL actual Dirección URL generada
scheme://host/?full%20name=David%20Krumholtz&ping=8&ping=300 scheme://host/?full%20name=Morena%20Baccarin&ping=35&ping=16&ping=87&ping=240
scheme://host/?ping=8&full%20name=David%20Krumholtz&ping=300 scheme://host/?ping=35&full%20name=Morena%20Baccarin&ping=16&ping=87&ping=240
scheme://host/?ping=8&ping=300&ping=50&ping=68&ping=42 scheme://host/?ping=35&ping=16&ping=87&ping=240&full%20name=Morena%20Baccarin

Para navegar con una cadena de consulta agregada o modificada, pasa una dirección URL generada a NavigateTo.

En el ejemplo siguiente se llama a:

  • GetUriWithQueryParameter para agregar o reemplazar el parámetro de consulta name con un valor de Morena Baccarin.
  • Llama a NavigateTo para desencadenar la navegación a la nueva dirección URL.
Navigation.NavigateTo(
    Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));

La cadena de consulta de una solicitud se obtiene de la propiedad NavigationManager.Uri:

@inject NavigationManager Navigation

...

var query = new Uri(Navigation.Uri).Query;

Para analizar los parámetros de una cadena de consulta, un enfoque consiste en usar URLSearchParams con la interoperabilidad de JavaScript (JS):

export createQueryString = (string queryString) => new URLSearchParams(queryString);

Para obtener más información sobre el aislamiento de JavaScript con módulos de JavaScript, consulta Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.

Enrutamiento con hash a elementos con nombre

Ve a un elemento con nombre mediante los siguientes enfoques con una referencia hash (#) al elemento. Las rutas a los elementos dentro del componente y las rutas a los elementos de los componentes externos usan rutas de acceso relativas a la raíz. Una barra diagonal inicial (/) es opcional.

Los ejemplos de cada uno de los enfoques siguientes muestran la navegación a un elemento con un id de targetElement en el componente Counter:

  • Elemento delimitador (<a>) con href:

    <a href="/counter#targetElement">
    
  • Componente NavLink con href:

    <NavLink href="/counter#targetElement">
    
  • NavigationManager.NavigateTo pasando la URL relativa:

    Navigation.NavigateTo("/counter#targetElement");
    

En el ejemplo siguiente se muestra el enrutamiento con hash a encabezados H2 con nombre dentro de un componente y a componentes externos.

En los componentes Home (Home.razor) y Counter (Counter.razor), coloca el marcado siguiente en la parte inferior del marcado de componente existente para que actúe como destinos de navegación. <div> crea un espacio vertical artificial para demostrar el comportamiento del desplazamiento del explorador:

<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>

Agrega el siguiente componente HashedRouting a la aplicación.

HashedRouting.razor:

@page "/hashed-routing"
@inject NavigationManager Navigation

<PageTitle>Hashed routing</PageTitle>

<h1>Hashed routing to named elements</h1>

<ul>
    <li>
        <a href="/hashed-routing#targetElement">
            Anchor in this component
        </a>
    </li>
    <li>
        <a href="/#targetElement">
            Anchor to the <code>Home</code> component
        </a>
    </li>
    <li>
        <a href="/counter#targetElement">
            Anchor to the <code>Counter</code> component
        </a>
    </li>
    <li>
        <NavLink href="/hashed-routing#targetElement">
            Use a `NavLink` component in this component
        </NavLink>
    </li>
    <li>
        <button @onclick="NavigateToElement">
            Navigate with <code>NavigationManager</code> to the 
            <code>Counter</code> component
        </button>
    </li>
</ul>

<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>

@code {
    private void NavigateToElement()
    {
        Navigation.NavigateTo("/counter#targetElement");
    }
}

Interacción del usuario con el contenido de <Navigating>

Si se produce un retraso significativo durante la navegación, por ejemplo durante la carga diferida de ensamblados en una aplicación Blazor WebAssembly o por una conexión de red lenta a una aplicación Blazor del lado del servidor, el componente Router puede indicar al usuario que se está produciendo una transición de página.

En la parte superior del componente que especifica el componente Router, agrega una directiva @using para el espacio de nombres Microsoft.AspNetCore.Components.Routing:

@using Microsoft.AspNetCore.Components.Routing

Proporciona contenido al parámetro Navigating para que se muestre durante los eventos de transición de página.

En el contenido del elemento enrutador (<Router>...</Router>):

<Navigating>
    <p>Loading the requested page&hellip;</p>
</Navigating>

Para obtener un ejemplo en el que se usa la propiedad Navigating, consulta Ensamblados de carga diferida en Blazor WebAssembly de ASP.NET Core.

Control de eventos de navegación asincrónicos con OnNavigateAsync

El componente Router admite una característica OnNavigateAsync. El controlador OnNavigateAsync se invoca cuando el usuario:

  • Visita una ruta por primera vez desplazándose hasta ella directamente en el explorador.
  • Navega a una nueva ruta mediante un vínculo o una invocación de NavigationManager.NavigateTo.
<Router AppAssembly="typeof(App).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        ...
    }
}
<Router AppAssembly="typeof(Program).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        ...
    }
}

Para obtener un ejemplo en el que se usa OnNavigateAsync, consulta Ensamblados de carga diferida en Blazor WebAssembly de ASP.NET Core.

Al representar previamente en el servidor, OnNavigateAsync se ejecuta dos veces:

  • Una primera vez cuando el componente de punto de conexión solicitado se representa inicialmente de forma estática.
  • Una segunda vez cuando el explorador representa el componente de punto de conexión.

Para evitar que el código del desarrollador en OnNavigateAsync se ejecute dos veces, el componente Routes puede almacenar NavigationContext para su uso en el método del ciclo de vida OnAfterRender{Async}, donde se puede comprobar firstRender. Para obtener más información, consulta Representación previa con interoperabilidad de JavaScript.

Para evitar que el código de desarrollador en OnNavigateAsync se ejecute dos veces, el componente App puede almacenar NavigationContext para su uso en OnAfterRender{Async}, donde firstRender se puede comprobar. Para obtener más información, consulta Representación previa con interoperabilidad de JavaScript.

Control de las cancelaciones en OnNavigateAsync

El objeto NavigationContext pasado a la devolución de llamada de OnNavigateAsync contiene un elemento CancellationToken que se establece cuando se produce un nuevo evento de navegación. La devolución de llamada de OnNavigateAsync debe iniciarse cuando se establece este token de cancelación para evitar que continúe la ejecución de la devolución de llamada de OnNavigateAsync en una navegación no actualizada.

Si un usuario navega a un punto de conexión, pero inmediatamente después navega a un nuevo punto de conexión, la aplicación no debe seguir ejecutando la devolución de llamada OnNavigateAsync para el primer punto de conexión.

En el ejemplo siguiente:

  • El token de cancelación se pasa en la llamada a PostAsJsonAsync, que puede cancelar el POST si el usuario se desplaza fuera del punto de conexión /about.
  • El token de cancelación se establece durante una operación de captura previa del producto si el usuario se desplaza fuera del punto de conexión /store.
@inject HttpClient Http
@inject ProductCatalog Products

<Router AppAssembly="typeof(App).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        if (context.Path == "/about") 
        {
            var stats = new Stats { Page = "/about" };
            await Http.PostAsJsonAsync("api/visited", stats, 
                context.CancellationToken);
        }
        else if (context.Path == "/store")
        {
            var productIds = new[] { 345, 789, 135, 689 };

            foreach (var productId in productIds) 
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                Products.Prefetch(productId);
            }
        }
    }
}
@inject HttpClient Http
@inject ProductCatalog Products

<Router AppAssembly="typeof(Program).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        if (context.Path == "/about") 
        {
            var stats = new Stats { Page = "/about" };
            await Http.PostAsJsonAsync("api/visited", stats, 
                context.CancellationToken);
        }
        else if (context.Path == "/store")
        {
            var productIds = new[] { 345, 789, 135, 689 };

            foreach (var productId in productIds) 
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                Products.Prefetch(productId);
            }
        }
    }
}

Nota

Cuando no se ejecuta si el token de cancelación de NavigationContext se cancela, puede resultar en un comportamiento imprevisto, como la representación de un componente de una navegación anterior.

Control o prevención de cambios de ubicación

RegisterLocationChangingHandler registra un controlador para procesar los eventos de navegación entrantes. El contexto del controlador proporcionado porLocationChangingContext incluye las siguientes propiedades:

Un componente puede registrar varios controladores de cambio de ubicación en el método de ciclo de vida de OnAfterRender{Async}. La navegación invoca todos los controladores de cambio de ubicación registrados en toda la aplicación (en varios componentes) y cualquier navegación interna los ejecuta en paralelo. Además de NavigateTo, los controladores se invocan:

  • Al seleccionar vínculos internos, que son vínculos que apuntan a direcciones URL en la ruta de acceso base de la aplicación.
  • Al navegar con los botones hacia delante y atrás en un navegador.

Los controladores solo se ejecutan en la navegación interna dentro de la aplicación. Si el usuario selecciona un vínculo que lleva a un sitio diferente o cambia la barra de direcciones a otro sitio manualmente, los controladores de cambio de ubicación no se ejecutarán.

Implementa IDisposable y elimina los controladores registrados para anular su registro. Para obtener más información, consulta Ciclo de vida de componentes Razor de ASP.NET Core.

Importante

No intentes ejecutar tareas de limpieza a través de la interoperabilidad de JavaScript (JS) al controlar los cambios de ubicación. Usa el patrón MutationObserver en JS en el cliente. Para obtener más información, consulta Interoperabilidad de JavaScript en Blazor de ASP.NET Core (interoperabilidad de JS).

En el ejemplo siguiente, se registra un controlador de cambio de ubicación en eventos de navegación.

NavHandler.razor:

@page "/nav-handler"
@implements IDisposable
@inject NavigationManager Navigation

<p>
    <button @onclick="@(() => Navigation.NavigateTo("/"))">
        Home (Allowed)
    </button>
    <button @onclick="@(() => Navigation.NavigateTo("/counter"))">
        Counter (Prevented)
    </button>
</p>

@code {
    private IDisposable? registration;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            registration = 
                Navigation.RegisterLocationChangingHandler(OnLocationChanging);
        }
    }

    private ValueTask OnLocationChanging(LocationChangingContext context)
    {
        if (context.TargetLocation == "/counter")
        {
            context.PreventNavigation();
        }

        return ValueTask.CompletedTask;
    }

    public void Dispose() => registration?.Dispose();
}

Dado que la navegación interna se puede cancelar de forma asincrónica, pueden solaparse varias llamadas a controladores registrados. Por ejemplo, se pueden producir varias llamadas de controlador cuando el usuario selecciona rápidamente el botón Atrás en una página o selecciona varios vínculos antes de ejecutar una navegación. A continuación se muestra un resumen de la lógica de navegación asincrónica:

  • Si hubiera algún controlador de cambio de ubicación registrado, se revertiría inicialmente toda la navegación y luego se reproduciría si no se cancela la navegación.
  • Si se solapan las solicitudes de navegación, la última solicitud siempre cancelará las solicitudes anteriores, lo que significa lo siguiente:
    • La aplicación puede tratar varias selecciones de botón atrás y adelante como una única selección.
    • Si el usuario selecciona varios vínculos antes de finalizar la navegación, el último vínculo seleccionado determinará la navegación.

Para obtener información adicional sobre cómo pasar NavigationOptions a NavigateTo para controlar las entradas y el estado de la pila del historial de navegación, consulta la sección Opciones de navegación.

Para obtener código de ejemplo adicional, consulta NavigationManagerComponent en BasicTestApp (origen de referencia dotnet/aspnetcore).

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, usa la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, consulta Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET Core (dotnet/AspNetCore.Docs #26205).

El componente NavigationLock intercepta los eventos de navegación siempre que se represente, "bloqueando" de forma eficaz cualquier navegación determinada hasta que se tome una decisión para continuar o cancelar. Usa NavigationLock cuando la interceptación de navegación se pueda limitar a la duración de un componente.

Parámetros de NavigationLock:

  • ConfirmExternalNavigation establece un diálogo del explorador para pedir al usuario que confirme o cancele la navegación externa. El valor predeterminado es false. Mostrar el diálogo de confirmación requiere una interacción inicial del usuario con la página antes de desencadenar la navegación externa con la URL de la barra de direcciones del explorador. Para obtener información adicional sobre el requisito de interacción, consulta Ventana: evento beforeunload (documentación de MDN).
  • OnBeforeInternalNavigation establece una devolución de llamada para eventos de navegación interna.

En el componente NavLock siguiente:

  • El usuario tiene que confirmar un intento de seguir el vínculo al sitio web de Microsoft para que la navegación a https://www.microsoft.com se realice correctamente.
  • PreventNavigation se llama a para evitar que se produzca la navegación si el usuario rechaza confirmar la navegación a través de una llamada de interoperabilidad de JavaScript (JS) que genera el diálogo JSconfirm.

NavLock.razor:

@page "/nav-lock"
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation

<NavigationLock ConfirmExternalNavigation="true" 
    OnBeforeInternalNavigation="OnBeforeInternalNavigation" />

<p>
    <button @onclick="Navigate">Navigate</button>
</p>

<p>
    <a href="https://www.microsoft.com">Microsoft homepage</a>
</p>

@code {
    private void Navigate()
    {
        Navigation.NavigateTo("/");
    }

    private async Task OnBeforeInternalNavigation(LocationChangingContext context)
    {
        var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm", 
            "Are you sure you want to navigate to the root page?");

        if (!isConfirmed)
        {
            context.PreventNavigation();
        }
    }
}

Para obtener código de ejemplo adicional, consulta el componenteConfigurableNavigationLock en BasicTestApp (origen de referencia dotnet/aspnetcore).

Usa un componente NavLink en lugar de los elementos de hipervínculo HTML (<a>) cuando cree vínculos de navegación. Un componente NavLink se comporta igual que un elemento <a>, salvo que alterna una clase CSS active en función de si su elemento href coincide con la dirección URL actual. La clase active ayuda a un usuario a entender qué página es la página activa entre los vínculos de navegación mostrados. Opcionalmente, asigna un nombre de clase CSS a NavLink.ActiveClass para aplicar una clase CSS personalizada al vínculo representado cuando la ruta actual coincida con href.

Hay dos opciones de NavLinkMatch que se pueden asignar al atributo Match del elemento <NavLink>:

  • NavLinkMatch.All: el elemento NavLink estará activo cuando coincida con la dirección URL actual completa.
  • NavLinkMatch.Prefix (predeterminado): el elemento NavLink estará activo cuando coincida con cualquier prefijo de la dirección URL actual.

En el ejemplo anterior, HomeNavLinkhref="" coincide con la dirección URL home y solo recibe la clase CSS active en la ruta de acceso base predeterminada de la aplicación (/). El segundo elemento NavLink recibe la clase active cuando el usuario visita una dirección URL con un prefijo component (por ejemplo, /component y /component/another-segment).

Se pasan más atributos del componente NavLink a la etiqueta delimitadora representada. En el siguiente ejemplo, el componente NavLink incluye el atributo target:

<NavLink href="example-page" target="_blank">Example page</NavLink>

Se representa el siguiente marcado HTML:

<a href="example-page" target="_blank">Example page</a>

Advertencia

Debido a la forma en que Blazor representa el contenido secundario, la representación de componentes NavLink dentro de un bucle for requiere una variable de índice local si se usa la variable de bucle incremental en el contenido del componente NavLink (secundario):

@for (int c = 1; c < 4; c++)
{
    var ct = c;
    <li ...>
        <NavLink ...>
            <span ...></span> Product #@ct
        </NavLink>
    </li>
}

El uso de una variable de índice en este escenario es un requisito para cualquier componente secundario que use una variable de bucle en su contenido secundario, no solo para el componente NavLink.

También puedes usar un bucle foreach con Enumerable.Range:

@foreach (var c in Enumerable.Range(1, 3))
{
    <li ...>
        <NavLink ...>
            <span ...></span> Product #@c
        </NavLink>
    </li>
}

Las entradas de componente NavLink se pueden crear dinámicamente a partir de los componentes de la aplicación a través de la reflexión. En el siguiente ejemplo se muestra el enfoque general para una mayor personalización.

Para la siguiente demostración, se usa una convención de nomenclatura estándar coherente para los componentes de la aplicación:

  • Los nombres de archivo de componentes enrutables usan el Pascal case†, por ejemplo, Pages/ProductDetail.razor.
  • Las rutas de archivos de componentes enrutables hacen coincidir sus direcciones URL en kebab case‡ con guiones que aparecen entre palabras en la plantilla de ruta de un componente. Por ejemplo, se solicita un componente ProductDetail con una plantilla de ruta de /product-detail (@page "/product-detail") en un explorador en la dirección URL /product-detail relativa.

†Pascal Case (letra mayúscula) es una convención de nomenclatura sin espacios y signos de puntuación y con la primera letra de cada palabra en mayúsculas, incluida la primera palabra.
‡Kebab case es una convención de nomenclatura sin espacios y signos de puntuación que usa letras minúsculas y guiones entre palabras.

En el marcado Razor del componente NavMenu (NavMenu.razor) en la página Home predeterminada, se agregan componentes NavLink desde una colección:

<div class="nav-scrollable" 
    onclick="document.querySelector('.navbar-toggler').click()">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="bi bi-house-door-fill-nav-menu" 
                    aria-hidden="true"></span> Home
            </NavLink>
        </div>

+       @foreach (var name in GetRoutableComponents())
+       {
+           <div class="nav-item px-3">
+               <NavLink class="nav-link" 
+                       href="@Regex.Replace(name, @"(\B[A-Z]|\d+)", "-$1").ToLower()">
+                   @Regex.Replace(name, @"(\B[A-Z]|\d+)", " $1")
+               </NavLink>
+           </div>
+       }

    </nav>
</div>

Método GetRoutableComponents del bloque @code:

public IEnumerable<string> GetRoutableComponents() => 
    Assembly.GetExecutingAssembly()
        .ExportedTypes
        .Where(t => t.IsSubclassOf(typeof(ComponentBase)))
        .Where(c => c.GetCustomAttributes(inherit: true)
                     .OfType<RouteAttribute>()
                     .Any())
        .Where(c => c.Name != "Home" && c.Name != "Error")
        .OrderBy(o => o.Name)
        .Select(c => c.Name);

En el ejemplo anterior no se incluyen las siguientes páginas en la lista representada de componentes:

  • Página Home: la página aparece por separado de los vínculos generados automáticamente porque debe aparecer en la parte superior de la lista y establecer el parámetro Match.
  • Página Error: la página de error solo se navega por el marco de trabajo y no debe aparecer.

Para obtener un ejemplo del código anterior en una aplicación de ejemplo que puedes ejecutar localmente, obtén la aplicación de muestra Blazor Web App o Blazor WebAssembly.

Integración del enrutamiento de puntos de conexión de ASP.NET Core

Esta sección se aplica a las Blazor Web App que funcionan a través de un circuito.

Esta sección es aplicable a aplicaciones Blazor Server.

Blazor Web App se integra en el enrutamiento de punto de conexión de ASP.NET Core. Una aplicación ASP.NET Core está configurada para aceptar conexiones entrantes de componentes interactivos con MapRazorComponents en el archivo Program. El componente raíz predeterminado (primer componente cargado) es el App (App.razor):

app.MapRazorComponents<App>();

Blazor Server se integra en el enrutamiento de puntos de conexión de ASP.NET Core. Una aplicación ASP.NET Core está configurada para aceptar conexiones entrantes de componentes interactivos con MapBlazorHub en el archivo Program:

app.UseRouting();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

Blazor Server se integra en el enrutamiento de puntos de conexión de ASP.NET Core. Una aplicación ASP.NET Core está configurada para aceptar conexiones entrantes de componentes interactivos con MapBlazorHub en Startup.Configure.

La configuración típica consiste en enrutar todas las solicitudes a una página de Razor, que actúa como el host del lado servidor de la aplicación Blazor Server. Convencionalmente, la página del host se suele llamar _Host.cshtmlen la carpeta Pages de la aplicación.

La ruta especificada en el archivo de host se denomina ruta de reserva porque tiene una prioridad baja en la búsqueda de rutas, y se solo se usa cuando no se encuentran coincidencias con otras rutas. Esto permite a la aplicación usar otros controladores y páginas sin interferir con el enrutamiento de componentes en la aplicación Blazor Server.

Para información sobre cómo configurar MapFallbackToPage para el hospedaje de servidores de direcciones URL no raíz, consulte Hospedaje e implementación de ASP.NET CoreBlazor.