Condividi tramite


ASP.NET routing e navigazione core Blazor

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo illustra come gestire Blazor il routing delle richieste di app e come usare il NavLink componente per creare collegamenti di spostamento.

Importante

Gli esempi di codice in questo articolo illustrano i metodi chiamati su Navigation, che è un oggetto inserito NavigationManager in classi e componenti.

Routing statico e interattivo

Questa sezione si applica a Blazor Web Apps.

Se il prerendering è abilitato, il router (Router componente , <Router> in Routes.razor) esegue il routing statico ai componenti durante il Blazor rendering statico lato server (SSR statico). Questo tipo di routing è denominato routing statico.

Quando al componente viene assegnata Routes una modalità di rendering interattiva, il router diventa interattivo dopo ssr Blazor statico con routing statico nel server. Questo tipo di routing è denominato routing interattivo.

I router statici usano il routing degli endpoint e il percorso della richiesta HTTP per determinare quale componente eseguire il rendering. Quando il router diventa interattivo, usa l'URL del documento (l'URL nella barra degli indirizzi del browser) per determinare quale componente eseguire il rendering. Ciò significa che il router interattivo può modificare dinamicamente il componente di cui viene eseguito il rendering se l'URL del documento cambia dinamicamente in un altro URL interno valido e può farlo senza eseguire una richiesta HTTP per recuperare il contenuto di una nuova pagina.

Il routing interattivo impedisce anche la pre-gestione perché il nuovo contenuto della pagina non viene richiesto dal server con una normale richiesta di pagina. Per altre informazioni, vedere Prerender ASP.NET Componenti di baseRazor.

Modelli di route

Il Router componente consente il routing ai Razor componenti e si trova nel componente dell'app Routes (Components/Routes.razor).

Il Router componente consente il routing ai Razor componenti. Il Router componente viene usato nel App componente (App.razor).

Quando viene compilato un Razor componente (.razor) con una@page direttiva, viene fornita una classe componente generata che RouteAttribute specifica il modello di route del componente.

All'avvio dell'app, l'assembly specificato come router AppAssembly viene analizzato per raccogliere informazioni di route per i componenti dell'app che hanno un oggetto RouteAttribute.

In fase di esecuzione, il RouteView componente:

  • Riceve l'oggetto RouteData Router da insieme a qualsiasi parametro di route.
  • Esegue il rendering del componente specificato con il relativo layout, inclusi eventuali altri layout annidati.

Facoltativamente, specificare un DefaultLayout parametro con una classe di layout per i componenti che non specificano un layout con la @layout direttiva . I modelli di progetto delBlazor framework specificano il MainLayout componente (MainLayout.razor) come layout predefinito dell'app. Per altre informazioni sui layout, vedere ASP.NET Layout coreBlazor.

I componenti supportano più modelli di route usando più @page direttive. Il componente di esempio seguente viene caricato nelle richieste per /blazor-route e /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

Affinché gli URL vengano risolti correttamente, l'app deve includere un <base> tag (posizione del <head> contenuto) con il percorso di base dell'app specificato nell'attributo href . Per altre informazioni, vedere Ospitare e distribuire ASP.NET Core Blazor.

non Router interagisce con i valori della stringa di query. Per usare le stringhe di query, vedere la sezione Stringhe di query.

In alternativa a specificare il modello di route come valore letterale stringa con la @page direttiva , è possibile specificare modelli di route costanti con la @attribute direttiva .

Nell'esempio seguente la @page direttiva in un componente viene sostituita con la @attribute direttiva e il modello di route basato su costante in Constants.CounterRoute, che viene impostato altrove nell'app su "/counter":

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

Nota

Con la versione di ASP.NET Core 5.0.1 e per eventuali versioni 5.x aggiuntive, il componente Router include il parametro PreferExactMatches impostato su @true. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 3.1 a 5.0.

Concentrarsi su un elemento nella navigazione

Il FocusOnNavigate componente imposta lo stato attivo dell'interfaccia utente su un elemento basato su un selettore CSS dopo lo spostamento da una pagina a un'altra.

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

Quando il Router componente passa a una nuova pagina, il FocusOnNavigate componente imposta lo stato attivo sull'intestazione di primo livello della pagina (<h1>). Si tratta di una strategia comune per garantire che uno spostamento di pagina venga annunciato quando si usa un'utilità per la lettura dello schermo.

Fornire contenuto personalizzato quando il contenuto non viene trovato

Il Router componente consente all'app di specificare contenuto personalizzato se il contenuto non viene trovato per la route richiesta.

Impostare il contenuto personalizzato per il Router parametro del NotFound componente:

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

Gli elementi arbitrari sono supportati come contenuto del NotFound parametro, ad esempio altri componenti interattivi. Per applicare un layout predefinito al NotFound contenuto, vedere ASP.NET Layout principaliBlazor.

Importante

Blazor Web Apps non usano il NotFound parametro (<NotFound>...</NotFound> markup), ma il parametro è supportato per la compatibilità con le versioni precedenti per evitare una modifica di rilievo nel framework. La pipeline middleware core ASP.NET lato server elabora le richieste nel server. Usare tecniche lato server per gestire richieste non valide. Per altre informazioni, vedere ASP.NET Modalità di rendering coreBlazor.

Indirizzare ai componenti da più assembly

Questa sezione si applica a Blazor Web Apps.

Usare il Router parametro del AdditionalAssemblies componente e il generatore AddAdditionalAssemblies di convenzioni dell'endpoint per individuare i componenti instradabili in assembly aggiuntivi. Le sottosezioni seguenti illustrano quando e come usare ogni API.

Routing statico

Per individuare componenti instradabili da assembly aggiuntivi per il rendering statico lato server (SSR statico), anche se il router diventa in seguito interattivo per il rendering interattivo, gli assembly devono essere divulgati al Blazor framework. Chiamare il AddAdditionalAssemblies metodo con gli assembly aggiuntivi concatenati a MapRazorComponents nel file del progetto server Program .

L'esempio seguente include i componenti instradabili nell'assembly BlazorSample.Client del progetto usando il file del _Imports.razor progetto:

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

Nota

Le indicazioni precedenti si applicano anche negli scenari della libreria di classi dei componenti. Altre indicazioni importanti per le librerie di classi e ssri statici sono disponibili in ASP.NET librerie di classi core Razor (RCLs) con rendering statico lato server (SSR statico).

