Поделиться через


Маршрутизация ASP.NET Core Blazor и навигация

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см. версию этой статьи для .NET 9.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии смотрите статью о версии .NET 9.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске смотрите версию .NET 9 этой статьи.

В этой статье объясняется, как управлять Blazor маршрутизацией запросов приложений и как использовать NavLink компонент для создания ссылок навигации.

Внимание

Примеры кода в этой статье показывают вызовы методов на Navigation, который является инъецированным NavigationManager в классах и компонентах.

Статическая и интерактивная маршрутизация

Этот раздел относится к Blazor Web Apps.

Если предварительная отрисовка включена, Blazor маршрутизатор (Routerкомпонент) <Router>Routes.razor выполняет статическую маршрутизацию компонентов во время отрисовки на стороне статического сервера (статический SSR). Этот тип маршрутизации называется статической маршрутизацией.

Когда компоненту назначен интерактивный режим отрисовки Routes, маршрутизатор Blazor становится интерактивным после выполнения статического SSR и статической маршрутизации на сервере. Этот тип маршрутизации называется интерактивной маршрутизацией.

Статические маршрутизаторы используют маршрутизацию конечных точек и путь HTTP-запроса для определения компонента для отрисовки. Когда маршрутизатор становится интерактивным, он использует URL-адрес документа (URL-адрес в адресной строке браузера) для определения компонента для отрисовки. Это означает, что интерактивный маршрутизатор может динамически изменять, какой компонент отображается, если URL-адрес документа динамически изменяется на другой допустимый внутренний URL-адрес, и он может сделать это без выполнения HTTP-запроса для получения нового содержимого страницы.

Интерактивная маршрутизация также предотвращает предварительное отображение, так как новое содержимое страницы не запрашивается с сервера с обычным запросом страницы. Дополнительные сведения см. в разделе Компоненты ASP.NET Core с предварительной отрисовкойRazor.

Шаблоны маршрутов

Компонент Router обеспечивает маршрутизацию к Razor компонентам и находится в компоненте приложения Routes (Components/Routes.razor).

Компонент Router обеспечивает маршрутизацию к Razor компонентам. Компонент Router используется в компоненте App (App.razor).

При компиляции компонента Razor (.razor) с директивой @page созданный класс компонента предоставляет атрибут RouteAttribute для указания шаблона маршрута.

При запуске приложения выполняется проверка сборки, указанной как AppAssembly маршрутизатора, для сбора сведений о маршрутах для компонентов приложения с RouteAttribute.

В среде выполнения компонент RouteView выполняет следующие операции:

  • получает RouteData от Router вместе со всеми параметрами маршрута;
  • Отображает указанный компонент с его макетом, включая любые дополнительные вложенные макеты.

При необходимости укажите параметр DefaultLayout с классом макета для компонентов, которые не задают макет с помощью директивы @layout. Шаблоны проекта платформы Blazor определяют компонент MainLayout (MainLayout.razor) как макет приложения по умолчанию. Дополнительные сведения о макетах см. в статье Макеты ASP.NET Core Blazor.

Компоненты поддерживают несколько шаблонов маршрутов с помощью нескольких директив @page. Приведенный ниже пример компонента загружается при запросах к /blazor-route и /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>

Внимание

Для правильного разрешения URL-адресов приложение должно содержать тег <base> (расположение содержимого <head>) с базовым путем к приложению, указанным в атрибуте href. Дополнительные сведения см. в статье Размещение и развертывание ASP.NET Core Blazor.

Router не взаимодействует со строковыми значениями запроса. Сведения о работе со строками запроса см. в разделе "Строки запроса".

В качестве альтернативы указанию шаблона маршрута как строкового литерала с директивой @page, шаблоны маршрутов на основе констант могут быть указаны с директивой @attribute.

В следующем примере директива @page в компоненте заменяется на директиву @attribute, а шаблон маршрута на основе констант в Constants.CounterRoute в приложении устанавливается в значение "/counter":

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

Примечание.

В выпуске ASP.NET Core 5.0.1 и дальнейших выпусках 5.x компонент Router содержит параметр PreferExactMatches со значением @true. Дополнительные сведения см. в статье Миграция с ASP.NET Core 3.1 на 5.0.

Фокусировка элемента на навигации

Компонент FocusOnNavigate задает фокус пользовательского интерфейса элементу на основе селектора CSS после перехода с одной страницы на другую.

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

Когда компонент Router переходит на новую страницу, компонент FocusOnNavigate устанавливает фокус на заголовок верхнего уровня страницы (<h1>). Это распространенная стратегия для обеспечения объявления навигации по страницам при использовании пользователем средства чтения с экрана.

Предоставление пользовательского содержимого, когда содержимое не найдено

Компонент Router позволяет приложению указать пользовательское содержимое, если содержимое для запрошенного маршрута не найдено.

Задайте настраиваемое содержимое для Router параметра компонента NotFound :

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

Произвольные элементы поддерживаются в качестве содержимого NotFound параметра, например других интерактивных компонентов. Сведения о применении макета по умолчанию к содержимому NotFound см. в статье Макеты Blazor в ASP.NET Core.

Внимание

Blazor Web Appне используют параметр NotFound (<NotFound>...</NotFound> разметку), но параметр поддерживается† для обратной совместимости, чтобы избежать разрушающих изменений в фреймворке. Конвейер промежуточного программного обеспечения ASP.NET Core обрабатывает запросы на сервере. Используйте методы на стороне сервера для обработки плохих запросов.

Поддержка в этом контексте означает, что помещение разметки <NotFound>...</NotFound> не приводит к исключению, но и использование разметки не приносит результата.

Дополнительные сведения, включая рекомендуемый подход к обработке ошибочных запросов, см. раздел режимы рендеринга ASP.NET Core Blazor.

Маршрутизация к компонентам из множества сборок

Этот раздел относится к Blazor Web Apps.

Используйте параметр AdditionalAssemblies компонента Router и создатель соглашений для конечных точек AddAdditionalAssemblies для обнаружения маршрутизируемых компонентов в дополнительных сборках. В следующих подразделах объясняется, когда и как использовать каждый API.

Статическая маршрутизация