Routing interattivo

È possibile assegnare una modalità di rendering interattiva al Routes componente (Routes.razor) che rende il router interattivo dopo ssr Blazor statico e routing statico nel server. Ad esempio, <Routes @rendermode="InteractiveServer" /> assegna il rendering interattivo lato server (SSR interattivo) al Routes componente. Il Router componente eredita il rendering interattivo lato server (SSR interattivo) dal Routes componente. Il router diventa interattivo dopo il routing statico nel server.

Lo spostamento interno per il routing interattivo non comporta la richiesta di nuovo contenuto della pagina dal server. Di conseguenza, il prerendering non si verifica per le richieste di pagina interne. Per altre informazioni, vedere Prerender ASP.NET Componenti di baseRazor.

Se il Routes componente è definito nel progetto server, il AdditionalAssemblies parametro del Router componente deve includere l'assembly .Client del progetto. Ciò consente al router di funzionare correttamente quando ne viene eseguito il rendering in modo interattivo.

Nell'esempio seguente il Routes componente si trova nel progetto server e il _Imports.razor file del BlazorSample.Client progetto indica l'assembly in cui cercare i componenti instradabili:

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

Gli assembly aggiuntivi vengono analizzati oltre all'assembly specificato in AppAssembly.

Nota

Le indicazioni precedenti si applicano anche negli scenari della libreria di classi dei componenti.

In alternativa, i componenti instradabili esistono solo nel .Client progetto con rendering Interattivo WebAssembly o Automatico applicati e il Routes componente è definito nel .Client progetto, non nel progetto server. In questo caso, non sono presenti assembly esterni con componenti instradabili, quindi non è necessario specificare un valore per AdditionalAssemblies.

Questa sezione si applica alle Blazor Server app.

Usare il Router parametro del AdditionalAssemblies componente e il generatore AddAdditionalAssemblies di convenzioni dell'endpoint per individuare i componenti instradabili in assembly aggiuntivi.

Nell'esempio seguente è Component1 un componente instradabile definito in una libreria di classi di componenti a cui si fa riferimento denominata ComponentLibrary:

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

Gli assembly aggiuntivi vengono analizzati oltre all'assembly specificato in AppAssembly.

Parametri di route

Il router usa i parametri di route per popolare i parametri del componente corrispondenti con lo stesso nome. I nomi dei parametri di route non fanno distinzione tra maiuscole e minuscole. Nell'esempio seguente il text parametro assegna il valore del segmento di route alla proprietà del Text componente. Quando viene effettuata una richiesta per /route-parameter-1/amazing, il rendering del contenuto viene eseguito come 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; }
}

Sono supportati parametri facoltativi. Nell'esempio seguente il parametro facoltativo text assegna il valore del segmento di route alla proprietà Text del componente. Se il segmento non è presente, il valore di Text viene impostato su fantastic.

I parametri facoltativi non sono supportati. Nell'esempio seguente vengono applicate due @page direttive . La prima direttiva consente la navigazione al componente senza un parametro. La seconda direttiva assegna il valore del {text} parametro di route alla proprietà del Text 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";
    }
}

Quando viene usato il OnInitialized{Async} metodo del ciclo di vita anziché ilOnParametersSet{Async} metodo del ciclo di vita, l'assegnazione predefinita della Text proprietà a fantastic non si verifica se l'utente si sposta all'interno dello stesso componente. Ad esempio, questa situazione si verifica quando l'utente passa da /route-parameter-2/amazing a /route-parameter-2. Quando l'istanza del componente persiste e accetta nuovi parametri, il OnInitialized metodo non viene richiamato di nuovo.

Nota

I parametri di route non funzionano con i valori della stringa di query. Per usare le stringhe di query, vedere la sezione Stringhe di query.

Vincoli della route

Un vincolo di route applica la corrispondenza dei tipi in un segmento di route a un componente.

Nell'esempio seguente la route al User componente corrisponde solo se:

  • Un Id segmento di route è presente nell'URL della richiesta.
  • Il Id segmento è un tipo integer (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

I vincoli di route non funzionano con i valori della stringa di query. Per usare le stringhe di query, vedere la sezione Stringhe di query.

Sono disponibili i vincoli di route illustrati nella tabella seguente. Per i vincoli di route corrispondenti alle impostazioni cultura invarianti, vedere l'avviso sotto la tabella per altre informazioni.

Vincolo Esempio Esempi di corrispondenza Invariante
Impostazioni cultura
corrispondenti
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} Non BlazorSample.styles.css, non favicon.ico

Avviso

I vincoli di route che verificano l'URL e vengono convertiti in un tipo CLR, ad esempio int o DateTime, usano sempre le impostazioni cultura inglese non dipendenti da paese/area geografica, presupponendo che l'URL sia non localizzabile.

I vincoli di route funzionano anche con parametri facoltativi. Nell'esempio Id seguente è obbligatorio, ma Option è un parametro di route booleano facoltativo.

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; }
}

Evitare l'acquisizione di file in un parametro di route

Il modello di route seguente acquisisce inavvertitamente percorsi di asset statici nel parametro di route facoltativo (Optional). Ad esempio, il foglio di stile dell'app (.styles.css) viene acquisito, che interrompe gli stili dell'app:

@page "/{optional?}"

...

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

Per limitare un parametro di route all'acquisizione di percorsi non di file, usare il :nonfile vincolo nel modello di route:

@page "/{optional:nonfile?}"

Routing con URL che contengono punti

Un modello di route predefinito lato server presuppone che se l'ultimo segmento di un URL di richiesta contiene un punto (.) richiesto da un file. Ad esempio, l'URL /example/some.thing relativo viene interpretato dal router come richiesta di un file denominato some.thing. Senza una configurazione aggiuntiva, un'app restituisce una risposta 404 - Non trovato se some.thing è stato progettato per instradare a un componente con una @page direttiva ed some.thing è un valore del parametro di route. Per usare una route con uno o più parametri che contengono un punto, l'app deve configurare la route con un modello personalizzato.

Si consideri il componente seguente Example che può ricevere un parametro di route dall'ultimo segmento dell'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; }
}

Per consentire all'app Server di una soluzione ospitata Blazor WebAssemblydi instradare la richiesta con un punto nel param parametro di route, aggiungere un modello di route di file di fallback con il parametro facoltativo nel Program file:

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

Per configurare un'app Blazor Server per instradare la richiesta con un punto nel param parametro di route, aggiungere un modello di route di pagina di fallback con il parametro facoltativo nel Program file:

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

Per altre informazioni, vedere Routing in ASP.NET Core.

Per consentire all'app Server di una soluzione ospitata Blazor WebAssemblydi instradare la richiesta con un punto nel param parametro di route, aggiungere un modello di route di file di fallback con il parametro facoltativo in Startup.Configure.

Startup.cs:

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

Per configurare un'app Blazor Server per instradare la richiesta con un punto nel param parametro di route, aggiungere un modello di route di pagina di fallback con il parametro facoltativo in Startup.Configure.

Startup.cs:

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

Per altre informazioni, vedere Routing in ASP.NET Core.

Parametri di route catch-all

I parametri di route catch-all, che acquisiscono i percorsi attraverso più limiti di cartella, sono supportati nei componenti.

I parametri di route catch-all sono:

  • Denominato in modo che corrisponda al nome del segmento di route. La denominazione non fa distinzione tra maiuscole e minuscole.
  • Tipo string. Il framework non fornisce il cast automatico.
  • Alla fine dell'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; }
}

Per l'URL /catch-all/this/is/a/test con un modello di route di /catch-all/{*pageRoute}, il valore di PageRoute è impostato su this/is/a/test.

Le barre e i segmenti del percorso acquisito vengono decodificati. Per un modello di route di /catch-all/{*pageRoute}, l'URL /catch-all/this/is/a%2Ftest%2A restituisce this/is/a/test*.

Helper dello stato di URI e navigazione

Usare NavigationManager per gestire gli URI e lo spostamento nel codice C#. NavigationManager fornisce l'evento e i metodi illustrati nella tabella seguente.

Membro Descrizione
Uri Ottiene l'URI assoluto corrente.
BaseUri Ottiene l'URI di base (con una barra finale) che può essere anteporto ai percorsi URI relativi per produrre un URI assoluto. In genere, BaseUri corrisponde all'attributo href dell'elemento del <base> documento (posizione del <head> contenuto).
NavigateTo Passa all'URI specificato. Se forceLoad è false:
  • E la navigazione avanzata è disponibile nell'URL corrente, Blazorviene attivata la navigazione avanzata.
  • In caso contrario, Blazor esegue un ricaricamento a pagina intera per l'URL richiesto.
Se forceLoad è true:
  • Il routing lato client viene ignorato.
  • Il browser viene forzato a caricare la nuova pagina dal server, indipendentemente dal fatto che l'URI venga normalmente gestito dal router interattivo lato client.

Per altre informazioni, vedere la sezione Navigazione avanzata e gestione dei moduli.

Se replace è true, l'URI corrente nella cronologia del browser viene sostituito invece di eseguire il push di un nuovo URI nello stack di cronologia.

LocationChanged Evento che viene generato quando la posizione di spostamento è cambiata. Per altre informazioni, vedere la sezione Modifiche alla posizione .
ToAbsoluteUri Converte un URI relativo in un URI assoluto.
ToBaseRelativePath In base all'URI di base dell'app, converte un URI assoluto in un URI relativo al prefisso dell'URI di base. Per un esempio, vedere la sezione Produrre un URI relativo al prefisso dell'URI di base.
RegisterLocationChangingHandler Registra un gestore per elaborare gli eventi di spostamento in ingresso. La chiamata NavigateTo richiama sempre il gestore.
GetUriWithQueryParameter Restituisce un URI costruito aggiornando NavigationManager.Uri con un singolo parametro aggiunto, aggiornato o rimosso. Per altre informazioni, vedere la sezione Stringhe di query.
Membro Descrizione
Uri Ottiene l'URI assoluto corrente.
BaseUri Ottiene l'URI di base (con una barra finale) che può essere anteporto ai percorsi URI relativi per produrre un URI assoluto. In genere, BaseUri corrisponde all'attributo href dell'elemento del <base> documento (posizione del <head> contenuto).
NavigateTo Passa all'URI specificato. Se forceLoad è true:
  • Il routing lato client viene ignorato.
  • Il browser viene forzato a caricare la nuova pagina dal server, indipendentemente dal fatto che l'URI venga normalmente gestito dal router lato client.
Se replace è true, l'URI corrente nella cronologia del browser viene sostituito invece di eseguire il push di un nuovo URI nello stack di cronologia.
LocationChanged Evento che viene generato quando la posizione di spostamento è cambiata. Per altre informazioni, vedere la sezione Modifiche alla posizione .
ToAbsoluteUri Converte un URI relativo in un URI assoluto.
ToBaseRelativePath In base all'URI di base dell'app, converte un URI assoluto in un URI relativo al prefisso dell'URI di base. Per un esempio, vedere la sezione Produrre un URI relativo al prefisso dell'URI di base.
RegisterLocationChangingHandler Registra un gestore per elaborare gli eventi di spostamento in ingresso. La chiamata NavigateTo richiama sempre il gestore.
GetUriWithQueryParameter Restituisce un URI costruito aggiornando NavigationManager.Uri con un singolo parametro aggiunto, aggiornato o rimosso. Per altre informazioni, vedere la sezione Stringhe di query.
Membro Descrizione
Uri Ottiene l'URI assoluto corrente.
BaseUri Ottiene l'URI di base (con una barra finale) che può essere anteporto ai percorsi URI relativi per produrre un URI assoluto. In genere, BaseUri corrisponde all'attributo href dell'elemento del <base> documento (posizione del <head> contenuto).
NavigateTo Passa all'URI specificato. Se forceLoad è true:
  • Il routing lato client viene ignorato.
  • Il browser viene forzato a caricare la nuova pagina dal server, indipendentemente dal fatto che l'URI venga normalmente gestito dal router lato client.