Чтобы обнаружить маршрутизируемые компоненты из дополнительных сборок для статической серверной отрисовки (SSR), даже если маршрутизатор затем становится интерактивным для интерактивной отрисовки, сборки должны быть предоставлены платформе Blazor. Вызовите метод AddAdditionalAssemblies с дополнительными сборками, связанными с MapRazorComponents в файле Program проекта сервера.

Пример ниже включает маршрутизируемые компоненты в сборке проекта BlazorSample.Client с использованием файла проекта _Imports.razor.

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

Примечание.

Приведенные выше рекомендации также применяются в сценариях библиотеки классов компонентов . Дополнительные важные рекомендации по библиотекам классов и статическим SSR находятся в библиотеках классов ASP.NET Core Razor (RCLs) со статическим отображением на стороне сервера (статический SSR).

Интерактивная маршрутизация

Интерактивный режим отрисовки можно назначить компоненту Routes (Routes.razor), который делает маршрутизатор Blazor становящимся интерактивным после выполнения статического SSR и статической маршрутизации на сервере. Например, <Routes @rendermode="InteractiveServer" /> назначает интерактивную отрисовку на стороне сервера (Interactive SSR) для компонента Routes. Компонент Router наследует интерактивную серверную отрисовку (интерактивное SSR) от компонента Routes. Маршрутизатор становится интерактивным после статической маршрутизации на сервере.

Внутренняя навигация для интерактивной маршрутизации не включает запрос нового содержимого страницы с сервера. Поэтому предварительное отображение не выполняется для внутренних запросов страниц. Дополнительные сведения см. в разделе Предварительный рендеринг компонентов ASP.NET CoreRazor.

Если компонент Routes определен в серверном проекте, то параметр AdditionalAssemblies компонента Router должен включать сборку проекта .Client. Это позволяет маршрутизатору работать правильно при интерактивном отображении.

В следующем примере компонент Routes находится в серверном проекте, а файл _Imports.razor проекта BlazorSample.Client указывает сборку, в которой следует искать маршрутизируемые компоненты.

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

Дополнительные сборки проверяются в дополнение к сборке, указанной в AppAssembly.

Примечание.

Приведенные выше рекомендации также применяются в сценариях библиотеки классов компонентов .

Кроме этого, в проекте .Client существуют только маршрутизируемые компоненты с глобальным Interactive WebAssembly или Auto rendering, а компонент Routes определен в проекте .Client, а не в серверном проекте. В этом случае нет внешних сборок с маршрутизируемыми компонентами, поэтому не нужно указывать значение для AdditionalAssemblies.

Этот раздел относится к приложениям Blazor Server.

Используйте параметр AdditionalAssemblies компонента Router, а также конвенционный построитель конечных точек AddAdditionalAssemblies для обнаружения маршрутизируемых компонентов в дополнительных сборках.

В следующем примере Component1 — это маршрутизируемый компонент, определенный в указанной библиотеке классов компонентов с именемComponentLibrary.

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

В дополнение к сборке, указанной в AppAssembly, проверяются дополнительные сборки.

Параметры маршрута

Маршрутизатор использует параметры маршрута для заполнения соответствующих параметров компонента с тем же именем. В именах параметров маршрута регистр не учитывается. В приведенном ниже примере параметр text присваивает значение сегмента маршрута свойству Text компонента. При выполнении /route-parameter-1/amazingзапроса содержимое отображается как 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; }
}

Поддерживаются необязательные параметры. В следующем примере необязательный параметр text назначает значение сегмента маршрута свойству Text компонента. Если сегмента нет, для Text устанавливается значение fantastic.

Необязательные параметры не поддерживаются. В приведенном ниже примере применяются две директивы @page. Первая директива позволяет переходить к компоненту без параметра. Вторая директива присваивает значение параметра маршрута {text} свойству Text компонента.

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

Когда метод жизненного цикла OnInitialized{Async} используется вместо метода жизненного цикла OnParametersSet{Async}, назначение свойства Text по умолчанию на fantastic не происходит, если пользователь перемещается в пределах того же компонента. Например, эта ситуация возникает, когда пользователь переходит из /route-parameter-2/amazing/route-parameter-2. Так как экземпляр компонента сохраняется и принимает новые параметры, OnInitialized метод не вызывается снова.

Примечание.

Параметры маршрута не работают со значениями строки запроса. Сведения о работе со строками запроса см. в разделе "Строки запроса".

Ограничения маршрута

Ограничение маршрута обеспечивает соответствие типа сегмента маршрута компоненту.

В следующем примере маршрут к компоненту User соответствует только в следующих случаях:

  • в URL-адресе запроса имеется сегмент маршрута Id;
  • сегмент Id имеет целочисленный тип (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; }
}

Примечание.

Ограничения маршрута не работают со значениями строки запроса. Сведения о работе со строками запроса см. в разделе "Строки запроса".

Доступны ограничения маршрутов, приведенные в следующей таблице. Сведения об ограничениях маршрута, соответствующих инвариантной культуре, см. в предупреждении под таблицей для получения дополнительной информации.

Ограничение Пример Примеры совпадений Инвариант
Культура
соответствие
bool {active:bool} true, FALSE Нет
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} Нет
int {id:int} 123456789, -123456789 Да
long {ticks:long} 123456789, -123456789 Да
nonfile {parameter:nonfile} Не BlazorSample.styles.css, не favicon.ico Да

Предупреждение

Ограничения маршрута, которые проверяют URL-адрес и могут быть преобразованы в тип CLR (например, int или DateTime), всегда используют инвариантные культурные настройки. Эти ограничения предполагают, что URL-адрес является нелокализуемым.

Ограничения маршрута также работают с необязательными параметрами. В следующем примере Id является обязательным параметром, а Option — необязательным логическим параметром маршрута.

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

Избегайте записи файлов в параметре маршрута

Следующий шаблон маршрута непреднамеренно фиксирует пути статических ресурсов в необязательном параметре маршрута (Optional). Например, захватывается таблица стилей приложения (.styles.css), что нарушает стили приложения.

@page "/{optional?}"

...

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

Чтобы ограничить параметр маршрута записью путей, отличных от файлов, используйте :nonfile ограничение в шаблоне маршрута:

@page "/{optional:nonfile?}"

Маршрутизация URL-адресов, содержащих точки

Шаблон маршрута на стороне сервера по умолчанию предполагает, что если последний сегмент URL-адреса запроса содержит точку (.), то запрашивается файл. Например, относительный URL-адрес /example/some.thing интерпретируется маршрутизатором как запрос файла с именем some.thing. Без дополнительной настройки приложение возвращает ответ 404 — не найден, если some.thing предназначен для маршрутизации на компонент с @page директивой и some.thing является значением параметра маршрута. Чтобы использовать маршрут с одним параметром или несколькими, содержащими точку, в приложении необходимо настроить маршрут с помощью пользовательского шаблона.

Рассмотрим приведенный ниже компонент Example, который может получать параметр маршрута из последнего сегмента 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; }
}

Чтобы разрешить Server приложению хостингового Blazor WebAssemblyрешения маршрутизировать запрос с точкой в param параметре маршрута, добавьте резервный шаблон маршрута файла с опциональным параметром в Program файле:

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

Чтобы настроить Blazor Server приложение для маршрутизации запроса с точкой в param параметре маршрута, добавьте резервный шаблон маршрута страницы с необязательным параметром Program в файле:

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

Подробные сведения см. в статье Маршрутизация в ASP.NET Core.

Чтобы разрешить приложению Server размещенного решения Blazor WebAssemblyмаршрутизировать запрос с точкой в параметре маршрута param, добавьте шаблон маршрута резервного файла с необязательным параметром в Startup.Configure.

Startup.cs:

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

Чтобы позволить приложению Blazor Server маршрутизировать запрос с точкой в параметре маршрута param, добавьте шаблон резервного маршрута к странице с дополнительным параметром в Startup.Configure.

Startup.cs:

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

Подробные сведения см. в статье Маршрутизация в ASP.NET Core.

Параметры универсального маршрута

В компонентах поддерживаются catch-all параметры маршрутов, которые захватывают пути, проходящие через несколько границ папок.

Параметры маршрута catch-all

  • Его имя должно соответствовать имени сегмента маршрута. Именование не чувствительно к регистру.
  • Тип string. Платформа не обеспечивает автоматическое приведение.
  • В конце 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; }
}

Для URL-адреса /catch-all/this/is/a/test с шаблоном маршрута /catch-all/{*pageRoute} значение PageRoute равно this/is/a/test.

Косые черты и сегменты захваченного пути декодированы. Для шаблона маршрута /catch-all/{*pageRoute}, URL-адрес /catch-all/this/is/a%2Ftest%2A дает this/is/a/test*.

URI и вспомогательные средства для навигации

Используйте NavigationManager для управления кодами URI и навигацией в коде C#. NavigationManager предоставляет события и методы, приведенные в следующей таблице.

Член Описание
Uri Возвращает текущий абсолютный URI.
BaseUri Получает базовый URI (с завершающим слэшем), который можно присоединить к относительным путям URI для получения абсолютного URI. Как правило, BaseUri соответствует атрибуту href элемента <base> документа (расположение содержимого <head>).
NavigateTo Переходит по указанному URI. Если значение forceLoad равно false:
  • Также доступна расширенная навигация по текущему URL-адресу, и расширенная навигация Blazor активирована.
  • Blazor В противном случае выполняется полная перезагрузка страницы для запрошенного URL-адреса.
Если значение forceLoad равно true:
  • маршрутизация на стороне клиента обходится;
  • Браузер вынужден загрузить новую страницу с сервера, независимо от того, обрабатывается ли URI на стороне клиента интерактивным маршрутизатором.

Дополнительные сведения см. в разделе "Расширенная навигация и обработка форм".

Если replace имеет значение true, текущий URI в журнале браузера заменяется вместо отправки нового URI в стек журнала.

LocationChanged Событие, возникающее при изменении расположения навигации. Дополнительные сведения см. в разделе Изменения расположения.
ToAbsoluteUri Преобразует относительный URI в абсолютный.
ToBaseRelativePath На основе базового URI приложения преобразует абсолютный URI в URI относительно базового URI. Например, см. раздел "Создать URI, относящийся к базовому префиксу URI".
RegisterLocationChangingHandler Регистрирует обработчик для обработки входящих событий навигации. Вызов NavigateTo всегда вызывает обработчик.
GetUriWithQueryParameter Возвращает URI, созданный путем обновления NavigationManager.Uri с добавлением, обновлением или удалением одного параметра. Дополнительные сведения см. в разделе Строки запросов.
Член Описание
Uri Возвращает текущий абсолютный URI.
BaseUri Получает базовый URI (с завершающей косой чертой), который можно добавить в начало относительных путей URI для получения абсолютного URI. Как правило, BaseUri соответствует атрибуту href элемента <base> документа (расположение содержимого <head>).
NavigateTo Переходит по указанному URI. Если значение forceLoad равно true:
  • маршрутизация на стороне клиента обходится;
  • Браузер вынужден загрузить новую страницу с сервера, независимо от того, обрабатывается ли URI клиентским маршрутизатором.
Если replace имеет значение true, текущий URI в журнале браузера заменяется вместо отправки нового URI в стек журнала.
LocationChanged Событие, которое возникает при изменении местоположения навигации. Дополнительные сведения см. в разделе Изменения расположения.
ToAbsoluteUri Преобразует относительный URI в абсолютный.
ToBaseRelativePath Используя базовый URI приложения, преобразует абсолютный URI в URI относительно префикса базового URI. Пример см. в разделе «Создание URI относительно базового префикса URI».
RegisterLocationChangingHandler Регистрирует обработчик для обработки входящих событий навигации. Вызов NavigateTo всегда вызывает обработчик.
GetUriWithQueryParameter Возвращает URI, созданный путем обновления NavigationManager.Uri с добавлением, обновлением или удалением одного параметра. Дополнительные сведения см. в разделе Строки запросов.
Член Описание
Uri Возвращает текущий абсолютный URI.
BaseUri Получает базовый URI (с завершающей косой чертой), который можно добавить в начало относительных путей URI для получения абсолютного URI. Как правило, BaseUri соответствует атрибуту href элемента <base> документа (расположение содержимого <head>).
NavigateTo Переходит по указанному URI. Если значение forceLoad равно true:
  • Маршрутизация на стороне клиента игнорируется.
  • браузер вынужден загрузить новую страницу с сервера, независимо от того, обрабатывается ли URI клиентским маршрутизатором или нет.