Se replace è true, l'URI corrente nella cronologia del browser viene sostituito invece di eseguire il push di un nuovo URI nello stack di cronologia.
LocationChanged Evento che viene generato quando la posizione di spostamento è cambiata. Per altre informazioni, vedere la sezione Modifiche alla posizione .
ToAbsoluteUri Converte un URI relativo in un URI assoluto.
ToBaseRelativePath In base all'URI di base dell'app, converte un URI assoluto in un URI relativo al prefisso dell'URI di base. Per un esempio, vedere la sezione Produrre un URI relativo al prefisso dell'URI di base.
GetUriWithQueryParameter Restituisce un URI costruito aggiornando NavigationManager.Uri con un singolo parametro aggiunto, aggiornato o rimosso. Per altre informazioni, vedere la sezione Stringhe di query.
Membro Descrizione
Uri Ottiene l'URI assoluto corrente.
BaseUri Ottiene l'URI di base (con una barra finale) che può essere anteporto ai percorsi URI relativi per produrre un URI assoluto. In genere, BaseUri corrisponde all'attributo href dell'elemento del <base> documento (posizione del <head> contenuto).
NavigateTo Passa all'URI specificato. Se forceLoad è true:
  • Il routing lato client viene ignorato.
  • Il browser viene forzato a caricare la nuova pagina dal server, indipendentemente dal fatto che l'URI venga normalmente gestito dal router lato client.
LocationChanged Evento che viene generato quando la posizione di spostamento è cambiata.
ToAbsoluteUri Converte un URI relativo in un URI assoluto.
ToBaseRelativePath In base all'URI di base dell'app, converte un URI assoluto in un URI relativo al prefisso dell'URI di base. Per un esempio, vedere la sezione Produrre un URI relativo al prefisso dell'URI di base.

Modifiche alla posizione

Per l'evento LocationChanged , LocationChangedEventArgs fornisce le informazioni seguenti sugli eventi di spostamento:

Componente seguente:

  • Passa al componente dell'app Counter (Counter.razor) quando il pulsante è selezionato usando NavigateTo.
  • Gestisce l'evento di modifica della posizione sottoscrivendo NavigationManager.LocationChanged.
    • Il HandleLocationChanged metodo viene scollegato quando Dispose viene chiamato dal framework. Unhooking del metodo consente l'operazione di Garbage Collection del componente.

    • L'implementazione del logger registra le informazioni seguenti quando viene selezionato il pulsante:

      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;
    }
}

Per altre informazioni sull'eliminazione dei componenti, vedere ASP.NET ciclo di vita dei componenti principaliRazor.

Spostamento avanzato e gestione dei moduli

Questa sezione si applica a Blazor Web Apps.

Blazor Web Apps è in grado di eseguire due tipi di routing per lo spostamento di pagine e le richieste di gestione dei moduli:

  • Navigazione normale (spostamento tra documenti): viene attivato un ricaricamento a pagina intera per l'URL della richiesta.
  • Spostamento avanzato (navigazione nello stesso documento): Blazor intercetta la richiesta ed esegue invece una fetch richiesta. Blazor applica quindi patch al contenuto della risposta nel DOM della pagina. BlazorLa gestione avanzata dei moduli e della navigazione evita la necessità di ricaricare una pagina intera e mantiene più dello stato della pagina, quindi le pagine vengono caricate più velocemente, in genere senza perdere la posizione di scorrimento dell'utente nella pagina.