Если replace имеет значение true, текущий URI в журнале браузера заменяется вместо отправки нового URI в стек журнала.
LocationChanged Событие, возникающее при изменении точки навигации. Дополнительные сведения см. в разделе Изменения расположения.
ToAbsoluteUri Преобразует относительный URI в абсолютный.
ToBaseRelativePath На основе базового URI приложения преобразует абсолютный URI в URI относительно префикса базового URI. Для примера см. раздел Создание URI относительно базового префикса URI.
GetUriWithQueryParameter Возвращает URI, созданный путем обновления NavigationManager.Uri с добавлением, обновлением или удалением одного параметра. Дополнительные сведения см. в разделе Строки запросов.
Член Описание
Uri Возвращает текущий абсолютный URI.
BaseUri Получает базовый URI (с завершающей косой чертой), который можно добавить в начало относительных путей URI для получения абсолютного URI. Как правило, BaseUri соответствует атрибуту href элемента <base> документа (расположение содержимого <head>).
NavigateTo Переходит по указанному URI. Если значение forceLoad равно true:
  • Маршрутизация на стороне клиента пропускается.
  • Браузер вынужден загружать новую страницу с сервера, независимо от того, обрабатывается ли URI клиентским маршрутизатором.
LocationChanged Событие, возникающее при изменении расположения навигации.
ToAbsoluteUri Преобразует относительный URI в абсолютный.
ToBaseRelativePath На основе базового URI приложения преобразует абсолютный URI в URI, относительный к префиксу базового URI. Пример приведён в разделе Создание относительного URI от базового префикса URI.

Изменения расположения

Для события LocationChangedLocationChangedEventArgs предоставляет следующие сведения о событиях навигации.

Приведенный ниже компонент делает следующее.

  • переходит к компоненту Counter приложения (Counter.razor) при нажатии кнопки с помощью NavigateTo;
  • обрабатывает событие изменения расположения путем оформления подписки на NavigationManager.LocationChanged.
    • При вызове HandleLocationChanged платформой метод Dispose отсоединяется. После отсоединения метода становится возможной сборка мусора для компонента.

    • При нажатии кнопки реализация средства ведения журнала записывает следующие сведения.

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

Дополнительные сведения об удалении компонентов см. в разделе Удаление компонентов в ASP.NET Core Razor.

Улучшенная навигация и обработка форм

Этот раздел относится к Blazor Web Apps.

Blazor Web Apps поддерживает два типа маршрутизации для запросов навигации по страницам и обработки форм:

  • Обычная навигация (перекрестная навигация по документу): для URL-адреса запроса активируется полная перезагрузка.
  • Улучшенная навигация (навигация с тем же документом): Blazor перехватывает запрос и выполняет fetch запрос. Blazor затем вставляет содержимое ответа в DOM страницы. BlazorУлучшенная навигация и обработка форм избегают необходимости полностраничной перезагрузки и, при этом, сохраняют больше состояния страницы, что помогает страницам загружаться быстрее, обычно без потери позиции прокрутки пользователя на странице.

Улучшенная навигация доступна при следующих случаях:

  • Скрипт Blazor Web App (blazor.web.js) используется, а не Blazor Server скрипт (blazor.server.js) или Blazor WebAssembly скрипт (blazor.webassembly.js).
  • Эта функция не отключена явным образом.
  • URL-адрес назначения находится в пределах внутреннего базового пространства URI (базовый путь приложения).

Если включена маршрутизация на стороне сервера и расширенная навигация, обработчики изменения расположения вызываются только для программной навигации, инициированной из интерактивной среды выполнения. В будущих выпусках дополнительные типы навигации, такие как переход по ссылке, также могут вызывать обработчики изменения местоположения.

Когда происходит расширенная навигация, обработчики событий, LocationChanged зарегистрированные в интерактивном сервере и среде выполнения WebAssembly, обычно вызываются. В некоторых случаях обработчики изменения местоположения не могут перехватывать расширенную навигацию. Например, пользователь может переключиться на другую страницу перед тем, как интерактивная среда выполнения станет доступной. Поэтому важно, чтобы логика приложения не зависела от вызова обработчика изменения расположения, так как не гарантируется выполнение обработчика.

При вызове NavigateTo:

  • Если forceLoad — это false, как и установлено по умолчанию:
    • И расширенная навигация доступна по текущему URL-адресу, Blazorактивируется расширенная навигация.
    • Blazor В противном случае выполняется полная перезагрузка страницы для запрошенного URL-адреса.
  • Если forceLoad это true: Blazor выполняет полную перезагрузку страницы для запрошенного URL-адреса, независимо от того, доступна ли расширенная навигация.

Вы можете обновить текущую страницу, вызвав NavigationManager.Refresh(bool forceLoad = false)функцию, которая всегда выполняет расширенную навигацию, если она доступна. Если расширенная навигация недоступна, Blazor выполняет полную перезагрузку страницы.

Navigation.Refresh();

Передайте true параметру forceLoad, чтобы убедиться, что полная перезагрузка страницы всегда выполняется, даже если имеется расширенная навигация.

Navigation.Refresh(true);

Расширенная навигация включена по умолчанию, но она может управляться иерархически и на основе каждой ссылки с помощью атрибута data-enhance-nav HTML.

В следующих примерах отключена расширенная навигация:

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

Если назначение не является конечнойBlazor точкой, расширенная навигация не применяется, и JavaScript на стороне клиента повторно загружает страницу целиком. Это гарантирует отсутствие путаницы для фреймворка относительно внешних страниц, которые не должны быть внедрены в существующую страницу.

Чтобы включить расширенную обработку форм, добавьте Enhance параметр в EditForm формы или data-enhance атрибут в HTML-формы (<form>):

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

Улучшенная обработка форм не является иерархической и не будет передаваться в дочерние формы:

Неподдерживается: нельзя задать расширенную навигацию на элементе-предке формы, чтобы включить ее для формы.

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

Расширенные записи форм работают только с Blazor конечными точками. Публикация расширенной формы в неконечнуюBlazor точку приводит к ошибке.