La navigazione avanzata è disponibile quando:

  • Lo Blazor Web App script (blazor.web.js) viene usato, non lo Blazor Server script (blazor.server.js) o Blazor WebAssembly lo script (blazor.webassembly.js).
  • La funzionalità non è disabilitata in modo esplicito.
  • L'URL di destinazione si trova all'interno dello spazio URI di base interno (percorso di base dell'app).

Se il routing sul lato server e la navigazione avanzata sono abilitati, i gestori di modifica della posizione vengono richiamati solo per la navigazione a livello di codice avviata da un runtime interattivo. Nelle versioni future, altri tipi di navigazione, ad esempio dopo un collegamento, possono anche richiamare gestori di modifica della posizione.

Quando si verifica un spostamento avanzato, LocationChanged i gestori eventi registrati con i runtime Interactive Server e WebAssembly vengono in genere richiamati. Esistono casi in cui i gestori di modifica della posizione potrebbero non intercettare una navigazione avanzata. Ad esempio, l'utente potrebbe passare a un'altra pagina prima che un runtime interattivo diventi disponibile. Pertanto, è importante che la logica dell'app non si basi sul richiamo di un gestore di modifica della posizione, perché non esiste alcuna garanzia dell'esecuzione del gestore.

Quando si chiama NavigateTo:

  • Se forceLoad è false, ovvero l'impostazione predefinita:
    • E la navigazione avanzata è disponibile nell'URL corrente, Blazorviene attivata la navigazione avanzata.
    • In caso contrario, Blazor esegue un ricaricamento a pagina intera per l'URL richiesto.
  • Se forceLoad è true: Blazor esegue un ricaricamento a pagina intera per l'URL richiesto, indipendentemente dal fatto che lo spostamento avanzato sia disponibile o meno.

È possibile aggiornare la pagina corrente chiamando NavigationManager.Refresh(bool forceLoad = false), che esegue sempre una navigazione avanzata, se disponibile. Se lo spostamento avanzato non è disponibile, Blazor esegue un ricaricamento a pagina intera.

Navigation.Refresh();

Passare true al forceLoad parametro per assicurarsi che venga sempre eseguito un ricaricamento a pagina intera, anche se la navigazione avanzata è disponibile:

Navigation.Refresh(true);

La navigazione avanzata è abilitata per impostazione predefinita, ma può essere controllata gerarchicamente e per ogni collegamento usando l'attributo data-enhance-nav HTML.

Gli esempi seguenti disabilitano la navigazione avanzata:

<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>

Se la destinazione è unBlazor endpoint diverso da un endpoint, lo spostamento avanzato non si applica e il lato client JavaScript ritenta come caricamento completo della pagina. In questo modo non viene eseguita alcuna confusione con il framework relativo alle pagine esterne che non devono essere applicate patch in una pagina esistente.

Per abilitare la gestione avanzata dei moduli, aggiungere il Enhance parametro ai EditForm moduli o all'attributo data-enhance ai moduli HTML (<form>):

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

La gestione avanzata dei moduli non è gerarchica e non passa ai moduli figlio:

Non supportato: non è possibile impostare lo spostamento avanzato sull'elemento predecessore di un modulo per abilitare la navigazione avanzata per il modulo.

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

I post di modulo avanzati funzionano solo con Blazor gli endpoint. La pubblicazione di un modulo avanzato in un endpoint diversoBlazor da un endpoint genera un errore.

Per disabilitare la navigazione avanzata:

  • Per un EditFormoggetto , rimuovere il Enhance parametro dall'elemento del modulo (o impostarlo su false: Enhance="false").
  • Per un codice HTML <form>, rimuovere l'attributo dall'elemento data-enhance form (o impostarlo su false: data-enhance="false").

BlazorSe il contenuto aggiornato non fa parte del rendering del server, la navigazione avanzata e la mano del modulo possono annullare modifiche dinamiche al DOM. Per mantenere il contenuto di un elemento, usare l'attributo data-permanent .

Nell'esempio seguente il contenuto dell'elemento <div> viene aggiornato dinamicamente da uno script quando la pagina viene caricata:

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

Dopo Blazor l'avvio nel client, è possibile usare l'evento enhancedload per ascoltare gli aggiornamenti di pagina avanzati. Ciò consente di riapplicare le modifiche al DOM che potrebbero essere state annullate da un aggiornamento di pagina avanzato.

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

Per disabilitare l'esplorazione avanzata e la gestione dei moduli a livello globale, vedere ASP.NET avvio di CoreBlazor.

La navigazione avanzata con il rendering statico lato server (SSR statico) richiede particolare attenzione durante il caricamento di JavaScript. Per altre informazioni, vedere ASP.NET Core Blazor JavaScript con rendering statico lato server (SSR statico).

Produrre un URI relativo al prefisso URI di base

In base all'URI di base dell'app, ToBaseRelativePath converte un URI assoluto in un URI relativo al prefisso dell'URI di base.

Si consideri l'esempio seguente:

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

Se l'URI di base dell'app è https://localhost:8000, vengono ottenuti i risultati seguenti:

  • Il passaggio di restituisce https://localhost:8000/segment inputURI un baseRelativePath oggetto di segment.
  • Il passaggio di restituisce https://localhost:8000/segment1/segment2 inputURI un baseRelativePath oggetto di segment1/segment2.

Se l'URI di base dell'app non corrisponde all'URI di base di inputURI, viene generata un'eccezione ArgumentException .

Il passaggio https://localhost:8001/segment inputURI comporta l'eccezione seguente:

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

NavigationManager usa l'API Cronologia del browser per mantenere lo stato della cronologia di navigazione associato a ogni modifica della posizione apportata dall'app. Mantenere lo stato della cronologia è particolarmente utile in scenari di reindirizzamento esterni, ad esempio quando si autenticano gli utenti con provider esterniidentity. Per altre informazioni, vedere la sezione Opzioni di spostamento.

Passare NavigationOptions a NavigateTo per controllare i comportamenti seguenti:

  • ForceLoad: ignorare il routing lato client e forzare il browser a caricare la nuova pagina dal server, indipendentemente dal fatto che l'URI sia gestito dal router lato client. Il valore predefinito è false.
  • ReplaceHistoryEntry: sostituire la voce corrente nello stack di cronologia. Se false, aggiungere la nuova voce allo stack di cronologia. Il valore predefinito è false.
  • HistoryEntryState: ottiene o imposta lo stato da aggiungere alla voce della cronologia.
Navigation.NavigateTo("/path", new NavigationOptions
{
    HistoryEntryState = "Navigation state"
});

Per altre informazioni su come ottenere lo stato associato alla voce della cronologia di destinazione durante la gestione delle modifiche della posizione, vedere la sezione Gestire/impedire modifiche alla posizione.

Stringhe di query

Usare l'attributo [SupplyParameterFromQuery] per specificare che un parametro del componente proviene dalla stringa di query.

Usare l'attributo con l'attributo [Parameter] [SupplyParameterFromQuery] per specificare che un parametro componente di un componente instradabile proviene dalla stringa di query.

Nota

I parametri del componente possono ricevere solo i valori dei parametri di query nei componenti instradabili con una @page direttiva .

Solo i componenti instradabili ricevono direttamente parametri di query per evitare di invertire il flusso di informazioni dall'alto verso il basso e rendere chiaro l'ordine di elaborazione dei parametri, sia dal framework che dall'app. Questa progettazione evita bug sottili nel codice dell'app scritto presupponendo un ordine di elaborazione dei parametri specifico. È possibile definire parametri a catena personalizzati o assegnare direttamente ai parametri dei componenti regolari per passare i valori dei parametri di query a componenti non instradabili.

I parametri del componente forniti dalla stringa di query supportano i tipi seguenti:

  • bool, DateTime, , doublefloat, Guid, int, long. stringdecimal
  • Varianti nullable dei tipi precedenti.
  • Matrici dei tipi precedenti, che siano nullable o meno nullable.

La formattazione invariante delle impostazioni cultura corretta viene applicata per il tipo specificato (CultureInfo.InvariantCulture).

Specificare la [SupplyParameterFromQuery] proprietà dell'attributo per usare un nome di parametro di Name query diverso dal nome del parametro del componente. Nell'esempio seguente il nome C# del parametro del componente è {COMPONENT PARAMETER NAME}. Per il {QUERY PARAMETER NAME} segnaposto viene specificato un nome di parametro di query diverso:

A differenza delle proprietà dei parametri del componente ([Parameter]), [SupplyParameterFromQuery] le proprietà possono essere contrassegnate private oltre a public.

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

Proprio come le proprietà dei parametri dei componenti ([Parameter]), [SupplyParameterFromQuery] le proprietà sono sempre public proprietà in .NET 6/7. In .NET 8 o versioni successive le [SupplyParameterFromQuery] proprietà possono essere contrassegnate public o private.

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

Nell'esempio seguente con un URL di /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman:

  • La Filter proprietà viene risolta in scifi stars.
  • La Page proprietà viene risolta in 3.
  • La Stars matrice viene riempita dai parametri di query denominati star (Name = "star") e viene risolta in LeVar Burton e Gary Oldman.

Nota

I parametri della stringa di query nel componente pagina instradabile seguente funzionano anche in un componente non instradabile senza una @page direttiva , Search.razor ad esempio per un componente condiviso Search usato in altri componenti.

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; }
}

Usare GetUriWithQueryParameter per aggiungere, modificare o rimuovere uno o più parametri di query nell'URL corrente:

@inject NavigationManager Navigation

...

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