Чтобы отключить расширенную навигацию, выполните приведенные действия.

  • Удалите параметр Enhance из элемента формы (или задайте для него значение false: Enhance="false").
  • Для HTML <form>удалите data-enhance атрибут из элемента формы (или задайте для него falseзначение : data-enhance="false").

BlazorУлучшенная навигация и обработка форм могут отменить динамические изменения в DOM, если обновленное содержимое не является частью рендеринга сервера. Чтобы сохранить содержимое элемента, используйте data-permanent атрибут.

В следующем примере содержимое <div> элемента динамически обновляется скриптом при загрузке страницы:

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

После запуска Blazor на клиенте можно использовать событие enhancedload для отслеживания расширенных обновлений страниц. Это позволяет повторно применять изменения к DOM, которые, возможно, были отменены расширенным обновлением страницы.

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

Сведения об отключении расширенной навигации и глобальной обработки форм см. в разделе ASP.NET Core Blazor startup.

Улучшенная навигация с стати́ческой серверной отрисо́вкой (SSR) требует особого внимания при загрузке JavaScript. Дополнительные сведения см. в разделе ASP.NET Core Blazor JavaScript со статическим отображением на стороне сервера (статический SSR).

Создайте URI относительно префикса базового URI

Исходя из базового URI приложения, ToBaseRelativePath преобразует абсолютный URI в URI, относительно префикса базового URI.

Рассмотрим следующий пример:

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

Если базовый универсальный код ресурса (URI) приложения равен https://localhost:8000, то получаются следующие результаты:

  • https://localhost:8000/segment в inputURI приводит к baseRelativePath в segment.
  • Передача https://localhost:8000/segment1/segment2 в inputURI приводит к baseRelativePath из segment1/segment2.

Если базовый URI приложения не соответствует базовому URI inputURI, будет выброшено исключение ArgumentException.

Передача https://localhost:8001/segment в inputURI приводит к следующему исключению:

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

NavigationManager использует History API браузера, чтобы поддерживать состояние истории навигации, связанное с каждым изменением местоположения, внесенным приложением. Поддержание состояния истории особенно полезно в сценариях внешнего перенаправления, например, при аутентификации пользователей с внешними поставщиками идентификационных данных. Дополнительные сведения см. в разделе Параметры навигации.

Передайте NavigationOptions в NavigateTo для управления следующими функциями:

  • ForceLoad: Обойти маршрутизацию на стороне клиента и принудительно загрузить новую страницу с сервера, независимо от того, обрабатывается ли URI маршрутизатором на стороне клиента. Значение по умолчанию — false.
  • ReplaceHistoryEntry: замените текущую запись в стеке истории. Если false, добавьте новую запись в стек журнала. Значение по умолчанию — false.
  • HistoryEntryState: возвращает или задает состояние, которое будет добавлено к записи журнала.
Navigation.NavigateTo("/path", new NavigationOptions
{
    HistoryEntryState = "Navigation state"
});

Дополнительные сведения о получении состояния, связанного с записью в целевой журнал при обработке изменений расположения, см. в разделе Обработка и предотвращение изменений расположения.

Строки запросов

[SupplyParameterFromQuery] Используйте атрибут, чтобы указать, что параметр компонента поступает из строки запроса.

Используйте атрибут [SupplyParameterFromQuery] с атрибутом [Parameter], чтобы указать, что параметр компонента маршрутизируемого поступает из строки запроса.

Примечание.

Параметры компонента могут принимать значения параметров запроса в маршрутизируемых компонентах с помощью директивы @page.

Только маршрутизируемые компоненты напрямую получают параметры запроса, чтобы избежать переключения потока сведений сверху вниз и очистить порядок обработки параметров, как платформы, так и приложения. Эта конструкция позволяет избежать тонких ошибок в коде приложения, написанном при условии определенного порядка обработки параметров. Вы можете задать настраиваемые каскадные параметры или напрямую назначить их обычным параметрам компонента, чтобы передать значения параметров запроса в нераутируемые компоненты.

Параметры компонента, предоставляемые из строки запроса, поддерживают следующие типы:

  • bool, DateTime, decimaldoublefloatGuidintlongstring.
  • Варианты предыдущих типов, допускающие значение NULL.
  • Массивы предыдущих типов, допускающие или не допускающие значение NULL.

Корректное форматирование для инвариантных языка и региональных параметров применяется для данного типа (CultureInfo.InvariantCulture).

Укажите свойство [SupplyParameterFromQuery] атрибута Name, чтобы использовать имя параметра запроса, отличное от имени параметра компонента. В следующем примере в C# именем параметра компонента является {COMPONENT PARAMETER NAME}. Для заполнителя {QUERY PARAMETER NAME} указано другое имя параметра запроса:

В отличие от свойств параметров компонента ([Parameter]), свойства [SupplyParameterFromQuery] можно пометить private, так же, как и public.

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

Как и свойства параметра компонента ([Parameter]), [SupplyParameterFromQuery] свойства в .NET 6/7 всегда являются public свойствами. В .NET 8 или более поздней версии [SupplyParameterFromQuery] свойства можно пометить public или private.

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

В следующем примере с URL-адресом /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman:

  • Свойство Filter разрешается в значение scifi stars.
  • Свойство Page преобразуется в 3.
  • Массив Stars заполняется из параметров запроса с именем star (Name = "star") и преобразуется в LeVar Burton и Gary Oldman.

Примечание.

Параметры строки запроса в следующем маршрутизируемом компоненте страницы также работают в немаршрутизируемом компоненте без @page директивы (например, для общего Search.razor компонента, Search используемого в других компонентах).

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

Используйте GetUriWithQueryParameter для добавления, изменения или удаления одного или нескольких параметров запроса для текущего URL-адреса:

@inject NavigationManager Navigation

...

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

В предыдущем примере:

  • Заполнитель {NAME} указывает имя параметра запроса. Заполнитель {VALUE} указывает значение в качестве поддерживаемого типа. Поддерживаемые типы перечислены далее в этом разделе.
  • Строка возвращается, равная текущему URL-адресу с одним параметром:
    • Добавляется, если имя параметра запроса не существует в текущем URL-адресе.
    • Изменено на значение, указываемое в том случае, если параметр запроса существует в текущем URL-адресе.
    • Удаляется, если тип предоставленного значения допускает значение NULL, а значение равно null.
  • Корректное форматирование для инвариантных языка и региональных параметров применяется для данного типа (CultureInfo.InvariantCulture).
  • Имя и значение параметра запроса кодируются в виде URL-адреса.
  • Все значения с соответствующим именем параметра запроса заменяются при наличии нескольких экземпляров типа.

Вызовите GetUriWithQueryParameters для создания URI, созданного из Uri, с добавлением, обновлением или удалением нескольких параметров. Для каждого значения платформа использует value?.GetType() для определения времени выполнения запроса и выбирает правильный инвариантный формат для культуры. Платформа выдает ошибку для неподдерживаемых типов.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters({PARAMETERS})

Заполнитель {PARAMETERS} — это IReadOnlyDictionary<string, object>.

Передайте строку URI в GetUriWithQueryParameters, чтобы создать новый URI из предоставленного URI с несколькими добавленными, обновленными или удаленными параметрами. Для каждого значения платформа использует value?.GetType() для определения типа времени выполнения для каждого параметра запроса и выбирает правильное языково-независимое форматирование. Платформа выдает ошибку для неподдерживаемых типов. Поддерживаемые типы перечислены далее в этом разделе.

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
  • Заполнитель {URI} — это URI с параметрами запроса или без них.
  • Заполнитель {PARAMETERS} является IReadOnlyDictionary<string, object>.

Поддерживаемые типы идентичны поддерживаемым типам для ограничений маршрута:

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

К поддерживаемым типам относятся:

  • Варианты, допускающие значение NULL, для перечисленных выше типов.
  • Массивы предыдущих типов, допускающие или не допускающие значение NULL.

Предупреждение

С помощью сжатия, который включен по умолчанию, избегайте создания безопасных (прошедших проверку подлинности или авторизованных) интерактивных компонентов на стороне сервера, отрисовывающих данные из ненадежных источников. Ненадежные источники включают параметры маршрута, строки запроса, данные из JS взаимодействия и любой другой источник данных, которые сторонний пользователь может контролировать (базы данных, внешние службы). Для получения дополнительной информации см. руководство по ASP.NET Core и руководство по снижению угроз для интерактивного серверного рендеринга в ASP.NET Core.

Замена значения параметра запроса, если параметр существует

Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
Текущий URL-адрес Созданный URL-адрес
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

Добавление параметра запроса и значения, если параметр не существует

Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
Текущий URL-адрес Созданный URL-адрес
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

Удаление параметра запроса, если значение параметра — null

Navigation.GetUriWithQueryParameter("full name", (string)null)
Текущий URL-адрес Созданный URL-адрес
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/

Добавление, обновление и удаление параметров запроса

В следующем примере :

  • name удаляется при наличии.
  • age добавляется со значением 25 (int), если отсутствует. При наличии age обновляется и получает значение 25.
  • eye color добавляется или обновляется до значения green.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["name"] = null,
        ["age"] = (int?)25,
        ["eye color"] = "green"
    })
Текущий URL-адрес Созданный URL-адрес
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

Поддержка перечислимых значений

В следующем примере :

  • full name добавляется или обновляется до Morena Baccarin, одного значения.
  • Параметры ping добавляются или заменяются параметрами 35, 16, 87 и 240.
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["full name"] = "Morena Baccarin",
        ["ping"] = new int?[] { 35, 16, null, 87, 240 }
    })
Текущий URL-адрес Созданный URL-адрес
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

Для перехода с помощью добавленной или измененной строки запроса передайте созданный URL-адрес в NavigateTo.

В следующем примере вызывается:

  • GetUriWithQueryParameter для добавления или замены параметра запроса name с помощью значения Morena Baccarin.
  • Вызывает NavigateTo, чтобы активировать навигацию по новому URL-адресу.
Navigation.NavigateTo(
    Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));

Строка запроса получается из свойства NavigationManager.Uri.

@inject NavigationManager Navigation

...

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

Для анализа параметров строки запроса можно воспользоваться URLSearchParams в сочетании со взаимодействием JavaScript (JS):

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

Дополнительные сведения об изоляции JavaScript с модулями JavaScript см. в статье Вызов функций JavaScript из методов .NET в ASP.NET Core Blazor.

Хэшированная маршрутизация в именованные элементы

Перейдите к именованному элементу с использованием следующих подходов с хэш-ссылкой на элемент #. Маршруты к элементам в компоненте и маршруты к элементам во внешних компонентах используют корневой относительный путь. Начальная косая черта (/) является необязательной.

Примеры для каждого подхода демонстрируют навигацию к элементу с id и targetElement в компоненте Counter.

  • Якорный элемент (<a>) с href:

    <a href="/counter#targetElement">
    
  • NavLink компонент с параметром href:

    <NavLink href="/counter#targetElement">
    
  • NavigationManager.NavigateTo передача относительного URL-адреса:

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

В следующем примере показано использование хэш-маршрутизации для перехода к именованным заголовкам H2 внутри компонента и ко внешним компонентам.

В компонентах Home (Home.razor) и Counter (Counter.razor) поместите следующую разметку в нижней части существующей разметки компонента, чтобы использовать их в качестве целей навигации. <div> создает искусственное вертикальное пространство для демонстрации поведения прокрутки браузера.

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

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

Добавьте следующий HashedRouting компонент в приложение.

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

Взаимодействие пользователей с содержимым <Navigating>

Если во время навигации наблюдается значительная задержка, например при отложенной загрузке сборок в Blazor WebAssembly приложении или медленном сетевом подключении к Blazor серверному приложению, компонент может указать пользователю, Router что переход страницы происходит.

В верхней части компонента, определяющего Router компонент, добавьте директиву @using для пространства имен Microsoft.AspNetCore.Components.Routing.

@using Microsoft.AspNetCore.Components.Routing

Предоставьте содержимое параметру Navigating для отображения во время событий перехода страницы.

Содержимое элемента маршрутизатора (<Router>...</Router>):

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

Пример, в котором используется свойство Navigating, см. Сборки с отложенной загрузкой в ASP.NET Core Blazor WebAssembly.

Обрабатывайте события асинхронной навигации с использованием OnNavigateAsync

Компонент Router поддерживает функцию OnNavigateAsync. Обработчик OnNavigateAsync вызывается, когда пользователь выполняет следующие действия:

  • попадает на маршрут в первый раз, перейдя к нему непосредственно в браузере;
  • переходит на новый маршрут с помощью ссылки или вызова 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)
    {
        ...
    }
}