Per l'esempio precedente:

  • Il {NAME} segnaposto specifica il nome del parametro di query. Il {VALUE} segnaposto specifica il valore come tipo supportato. I tipi supportati sono elencati più avanti in questa sezione.
  • Viene restituita una stringa uguale all'URL corrente con un singolo parametro:
    • Aggiunto se il nome del parametro di query non esiste nell'URL corrente.
    • Aggiornato al valore specificato se il parametro di query esiste nell'URL corrente.
    • Rimosso se il tipo del valore specificato è nullable e il valore è null.
  • La formattazione invariante delle impostazioni cultura corretta viene applicata per il tipo specificato (CultureInfo.InvariantCulture).
  • Il nome e il valore del parametro di query sono codificati in URL.
  • Tutti i valori con il nome del parametro di query corrispondente vengono sostituiti se sono presenti più istanze del tipo.

Chiamare GetUriWithQueryParameters per creare un URI costruito da Uri con più parametri aggiunti, aggiornati o rimossi. Per ogni valore, il framework usa value?.GetType() per determinare il tipo di runtime per ogni parametro di query e seleziona la formattazione invariante delle impostazioni cultura corretta. Il framework genera un errore per i tipi non supportati.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters({PARAMETERS})

Il {PARAMETERS} segnaposto è un oggetto IReadOnlyDictionary<string, object>.

Passare una stringa URI a GetUriWithQueryParameters per generare un nuovo URI da un URI fornito con più parametri aggiunti, aggiornati o rimossi. Per ogni valore, il framework usa value?.GetType() per determinare il tipo di runtime per ogni parametro di query e seleziona la formattazione invariante delle impostazioni cultura corretta. Il framework genera un errore per i tipi non supportati. I tipi supportati sono elencati più avanti in questa sezione.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
  • Il {URI} segnaposto è l'URI con o senza una stringa di query.
  • Il {PARAMETERS} segnaposto è un oggetto IReadOnlyDictionary<string, object>.

I tipi supportati sono identici ai tipi supportati per i vincoli di route:

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

I tipi supportati includono:

  • Varianti nullable dei tipi precedenti.
  • Matrici dei tipi precedenti, che siano nullable o meno nullable.

Avviso

Con la compressione, abilitata per impostazione predefinita, evitare di creare componenti interattivi interattivi (autenticati/autorizzati) che eseguono il rendering dei dati da origini non attendibili. Le origini non attendibili includono parametri di route, stringhe di query, dati di JS interoperabilità e qualsiasi altra origine di dati che un utente di terze parti può controllare (database, servizi esterni). Per altre informazioni, vedere linee guida ASP.NET Core BlazorSignalR e Linee guida per la mitigazione delle minacce per ASP.NET rendering lato server interattivo CoreBlazor.

Sostituire un valore del parametro di query quando il parametro esiste

Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
URL corrente URL generato
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

Aggiungere un parametro di query e un valore quando il parametro non esiste

Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
URL corrente URL generato
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

Rimuovere un parametro di query quando il valore del parametro è null

Navigation.GetUriWithQueryParameter("full name", (string)null)
URL corrente URL generato
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/

Aggiungere, aggiornare e rimuovere parametri di query

Nell'esempio seguente :

  • name viene rimosso, se presente.
  • ageviene aggiunto con un valore (25int), se non presente. Se presente, age viene aggiornato a un valore di 25.
  • eye color viene aggiunto o aggiornato a un valore di green.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["name"] = null,
        ["age"] = (int?)25,
        ["eye color"] = "green"
    })
URL corrente URL generato
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

Supporto per i valori enumerabili

Nell'esempio seguente :

  • full name viene aggiunto o aggiornato a Morena Baccarin, un singolo valore.
  • pingi parametri vengono aggiunti o sostituiti con 35, 1687 e 240.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["full name"] = "Morena Baccarin",
        ["ping"] = new int?[] { 35, 16, null, 87, 240 }
    })
URL corrente URL generato
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

Per spostarsi con una stringa di query aggiunta o modificata, passare un URL generato a NavigateTo.

L'esempio seguente chiama:

  • GetUriWithQueryParameter per aggiungere o sostituire il name parametro di query usando un valore di Morena Baccarin.
  • Chiamate NavigateTo per attivare la navigazione al nuovo URL.
Navigation.NavigateTo(
    Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));

La stringa di query di una richiesta viene ottenuta dalla NavigationManager.Uri proprietà :

@inject NavigationManager Navigation

...

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

Per analizzare i parametri di una stringa di query, un approccio consiste nell'usare URLSearchParams con l'interoperabilità JavaScript (JS):

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

Per altre informazioni sull'isolamento JavaScript con i moduli JavaScript, vedere Chiamare le funzioni JavaScript dai metodi .NET in ASP.NET Core Blazor.

Routing con hash a elementi denominati

Passare a un elemento denominato usando gli approcci seguenti con un riferimento hash (#) all'elemento . Le route agli elementi all'interno del componente e instradano gli elementi nei componenti esterni usano percorsi relativi alla radice. Una barra iniziale (/) è facoltativa.

Esempi per ognuno degli approcci seguenti illustrano la navigazione a un elemento con un id di targetElement nel Counter componente :

  • Elemento Anchor (<a>) con :href

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

    <NavLink href="/counter#targetElement">
    
  • NavigationManager.NavigateTo passaggio dell'URL relativo:

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

Nell'esempio seguente viene illustrato il routing con hash a intestazioni H2 denominate all'interno di un componente e a componenti esterni.

Home Nei componenti (Home.razor) e Counter (Counter.razor) posizionare il markup seguente nella parte inferiore del markup del componente esistente da usare come destinazioni di spostamento. Crea <div> spazio verticale artificiale per illustrare il comportamento di scorrimento del browser:

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

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

Aggiungere il componente seguente HashedRouting all'app.

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");
    }
}

Interazione dell'utente con <Navigating> il contenuto

Se si verifica un ritardo significativo durante lo spostamento, ad esempio durante il caricamento differita di assembly in un'app Blazor WebAssembly o per una connessione di rete lenta a un'app Blazor lato server, il Router componente può indicare all'utente che si sta verificando una transizione di pagina.

Nella parte superiore del componente che specifica il Router componente aggiungere una @using direttiva per lo spazio dei Microsoft.AspNetCore.Components.Routing nomi :

@using Microsoft.AspNetCore.Components.Routing