Пример, в котором используется OnNavigateAsync, см. Сборки с отложенной загрузкой в ASP.NET Core Blazor WebAssembly.

При предварительной подготовке на сервере OnNavigateAsync выполняется дважды:

  • Один раз, когда запрошенный компонент конечной точки впервые отрисовывается статически.
  • Когда браузер во второй раз отрисовывает компонент конечной точки.

Чтобы предотвратить повторное выполнение кода разработчика в OnNavigateAsync, компонент Routes может сохранять NavigationContext для использования в методе OnAfterRender{Async} жизненного цикла, где можно проверить firstRender. Для получения дополнительной информации ознакомьтесь с "Предварительной отрисовкой с помощью взаимодействия JavaScript".

Чтобы код разработчика в OnNavigateAsync не выполнялся дважды, компонент App может хранить NavigationContext для использования в OnAfterRender{Async}, где можно проверить firstRender. Дополнительные сведения см. в разделе «Предварительная отрисовка с помощью взаимодействия JavaScript».

Обработка отмены в OnNavigateAsync

Объект NavigationContext, переданный в обратный вызов OnNavigateAsync, содержит CancellationToken, который задается при возникновении нового события навигации. Обратный вызов OnNavigateAsync должен вызываться, если этот токен отмены установлен так, чтобы не выполнять обратный вызов OnNavigateAsync для устаревшей навигации.

Если пользователь переходит на конечную точку, а затем сразу переходит к новой конечной точке, приложение не должно продолжать выполнение обратного вызова OnNavigateAsync к первой конечной точке.

В следующем примере :

  • Токен отмены передается в функцию, вызываемую через PostAsJsonAsync, которая может отменить запрос POST, если пользователь покидает данную конечную точку /about.
  • Токен отмены задается во время операции предварительной выборки продукта, если пользователь покидает конечную точку /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);
            }
        }
    }
}

Примечание.

Если не выбрасывать исключение при отмене токена отмены в NavigationContext, это может привести к непредсказуемому поведению, например, к рендерингу компонента из предыдущей навигации.

Управление и предотвращение изменений местоположения

RegisterLocationChangingHandler: регистрирует обработчик для обработки входящих событий навигации. Контекст обработчика, предоставляемый LocationChangingContext, включает следующие свойства:

  • TargetLocation: возвращает конечное расположение.
  • HistoryEntryState: возвращает состояние, связанное с записью в целевой журнал.
  • IsNavigationIntercepted: возвращает значение, указывающее, была ли перехвачена навигация из ссылки.
  • CancellationToken: возвращает значение CancellationToken, определяющее, была ли отменена навигация, например, чтобы определить, активировал ли пользователь другую навигацию.
  • PreventNavigation: вызывается, чтобы предотвратить продолжение навигации.

Компонент может зарегистрировать несколько обработчиков изменения местоположения в методе жизненного цикла. Навигация вызывает все обработчики изменения расположения, зарегистрированные во всем приложении (между несколькими компонентами), и любая внутренняя навигация выполняет их одновременно. В дополнение к NavigateTo обработчики вызываются в следующих случаях:

  • При нажатии на внутренние ссылки, которые указывают на URL-адреса в базовом пути приложения.
  • При навигации с помощью кнопок "Вперед" и "Назад" в браузере.

Обработчики выполняются только для внутренней навигации в приложении. Если пользователь нажимает на ссылку, которая ведет на другой сайт, или вручную вводит в адресную строку адрес другого сайта, обработчики изменения расположения не выполняются.

Реализуйте IDisposable и удалите зарегистрированные обработчики, чтобы отменить их регистрацию. Дополнительные сведения см. в разделе ASP.NET Core Razor утилизации компонентов.

Внимание

Не пытайтесь выполнять задачи очистки DOM с помощью взаимодействия JavaScript (JS) при обработке изменений местоположения. MutationObserver Используйте шаблон в JS клиенте. Дополнительные сведения см. в разделе Взаимодействие JavaScript с Blazor ASP.NET Core (интероперабельность JS).

В следующем примере обработчик изменения расположения регистрируется для событий навигации.

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

Так как внутренняя навигация может быть отменена асинхронно, может возникнуть несколько перекрывающихся вызовов зарегистрированных обработчиков. Например, несколько вызовов обработчика могут возникать, когда пользователь быстро нажимает кнопку "Назад" на странице или когда пользователь выбирает несколько ссылок перед выполнением навигации. Ниже приведена сводка по логике асинхронной навигации:

  • Если регистрируются какие-либо обработчики изменения местоположения, вся навигация изначально отменяется, а затем воспроизводится, если навигация не отменена.
  • Если выполняются перекрывающиеся запросы навигации, последний запрос всегда отменяет предыдущие запросы, что означает следующее:
    • Приложение может рассматривать несколько нажатий кнопки "Назад" и "Вперед" как одно.
    • Если пользователь выбирает несколько ссылок до завершения процесса навигации, последняя выбранная ссылка определяет навигацию.

Дополнительные сведения о передаче NavigationOptions в NavigateTo для управления записями и состоянием стека журнала навигации см. в разделе Варианты навигации.

Для получения дополнительных примеров кода см. NavigationManagerComponent в BasicTestApp (справочный источник dotnet/aspnetcore).

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент NavigationLock перехватывает события навигации до тех пор, пока он отображается, фактически "блокирует" любую заданную навигацию до тех пор, пока не будет принято решение о продолжении или отмене. Используйте NavigationLock при перехвате навигации в течение времени существования компонента.

Параметры NavigationLock:

  • ConfirmExternalNavigation задает диалоговое окно браузера, чтобы предложить пользователю подтвердить или отменить внешнюю навигацию. Значение по умолчанию — false. Для отображения диалогового окна подтверждения требуется начальное взаимодействие пользователя со страницей перед запуском внешней навигации по URL-адресу в адресной строке браузера. Дополнительные сведения о требованиях взаимодействия см. в разделе Окно: событие beforeunload (документация по MDN).
  • OnBeforeInternalNavigation задает обратный вызов для внутренних событий навигации.

В следующем компоненте NavLock:

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