Specificare il contenuto del Navigating parametro da visualizzare durante gli eventi di transizione della pagina.

Nel contenuto dell'elemento router (<Router>...</Router>):

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

Per un esempio che usa la Navigating proprietà , vedere Lazy load assemblies in ASP.NET Core Blazor WebAssembly.

Gestire gli eventi di spostamento asincroni con OnNavigateAsync

Il Router componente supporta una OnNavigateAsync funzionalità. Il OnNavigateAsync gestore viene richiamato quando l'utente:

  • Visita un itinerario per la prima volta passando a esso direttamente nel browser.
  • Passa a una nuova route usando un collegamento o una NavigationManager.NavigateTo chiamata.
<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)
    {
        ...
    }
}

Per un esempio che usa , vedere Assembly di caricamento differita OnNavigateAsyncin ASP.NET Core Blazor WebAssembly.

Quando si esegue la pre-esecuzione sul server, OnNavigateAsync viene eseguito due volte:

  • Quando il componente endpoint richiesto viene inizialmente sottoposto a rendering statico.
  • Seconda volta che il browser esegue il rendering del componente endpoint.

Per impedire l'esecuzione del codice OnNavigateAsync dello sviluppatore due volte, il Routes componente può archiviare per l'uso NavigationContext nel metodo del OnAfterRender{Async} ciclo di vita, dove firstRender può essere controllato. Per altre informazioni, vedere Prerendering with JavaScript interop (Prerendering with JavaScript interop).

Per impedire l'esecuzione del codice OnNavigateAsync dello sviluppatore due volte, il App componente può archiviare per l'uso NavigationContext in OnAfterRender{Async}, dove firstRender può essere controllato. Per altre informazioni, vedere Prerendering with JavaScript interop (Prerendering with JavaScript interop).

Gestire gli annullamenti in OnNavigateAsync

L'oggetto NavigationContext passato al OnNavigateAsync callback contiene un CancellationToken oggetto impostato quando si verifica un nuovo evento di navigazione. Il OnNavigateAsync callback deve generare quando questo token di annullamento è impostato per evitare di continuare a eseguire il OnNavigateAsync callback in una navigazione obsoleta.

Se un utente passa a un endpoint ma passa immediatamente a un nuovo endpoint, l'app non deve continuare a eseguire il OnNavigateAsync callback per il primo endpoint.

Nell'esempio seguente :

  • Il token di annullamento viene passato alla chiamata a PostAsJsonAsync, che può annullare l'operazione POST se l'utente si allontana dall'endpoint /about .
  • Il token di annullamento viene impostato durante un'operazione di prelettura del prodotto se l'utente si allontana dall'endpoint /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

Se il token di annullamento in NavigationContext viene annullato, non viene generato un errore imprevisto, ad esempio il rendering di un componente da una struttura di spostamento precedente.

Gestire/impedire modifiche alla posizione

RegisterLocationChangingHandler registra un gestore per elaborare gli eventi di spostamento in ingresso. Il contesto del gestore fornito da LocationChangingContext include le proprietà seguenti:

Un componente può registrare più gestori di modifica della posizione nel metodo del OnAfterRender{Async} ciclo di vita. La navigazione richiama tutti i gestori di modifica della posizione registrati nell'intera app (tra più componenti) e qualsiasi spostamento interno li esegue tutti in parallelo. Oltre ai NavigateTo gestori vengono richiamati:

  • Quando si selezionano collegamenti interni, ovvero collegamenti che puntano agli URL nel percorso di base dell'app.
  • Durante lo spostamento tramite i pulsanti avanti e indietro in un browser.

I gestori vengono eseguiti solo per lo spostamento interno all'interno dell'app. Se l'utente seleziona un collegamento che passa a un sito diverso o modifica manualmente la barra degli indirizzi in un altro sito, i gestori di modifica della posizione non vengono eseguiti.

Implementare IDisposable ed eliminare i gestori registrati per annullarne la registrazione. Per altre informazioni, vedere Ciclo di vita dei componenti di ASP.NET Core Razor.

Importante

Non tentare di eseguire attività di pulizia DOM tramite interoperabilità JavaScript (JS) durante la gestione delle modifiche della posizione. Usare il MutationObserver modello in JS nel client. Per altre informazioni, vedere ASP.NET Core JavaScript interoperabilità (interoperabilità).For more information, see ASP.NET Core Blazor JavaScript interoperability (JS interop).

Nell'esempio seguente viene registrato un gestore di modifica della posizione per gli eventi di spostamento.

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();
}

Poiché lo spostamento interno può essere annullato in modo asincrono, possono verificarsi più chiamate sovrapposte ai gestori registrati. Ad esempio, più chiamate di gestori possono verificarsi quando l'utente seleziona rapidamente il pulsante Indietro in una pagina o seleziona più collegamenti prima dell'esecuzione di una navigazione. Di seguito è riportato un riepilogo della logica di spostamento asincrona:

  • Se vengono registrati gestori di modifica della posizione, tutto lo spostamento viene inizialmente ripristinato, quindi riprodotto se lo spostamento non viene annullato.
  • Se vengono effettuate richieste di spostamento sovrapposte, la richiesta più recente annulla sempre le richieste precedenti, il che significa quanto segue:
    • L'app può trattare più selezioni di pulsanti indietro e avanti come una singola selezione.
    • Se l'utente seleziona più collegamenti prima del completamento della navigazione, l'ultimo collegamento selezionato determina lo spostamento.

Per altre informazioni sul passaggio NavigationOptions a NavigateTo per controllare le voci e lo stato dello stack della cronologia di navigazione, vedere la sezione Opzioni di spostamento.

Per altri esempi di codice, vedere in ( origine di riferimento).For additional example code, see the NavigationManagerComponent in the BasicTestApp (dotnet/aspnetcore reference source).

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Il NavigationLock componente intercetta gli eventi di navigazione purché venga eseguito il rendering, "bloccando" qualsiasi navigazione specificata fino a quando non viene presa una decisione per procedere o annullare. Usare NavigationLock quando è possibile definire l'ambito dell'intercettazione di navigazione per la durata di un componente.