Для дополнительных примеров кода см. компонент ConfigurableNavigationLock в BasicTestApp (справочный источник dotnet/aspnetcore).

Используйте при создании ссылок навигации компонент NavLink вместо HTML-элементов гиперссылок (<a>). Компонент NavLink ведет себя как элемент <a>, за исключением того, что он переключает класс CSS active в зависимости от того, соответствует ли его href текущему URL-адресу. Класс active помогает пользователю понять, какая страница является активной страницей среди отображаемых ссылок навигации. При необходимости назначьте имя класса CSS свойству NavLink.ActiveClass, чтобы применить пользовательский класс CSS к отображаемой ссылке, если текущий маршрут совпадает с href.

Существует два параметра NavLinkMatch, которые можно назначить атрибуту Match элемента <NavLink>:

  • NavLinkMatch.All: NavLink активна при совпадении с текущим URL-адресом, игнорируя строку запроса и фрагмент. Чтобы включить сопоставление в строку запроса или фрагмент, используйте параметр Microsoft.AspNetCore.Components.Routing.NavLink.DisableMatchAllIgnoresLeftUriPartAppContext.
  • NavLinkMatch.Prefix (по умолчанию) NavLink активен, если он соответствует любому префиксу текущего URL-адреса.

Существует два параметра NavLinkMatch, которые можно назначить атрибуту Match элемента <NavLink>:

  • NavLinkMatch.All: NavLink активна при совпадении всего текущего URL-адреса, включая строку запроса и фрагмент.
  • NavLinkMatch.Prefix (по умолчанию) NavLink активен, если он соответствует любому префиксу текущего URL-адреса.

В предыдущем примере HomeNavLinkhref="" совпадает с домашним URL-адресом и получает только класс CSS active по базовому пути приложения по умолчанию (/). Второй NavLink получает класс active, когда пользователь посещает любой URL-адрес с префиксом component (например, /component и /component/another-segment).

Для применения пользовательской логики сопоставления создайте подкласс NavLink и переопределите его метод ShouldMatch. Верните true из метода, если вы хотите применить класс CSS active:

public class CustomNavLink : NavLink
{
    protected override bool ShouldMatch(string currentUriAbsolute)
    {
        // Custom matching logic
    }
}

Дополнительные атрибуты компонента NavLink передаются в отображаемый тег привязки. В следующем примере компонент NavLink включает атрибут target.

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

Отобразится следующая разметка HTML.

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

Предупреждение

В связи с тем, как Blazor выполняет рендеринг дочернего содержимого, для рендеринга компонентов NavLink в цикле for требуется задать локальную переменную индекса, если в содержимом дочернего компонента NavLink используется переменная цикла приращения:

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

Использование переменной индекса в этом сценарии обязательно для любого дочернего компонента, который использует переменную цикла в своем дочернем содержимом, а не только для компонента NavLink.

Вместо этого можно использовать цикл foreach с Enumerable.Range:

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

NavLink Записи компонентов можно динамически создавать из компонентов приложения с помощью отражения. В следующем примере показан общий подход к дальнейшей настройке.

Для следующей демонстрации для компонентов приложения используется согласованное стандартное соглашение об именовании:

  • Имена файлов маршрутизируемых компонентов используют регистр Pascal†, например Pages/ProductDetail.razor.
  • Пути к файлам маршрутизируемого компонента соответствуют их URL-адресам в формате 'kebab case', с дефисами, разделяющими слова в шаблоне маршрута компонента. Например, компонент ProductDetail с шаблоном маршрута /product-detail (@page "/product-detail") запрашивается в браузере по относительному URL-адресу /product-detail.

†Регистр Pascal (верхний горбатый регистр) — это соглашение об именовании без пробелов и знаков препинания, где все слова, включая первое, пишутся с прописной буквы.
'Kebab case — это соглашение об именовании без пробелов и препинания, которое использует строчные буквы и дефисы между словами.

Razor В разметке NavMenu компонента (NavMenu.razor) на странице Home по умолчанию NavLink компоненты добавляются из коллекции:

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

Метод GetRoutableComponents в блоке @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);

Приведенный выше пример не содержит следующие страницы в отрисованном списке компонентов:

  • Home страница: Эта страница перечислена отдельно от автоматически сгенерированных ссылок, поскольку она должна находиться в верхней части списка и устанавливать параметр Match.
  • Error страница ошибок: страница ошибок открывается только фреймворком и не должна быть указана.

Для примера приведенного выше кода в приложении, которое можно запустить локально, получите Blazor Web App или Blazor WebAssembly образец приложения.

Интеграция маршрутизации конечных точек ASP.NET Core

Этот раздел относится к Blazor Web Appсистемам, работающим через цепь.

Этот раздел относится к приложениям Blazor Server.

A Blazor Web App интегрируется в маршрутизацию конечных точек ASP.NET Core. Приложение ASP.NET Core настроено на прием входящих подключений для интерактивных компонентов с MapRazorComponents в файле Program. Корневой компонент по умолчанию (первый загруженный компонент) — это App компонент (App.razor):

app.MapRazorComponents<App>();

Blazor Server интегрирован с функцией маршрутизации конечных точек ASP.NET Core. Приложение ASP.NET Core настроено для приема входящих подключений для интерактивных компонентов через MapBlazorHub в файле Program.

app.UseRouting();

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

Blazor Server интегрирован с функцией маршрутизации конечных точек ASP.NET Core. Приложение ASP.NET Core настроено для приема входящих подключений для интерактивных компонентов с помощью MapBlazorHub в Startup.Configure.

Типичная конфигурация — маршрутизация всех запросов на страницу Razor, которая выступает в качестве узла для серверной части приложения Blazor Server. По соглашению страница узла обычно называется _Host.cshtml и находится в папке Pages приложения.

Маршрут, указанный в файле узла, называется резервным маршрутом, так как он работает с низким приоритетом в соответствии с правилами маршрутизации. Резервный маршрут используется, когда другие маршруты не сопоставляются. Это позволяет приложению использовать другие контроллеры и страницы, не мешая маршрутизации компонента в приложении Blazor Server.

Сведения о настройке MapFallbackToPage для размещения сервера на некорневом URL-адресе см. в документе Хостинг и развертывание ASP.NET Core Blazor.