Parametri NavigationLock:

  • ConfirmExternalNavigation imposta una finestra di dialogo del browser per richiedere all'utente di confermare o annullare la navigazione esterna. Il valore predefinito è false. La visualizzazione della finestra di dialogo di conferma richiede l'interazione iniziale dell'utente con la pagina prima di attivare lo spostamento esterno con l'URL nella barra degli indirizzi del browser. Per altre informazioni sul requisito di interazione, vedere Window: beforeunload event (documentazione MDN).
  • OnBeforeInternalNavigation imposta un callback per gli eventi di spostamento interni.

Nel componente NavLock seguente:

  • Un tentativo di seguire il collegamento al sito Web di Microsoft deve essere confermato dall'utente prima che la navigazione https://www.microsoft.com abbia esito positivo.
  • PreventNavigationviene chiamato per impedire l'esecuzione della navigazione se l'utente rifiuta di confermare la navigazione tramite una chiamata di interoperabilità JavaScript (JS) che genera laJSconfirm finestra di dialogo.

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();
        }
    }
}

Per altri esempi di codice, vedere il componente nell'origine BasicTestAppdi riferimento (dotnet/aspnetcore ).ConfigurableNavigationLock

Usare un NavLink componente al posto degli elementi del collegamento ipertestuale HTML (<a>) durante la creazione di collegamenti di spostamento. Un NavLink componente si comporta come un <a> elemento, ad eccezione del fatto che attiva o disattiva una active classe CSS in base al fatto href che corrisponda all'URL corrente. La active classe aiuta un utente a capire quale pagina è la pagina attiva tra i collegamenti di spostamento visualizzati. Facoltativamente, assegnare un nome di classe CSS a per NavLink.ActiveClass applicare una classe CSS personalizzata al collegamento sottoposto a rendering quando la route corrente corrisponde a href.

Sono disponibili due NavLinkMatch opzioni che è possibile assegnare all'attributo Match dell'elemento <NavLink> :

Nell'esempio precedente, corrisponde HomeNavLink href="" all'URL home e riceve solo la active classe CSS nel percorso di base predefinito dell'app (/). Il secondo NavLink riceve la active classe quando l'utente visita qualsiasi URL con un component prefisso , /component ad esempio e /component/another-segment.

Gli attributi aggiuntivi NavLink dei componenti vengono passati al tag di ancoraggio sottoposto a rendering. Nell'esempio seguente il NavLink componente include l'attributo target :

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

Viene eseguito il rendering del markup HTML seguente:

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

Avviso

A causa del modo in cui Blazor esegue il rendering del contenuto figlio, il rendering NavLink dei componenti all'interno di un for ciclo richiede una variabile di indice locale se la variabile del ciclo di incremento viene usata nel contenuto del NavLink componente (figlio):

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

L'uso di una variabile di indice in questo scenario è un requisito per qualsiasi componente figlio che usa una variabile di ciclo nel contenuto figlio, non solo per il NavLink componente.

In alternativa, usare un foreach ciclo con Enumerable.Range:

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

NavLink Le voci dei componenti possono essere create dinamicamente dai componenti dell'app tramite reflection. Nell'esempio seguente viene illustrato l'approccio generale per un'ulteriore personalizzazione.

Per la dimostrazione seguente viene usata una convenzione di denominazione standard coerente per i componenti dell'app:

  • I nomi dei file dei componenti instradabili usano la distinzione tra maiuscole e minuscole Pascal†, ad esempio Pages/ProductDetail.razor.
  • I percorsi dei file dei componenti instradabili corrispondono ai rispettivi URL nel caso kebab++ con trattini visualizzati tra le parole nel modello di route di un componente. Ad esempio, un componente ProductDetail con un modello di route /product-detail (@page "/product-detail") viene richiesto in un browser nell'URL relativo /product-detail.

†La notazione Pascal (lettere maiuscole) è una convenzione di denominazione senza spazi e punteggiatura e con la prima lettera di ogni parola in maiuscolo, inclusa la prima parola.
*Kebab case è una convenzione di denominazione senza spazi e punteggiatura che usa lettere minuscole e trattini tra le parole.

Razor Nel markup del NavMenu componente (NavMenu.razor) nella pagina predefinitaHome, NavLink i componenti vengono aggiunti da una raccolta:

<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>

Metodo GetRoutableComponents nel @code blocco:

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);

L'esempio precedente non include le pagine seguenti nell'elenco di componenti di cui è stato eseguito il rendering:

  • Home page: la pagina viene elencata separatamente dai collegamenti generati automaticamente perché dovrebbe essere visualizzata nella parte superiore dell'elenco e impostare il Match parametro .
  • Error page: la pagina di errore viene visualizzata solo dal framework e non deve essere elencata.

Per un esempio del codice precedente in un'app di esempio che è possibile eseguire in locale, ottenere l'app Blazor Web Appdi esempio o Blazor WebAssembly .

integrazione del routing degli endpoint core ASP.NET

Questa sezione si applica a Blazor Web Apps che opera su un circuito.

Questa sezione si applica alle Blazor Server app.

Un Blazor Web App oggetto è integrato in ASP.NET routing dell'endpoint principale. Un'app ASP.NET Core è configurata per accettare connessioni in ingresso per i componenti interattivi con MapRazorComponents nel Program file. Il componente radice predefinito (primo componente caricato) è il App componente (App.razor):

app.MapRazorComponents<App>();

Blazor Server è integrato in ASP.NET routing dell'endpoint principale. Un'app ASP.NET Core è configurata per accettare connessioni in ingresso per i componenti interattivi con MapBlazorHub nel Program file:

app.UseRouting();

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

Blazor Server è integrato in ASP.NET routing dell'endpoint principale. Un'app core ASP.NET è configurata per accettare connessioni in ingresso per i componenti interattivi con MapBlazorHub in Startup.Configure.

La configurazione tipica consiste nell'instradare tutte le richieste a una Razor pagina, che funge da host per la parte lato server dell'app Blazor Server . Per convenzione, la pagina host viene in genere denominata _Host.cshtml nella Pages cartella dell'app.

La route specificata nel file host viene chiamata route di fallback perché opera con una priorità bassa nella corrispondenza delle route. La route di fallback viene usata quando altre route non corrispondono. In questo modo l'app può usare altri controller e pagine senza interferire con il routing dei componenti nell'app Blazor Server .

Per informazioni sulla configurazione MapFallbackToPage per l'hosting di server URL non radice, vedere Ospitare e distribuire ASP.NET Core Blazor.