ASP.NET Core 中的 Razor Pages 路由和应用约定

了解如何使用页面路由和应用模型提供程序约定来控制 Razor Pages 应用中的页面路由、发现和处理。

若要指定页面路由、添加路由段或向路由添加参数,请使用页面的 @page 指令。 有关详细信息,请参阅自定义路由

有些保留字不能用作路由段或参数名称。 有关详细信息,请参阅路由:保留的路由名称

查看或下载示例代码如何下载

方案 本示例演示
模型约定

Conventions.Add
将路由模板和标头添加到应用的页面。
页面路由操作约定 将路由模板添加到某个文件夹中的页面以及单个页面。
页面模型操作约定 将标头添加到某个文件夹中的多个页面,将标头添加到单个页面,以及配置筛选器工厂以将标头添加到应用的页面。

Razor Pages 约定是使用配置 RazorPagesOptionsAddRazorPages 重载配置的。 本主题稍后会介绍以下约定示例:


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages(options =>
    {
        options.Conventions.Add( ... );
        options.Conventions.AddFolderRouteModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageRouteModelConvention(
            "/About", model => { ... });
        options.Conventions.AddPageRoute(
            "/Contact", "TheContactPage/{text?}");
        options.Conventions.AddFolderApplicationModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageApplicationModelConvention(
            "/About", model => { ... });
        options.Conventions.ConfigureFilter(model => { ... });
        options.Conventions.ConfigureFilter( ... );
    });
}

路由顺序

路由会为进行处理指定一个 Order(路由匹配)。

路由顺序 行为
-1 在处理其他路由之前处理该路由。
0 未指定顺序(默认值)。 不分配 Order (Order = null) 会将路由 Order 默认为 0(零)以进行处理。
1、2 … n 指定路由处理顺序。

按约定建立路由处理:

  • 按顺序(-1、0、1、2、… n)处理路由。
  • 当路由具有相同 Order 时,首先匹配最具体的路由,然后匹配不太具体的路由。
  • 当具有相同 Order 和相同数量参数的路由与请求 URL 匹配时,会按添加到 PageConventionCollection 的顺序处理路由。

如果可能,请避免依赖于建立的路由处理顺序。 通常,路由会通过 URL 匹配选择正确路由。 如果必须设置路由 Order 属性以便正确路由请求,则应用的路由方案可能会使客户端感到困惑并且难以维护。 应设法简化应用的路由方案。 示例应用需要显式路由处理顺序以使用单个应用来演示几个路由方案。 但是,在生产应用中应尝试避免设置路由 Order 的做法。

Razor Pages 路由和 MVC 控制器路由共享一个实现。 有关 MVC 主题中的路由顺序的信息可在以下位置获得:路由到控制器操作:对属性路由排序

模型约定

IPageConvention 添加委托,以添加应用于 Razor Pages 的模型约定

将路由模型约定添加到所有页面

使用 Conventions 创建 IPageRouteModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面路由模型构造过程中应用。

示例应用包含 GlobalTemplatePageRouteModelConvention 类,用于将 {globalTemplate?} 路由模板添加到应用中的所有页面:

using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace SampleApp.Conventions;

public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel!.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

在上述代码中:

将 Razor Pages 添加到服务集合时,会添加 Razor Pages 选项,例如添加 Conventions。 有关示例,请参阅示例应用

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.EntityFrameworkCore;
using SampleApp.Conventions;
using SampleApp.Data;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
                                   options.UseInMemoryDatabase("InMemoryDb"));

builder.Services.AddRazorPages(options =>
   {
       options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

       options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
       {
           var selectorCount = model.Selectors.Count;
           for (var i = 0; i < selectorCount; i++)
           {
               var selector = model.Selectors[i];
               model.Selectors.Add(new SelectorModel
               {
                   AttributeRouteModel = new AttributeRouteModel
                   {
                       Order = 2,
                       Template = AttributeRouteModel.CombineTemplates(
                           selector.AttributeRouteModel!.Template,
                           "{otherPagesTemplate?}"),
                   }
               });
           }
       });

       options.Conventions.AddPageRouteModelConvention("/About", model =>
       {
           var selectorCount = model.Selectors.Count;
           for (var i = 0; i < selectorCount; i++)
           {
               var selector = model.Selectors[i];
               model.Selectors.Add(new SelectorModel
               {
                   AttributeRouteModel = new AttributeRouteModel
                   {
                       Order = 2,
                       Template = AttributeRouteModel.CombineTemplates(
                           selector.AttributeRouteModel!.Template,
                           "{aboutTemplate?}"),
                   }
               });
           }
       });

   });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapRazorPages();
app.Run();

请考虑使用 GlobalTemplatePageRouteModelConvention 类:

using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace SampleApp.Conventions;

public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel!.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder 属性设置为 1。 这可确保在示例应用中实现以下路由匹配行为:

  • 本主题后面会添加 TheContactPage/{text?} 的路由模板。 Contact Page 路由具有默认顺序 null (Order = 0),因此它在具有 Order = 1{globalTemplate?} 路由模板之前进行匹配。

  • {aboutTemplate?} 路由模板显示在前面的代码中。 为 {aboutTemplate?} 模板指定的 Order2。 当在 /About/RouteDataValue 中请求“关于”页面时,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。

  • {otherPagesTemplate?} 路由模板显示在前面的代码中。 为 {otherPagesTemplate?} 模板指定的 Order2。 当使用路由参数请求 Pages/OtherPages 文件夹中的任何页面:

  • 例如: /OtherPages/Page1/xyz

  • 路由数据值 "xyz" 加载到 RouteData.Values["globalTemplate"] (Order = 1)。

  • 由于 Order 属性 2 的值较高,未加载具有 (Order = 2) 的 RouteData.Values["otherPagesTemplate"]

如果可能,请不要设置 Order。 未设置 Order 时,默认为 Order = 0。 依赖于路由来选择正确的路由,而不是 Order 属性。

localhost:{port}/About/GlobalRouteValue 中请求示例的 About 页面并检查结果:

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

示例应用使用 Rick.Docs.Samples.RouteInfo NuGet 包在日志记录输出中显示路由信息。 使用 localhost:{port}/About/GlobalRouteValue 时,记录器会显示请求、Order 和使用的模板:

info: SampleApp.Pages.AboutModel[0]
       /About/GlobalRouteValue   Order = 1 Template = About/{globalTemplate?}

将应用模型约定添加到所有页面

使用 Conventions 创建 IPageApplicationModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面应用模型构造过程中应用。

为了演示此约定以及本主题后面的其他约定,示例应用包含了一个 AddHeaderAttribute 类。 类构造函数采用 name 字符串和 values 字符串数组。 将在其 OnResultExecuting 方法中使用这些值来设置响应标头。 本主题后面的页面模型操作约定部分展示了完整的类。

示例应用使用 AddHeaderAttribute 类将标头 GlobalHeader 添加到应用中的所有页面:

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseInMemoryDatabase("InMemoryDb"));

builder.Services.AddRazorPages(options =>
   {
       options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

       options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:{port}/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the GlobalHeader has been added.

将处理程序模型约定添加到所有页面

使用 Conventions 创建 IPageHandlerModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面处理程序模型构造过程中应用。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

页面路由操作约定

默认路由模型提供程序派生自 IPageRouteModelProvider,可调用旨在为页面路由配置提供扩展点的约定。

文件夹路由模型约定

使用 AddFolderRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于指定文件夹下的所有页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddFolderRouteModelConvention{otherPagesTemplate?} 路由模板添加到 OtherPages 文件夹中的页面:

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel!.Template,
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果使用路由参数值请求 Pages/OtherPages 文件夹中的任何页面(例如,/OtherPages/Page1/RouteDataValue)时,由于设置了 Order 属性,因此“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 中,而不是 RouteData.Values["otherPagesTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue 中请求示例的 Page1 页面并检查结果:

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

页面路由模型约定

使用 AddPageRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于具有指定名称的页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddPageRouteModelConvention{aboutTemplate?} 路由模板添加到“关于”页面:

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel!.Template,
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果在 /About/RouteDataValue 中使用路由参数值请求“关于”页面,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:{port}/About/GlobalRouteValue/AboutRouteValue 中请求示例的“关于”页面并检查结果:

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

记录器输出显示:

info: SampleApp.Pages.AboutModel[0]
       /About/GlobalRouteValue/AboutRouteValue   Order = 2 Template = About/{globalTemplate?}/{aboutTemplate?}

使用参数转换程序自定义页面路由

请参阅参数转换器

配置页面路由

使用 AddPageRoute 配置路由,该路由指向指定页面路径中的页面。 生成的页面链接使用指定的路由。 AddPageRoute 使用 AddPageRouteModelConvention 建立路由。

示例应用为 ContactRazor 页面创建指向 /TheContactPage 的路由:

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

还可在 /Contact1` 中通过其默认路由访问 Contact 页面。

示例应用的 Contact 页面自定义路由允许使用可选的 text 路由段 ({text?})。 该页面还在其 @page 指令中包含此可选段,以便访问者在 /Contact 路由中访问该页面:

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

请注意,在呈现的页面中,为联系人链接生成的 URL 反映了已更新的路由:

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

在常规路由 /Contact 或自定义路由 /TheContactPage 中访问 Contact 页面。 如果提供附加的 text 路由段,该页面会显示所提供的 HTML 编码段:

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

页面模型操作约定

实现 IPageApplicationModelProvider 的默认页面模型提供程序可调用约定,这些约定旨在为页面模型配置提供扩展点。 在生成和修改页面发现及处理方案时,可使用这些约定。

对于此部分中的示例,示例应用使用 AddHeaderAttribute 类(一个 ResultFilterAttribute)来应用响应标头:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

示例演示了如何使用约定将该属性应用于某个文件夹中的所有页面以及单个页面。

文件夹应用模型约定

使用 AddFolderApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于指定文件夹下的所有页面,会在 PageApplicationModel 实例上调用操作。

示例演示了如何使用 AddFolderApplicationModelConvention 将标头 OtherPagesHeader 添加到应用的 OtherPages 文件夹内的页面:

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 中请求示例的 Page1 页面,并检查标头以查看结果:

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

页面应用模型约定

使用 AddPageApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于具有指定名称的页面,会在 PageApplicationModel 上调用操作。

示例演示了如何使用 AddPageApplicationModelConvention 将标头 AboutHeader 添加到“关于”页面:

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the AboutHeader has been added.

配置筛选器

ConfigureFilter 可配置要应用的指定筛选器。 用户可以实现筛选器类,但示例应用演示了如何在 Lambda 表达式中实现筛选器,该筛选器在后台作为可返回筛选器的工厂实现:

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header",
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

页面应用模型用于检查指向 OtherPages 文件夹中 Page2 页面的段的相对路径。 如果条件通过,则添加标头。 如果不通过,则应用 EmptyFilter

EmptyFilter 是一种操作筛选器。 由于 Razor Pages 会忽略操作筛选器,因此,如果路径不包含 OtherPages/Page2EmptyFilter 应该无效。

localhost:5000/OtherPages/Page2 中请求示例的 Page2 页面,并检查标头以查看结果:

The OtherPagesPage2Header is added to the response for Page2.

配置筛选器工厂

ConfigureFilter 可配置指定的工厂,以将筛选器应用于所有 Razor Pages。

示例应用提供了一个示例,说明如何使用筛选器工厂将具有两个值的标头 FilterFactoryHeader 添加到应用的页面:

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC 筛选器和页面筛选器 (IPageFilter)

Razor Pages 会忽略 MVC 操作筛选器,因为 Razor Pages 使用处理程序方法。 可以使用其他类型的 MVC 筛选器:授权异常资源结果。 有关详细信息,请参阅筛选器主题。

页面筛选器 (IPageFilter) 是应用于 Razor Pages 的一种筛选器。 有关详细信息,请参阅 Razor Pages 的筛选方法

其他资源

了解如何使用页面路由和应用模型提供程序约定来控制 Razor Pages 应用中的页面路由、发现和处理。

需要为各个页面配置自定义页面路由时,可使用本主题稍后所述的 AddPageRoute 约定配置页面路由。

若要指定页面路由、添加路由段或向路由添加参数,请使用页面的 @page 指令。 有关详细信息,请参阅自定义路由

有些保留字不能用作路由段或参数名称。 有关详细信息,请参阅路由:保留的路由名称

查看或下载示例代码如何下载

方案 示例演示...
模型约定

Conventions.Add
  • IPageRouteModelConvention
  • IPageApplicationModelConvention
  • IPageHandlerModelConvention
将路由模板和标头添加到应用的页面。
页面路由操作约定
  • AddFolderRouteModelConvention
  • AddPageRouteModelConvention
  • AddPageRoute
将路由模板添加到某个文件夹中的页面以及单个页面。
页面模型操作约定
  • AddFolderApplicationModelConvention
  • AddPageApplicationModelConvention
  • ConfigureFilter(筛选器类、Lambda 表达式或筛选器工厂)
将标头添加到某个文件夹中的多个页面,将标头添加到单个页面,以及配置筛选器工厂以将标头添加到应用的页面。

Razor Pages 约定是使用在 Startup.ConfigureServices 中配置 RazorPagesOptionsAddRazorPages 重载配置的。 本主题稍后会介绍以下约定示例:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.Add( ... );
        options.Conventions.AddFolderRouteModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageRouteModelConvention(
            "/About", model => { ... });
        options.Conventions.AddPageRoute(
            "/Contact", "TheContactPage/{text?}");
        options.Conventions.AddFolderApplicationModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageApplicationModelConvention(
            "/About", model => { ... });
        options.Conventions.ConfigureFilter(model => { ... });
        options.Conventions.ConfigureFilter( ... );
    });
}

路由顺序

路由会为进行处理指定一个 Order(路由匹配)。

顺序 行为
-1 在处理其他路由之前处理该路由。
0 未指定顺序(默认值)。 不分配 Order (Order = null) 会将路由 Order 默认为 0(零)以进行处理。
1、2 … n 指定路由处理顺序。

按约定建立路由处理:

  • 按顺序(-1、0、1、2、… n)处理路由。
  • 当路由具有相同 Order 时,首先匹配最具体的路由,然后匹配不太具体的路由。
  • 当具有相同 Order 和相同数量参数的路由与请求 URL 匹配时,会按添加到 PageConventionCollection 的顺序处理路由。

如果可能,请避免依赖于建立的路由处理顺序。 通常,路由会通过 URL 匹配选择正确路由。 如果必须设置路由 Order 属性以便正确路由请求,则应用的路由方案可能会使客户端感到困惑并且难以维护。 应设法简化应用的路由方案。 示例应用需要显式路由处理顺序以使用单个应用来演示几个路由方案。 但是,在生产应用中应尝试避免设置路由 Order 的做法。

Razor Pages 路由和 MVC 控制器路由共享一个实现。 有关 MVC 主题中的路由顺序的信息可在以下位置获得:路由到控制器操作:对属性路由排序

模型约定

IPageConvention 添加委托,以添加应用于 Razor Pages 的模型约定

将路由模型约定添加到所有页面

使用 Conventions 创建 IPageRouteModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面路由模型构造过程中应用。

示例应用将 {globalTemplate?} 路由模板添加到应用中的所有页面:

public class GlobalTemplatePageRouteModelConvention 
    : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder 属性设置为 1。 这可确保在示例应用中实现以下路由匹配行为:

  • 本主题后面会添加 TheContactPage/{text?} 的路由模板。 “联系人”页面路由具有默认顺序 null (Order = 0),因此它在 {globalTemplate?} 路由模板之前进行匹配。
  • 本主题后面会添加 {aboutTemplate?} 路由模板。 为 {aboutTemplate?} 模板指定的 Order2。 当在 /About/RouteDataValue 中请求“关于”页面时,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。
  • 本主题后面会添加 {otherPagesTemplate?} 路由模板。 为 {otherPagesTemplate?} 模板指定的 Order2。 当使用路由参数请求 Pages/OtherPages 文件夹中的任何页面(例如,/OtherPages/Page1/RouteDataValue)时,由于设置了 Order 属性,因此“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 中,而不是 RouteData.Values["otherPagesTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

将 Razor Pages 添加到 Startup.ConfigureServices 中的服务集合时,会添加 Razor Pages 选项,例如添加 Conventions。 有关示例,请参阅示例应用

options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

localhost:5000/About/GlobalRouteValue 中请求示例的“关于”页面并检查结果:

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

将应用模型约定添加到所有页面

使用 Conventions 创建 IPageApplicationModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面应用模型构造过程中应用。

为了演示此约定以及本主题后面的其他约定,示例应用包含了一个 AddHeaderAttribute 类。 类构造函数采用 name 字符串和 values 字符串数组。 将在其 OnResultExecuting 方法中使用这些值来设置响应标头。 本主题后面的页面模型操作约定部分展示了完整的类。

示例应用使用 AddHeaderAttribute 类将标头 GlobalHeader 添加到应用中的所有页面:

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Startup.cs

options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the GlobalHeader has been added.

将处理程序模型约定添加到所有页面

使用 Conventions 创建 IPageHandlerModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面处理程序模型构造过程中应用。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

Startup.cs

options.Conventions.Add(new GlobalPageHandlerModelConvention());

页面路由操作约定

默认路由模型提供程序派生自 IPageRouteModelProvider,可调用旨在为页面路由配置提供扩展点的约定。

文件夹路由模型约定

使用 AddFolderRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于指定文件夹下的所有页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddFolderRouteModelConvention{otherPagesTemplate?} 路由模板添加到 OtherPages 文件夹中的页面:

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果使用路由参数值请求 Pages/OtherPages 文件夹中的任何页面(例如,/OtherPages/Page1/RouteDataValue)时,由于设置了 Order 属性,因此“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 中,而不是 RouteData.Values["otherPagesTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue 中请求示例的 Page1 页面并检查结果:

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

页面路由模型约定

使用 AddPageRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于具有指定名称的页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddPageRouteModelConvention{aboutTemplate?} 路由模板添加到“关于”页面:

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果在 /About/RouteDataValue 中使用路由参数值请求“关于”页面,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:5000/About/GlobalRouteValue/AboutRouteValue 中请求示例的“关于”页面并检查结果:

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

使用参数转换程序自定义页面路由

可以使用参数转换程序自定义 ASP.NET Core 生成的页面路由。 参数转换程序实现 IOutboundParameterTransformer 并转换参数值。 例如,一个自定义 SlugifyParameterTransformer 参数转换程序可将 SubscriptionManagement 路由值更改为 subscription-management

PageRouteTransformerConvention 页面路由模型约定将参数转换程序应用于应用中自动生成的页面路由的文件夹和文件名段。 例如,/Pages/SubscriptionManagement/ViewAll.cshtml 处的 Razor Pages 文件会将其路由从 /SubscriptionManagement/ViewAll 重写为 /subscription-management/view-all

PageRouteTransformerConvention 仅转换来自 Razor Pages 文件夹和文件名的自动生成页面路由段。 它不会转换使用 @page 指令添加的路由段。 该约定也不会转换 AddPageRoute 添加的路由。

PageRouteTransformerConventionStartup.ConfigureServices 中注册为选项:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.Add(
            new PageRouteTransformerConvention(
                new SlugifyParameterTransformer()));
    });
}
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString(),
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

警告

如果使用 System.Text.RegularExpressions 处理不受信任的输入,则传递一个超时。 恶意用户可能会向 RegularExpressions 提供输入,从而导致拒绝服务攻击。 使用 RegularExpressions 的 ASP.NET Core 框架 API 会传递一个超时。

配置页面路由

使用 AddPageRoute 配置路由,该路由指向指定页面路径中的页面。 生成的页面链接使用指定的路由。 AddPageRoute 使用 AddPageRouteModelConvention 建立路由。

示例应用为 Contact.cshtml 创建指向 /TheContactPage 的路由:

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

还可在 /Contact 中通过默认路由访问“联系人”页面。

示例应用的“联系人”页面自定义路由允许使用可选的 text 路由段 ({text?})。 该页面还在其 @page 指令中包含此可选段,以便访问者在 /Contact 路由中访问该页面:

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

请注意,在呈现的页面中,为联系人链接生成的 URL 反映了已更新的路由:

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

在常规路由 /Contact 或自定义路由 /TheContactPage 中访问“联系人”页面。 如果提供附加的 text 路由段,该页面会显示所提供的 HTML 编码段:

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

页面模型操作约定

实现 IPageApplicationModelProvider 的默认页面模型提供程序可调用约定,这些约定旨在为页面模型配置提供扩展点。 在生成和修改页面发现及处理方案时,可使用这些约定。

对于此部分中的示例,示例应用使用 AddHeaderAttribute 类(一个 ResultFilterAttribute)来应用响应标头:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

示例演示了如何使用约定将该属性应用于某个文件夹中的所有页面以及单个页面。

文件夹应用模型约定

使用 AddFolderApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于指定文件夹下的所有页面,会在 PageApplicationModel 实例上调用操作。

示例演示了如何使用 AddFolderApplicationModelConvention 将标头 OtherPagesHeader 添加到应用的 OtherPages 文件夹内的页面:

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 中请求示例的 Page1 页面,并检查标头以查看结果:

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

页面应用模型约定

使用 AddPageApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于具有指定名称的页面,会在 PageApplicationModel 上调用操作。

示例演示了如何使用 AddPageApplicationModelConvention 将标头 AboutHeader 添加到“关于”页面:

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the AboutHeader has been added.

配置筛选器

ConfigureFilter 可配置要应用的指定筛选器。 用户可以实现筛选器类,但示例应用演示了如何在 Lambda 表达式中实现筛选器,该筛选器在后台作为可返回筛选器的工厂实现:

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header", 
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

页面应用模型用于检查指向 OtherPages 文件夹中 Page2 页面的段的相对路径。 如果条件通过,则添加标头。 如果不通过,则应用 EmptyFilter

EmptyFilter 是一种操作筛选器。 由于 Razor Pages 会忽略操作筛选器,因此,如果路径不包含 OtherPages/Page2EmptyFilter 应该无效。

localhost:5000/OtherPages/Page2 中请求示例的 Page2 页面,并检查标头以查看结果:

The OtherPagesPage2Header is added to the response for Page2.

配置筛选器工厂

ConfigureFilter 可配置指定的工厂,以将筛选器应用于所有 Razor Pages。

示例应用提供了一个示例,说明如何使用筛选器工厂将具有两个值的标头 FilterFactoryHeader 添加到应用的页面:

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC 筛选器和页面筛选器 (IPageFilter)

Razor Pages 会忽略 MVC 操作筛选器,因为 Razor Pages 使用处理程序方法。 可以使用其他类型的 MVC 筛选器:授权异常资源结果。 有关详细信息,请参阅筛选器主题。

页面筛选器 (IPageFilter) 是应用于 Razor Pages 的一种筛选器。 有关详细信息,请参阅 Razor Pages 的筛选方法

其他资源

了解如何使用页面路由和应用模型提供程序约定来控制 Razor Pages 应用中的页面路由、发现和处理。

需要为各个页面配置自定义页面路由时,可使用本主题稍后所述的 AddPageRoute 约定配置页面路由。

若要指定页面路由、添加路由段或向路由添加参数,请使用页面的 @page 指令。 有关详细信息,请参阅自定义路由

有些保留字不能用作路由段或参数名称。 有关详细信息,请参阅路由:保留的路由名称

查看或下载示例代码如何下载

方案 示例演示...
模型约定

Conventions.Add
  • IPageRouteModelConvention
  • IPageApplicationModelConvention
  • IPageHandlerModelConvention
将路由模板和标头添加到应用的页面。
页面路由操作约定
  • AddFolderRouteModelConvention
  • AddPageRouteModelConvention
  • AddPageRoute
将路由模板添加到某个文件夹中的页面以及单个页面。
页面模型操作约定
  • AddFolderApplicationModelConvention
  • AddPageApplicationModelConvention
  • ConfigureFilter(筛选器类、Lambda 表达式或筛选器工厂)
将标头添加到某个文件夹中的多个页面,将标头添加到单个页面,以及配置筛选器工厂以将标头添加到应用的页面。

使用 AddRazorPagesOptions 扩展方法向 Startup 类中服务集合的 AddMvc 添加和配置 Razor Pages 约定。 本主题稍后会介绍以下约定示例:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.Add( ... );
            options.Conventions.AddFolderRouteModelConvention(
                "/OtherPages", model => { ... });
            options.Conventions.AddPageRouteModelConvention(
                "/About", model => { ... });
            options.Conventions.AddPageRoute(
                "/Contact", "TheContactPage/{text?}");
            options.Conventions.AddFolderApplicationModelConvention(
                "/OtherPages", model => { ... });
            options.Conventions.AddPageApplicationModelConvention(
                "/About", model => { ... });
            options.Conventions.ConfigureFilter(model => { ... });
            options.Conventions.ConfigureFilter( ... );
        });
}

路由顺序

路由会为进行处理指定一个 Order(路由匹配)。

顺序 行为
-1 在处理其他路由之前处理该路由。
0 未指定顺序(默认值)。 不分配 Order (Order = null) 会将路由 Order 默认为 0(零)以进行处理。
1、2 … n 指定路由处理顺序。

按约定建立路由处理:

  • 按顺序(-1、0、1、2、… n)处理路由。
  • 当路由具有相同 Order 时,首先匹配最具体的路由,然后匹配不太具体的路由。
  • 当具有相同 Order 和相同数量参数的路由与请求 URL 匹配时,会按添加到 PageConventionCollection 的顺序处理路由。

如果可能,请避免依赖于建立的路由处理顺序。 通常,路由会通过 URL 匹配选择正确路由。 如果必须设置路由 Order 属性以便正确路由请求,则应用的路由方案可能会使客户端感到困惑并且难以维护。 应设法简化应用的路由方案。 示例应用需要显式路由处理顺序以使用单个应用来演示几个路由方案。 但是,在生产应用中应尝试避免设置路由 Order 的做法。

Razor Pages 路由和 MVC 控制器路由共享一个实现。 有关 MVC 主题中的路由顺序的信息可在以下位置获得:路由到控制器操作:对属性路由排序

模型约定

IPageConvention 添加委托,以添加应用于 Razor Pages 的模型约定

将路由模型约定添加到所有页面

使用 Conventions 创建 IPageRouteModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面路由模型构造过程中应用。

示例应用将 {globalTemplate?} 路由模板添加到应用中的所有页面:

public class GlobalTemplatePageRouteModelConvention 
    : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder 属性设置为 1。 这可确保在示例应用中实现以下路由匹配行为:

  • 本主题后面会添加 TheContactPage/{text?} 的路由模板。 “联系人”页面路由具有默认顺序 null (Order = 0),因此它在 {globalTemplate?} 路由模板之前进行匹配。
  • 本主题后面会添加 {aboutTemplate?} 路由模板。 为 {aboutTemplate?} 模板指定的 Order2。 当在 /About/RouteDataValue 中请求“关于”页面时,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。
  • 本主题后面会添加 {otherPagesTemplate?} 路由模板。 为 {otherPagesTemplate?} 模板指定的 Order2。 当使用路由参数请求 Pages/OtherPages 文件夹中的任何页面(例如,/OtherPages/Page1/RouteDataValue)时,由于设置了 Order 属性,因此“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 中,而不是 RouteData.Values["otherPagesTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

将 MVC 添加到 Startup.ConfigureServices 中的服务集合时,会添加 Razor Pages 选项,例如添加 Conventions。 有关示例,请参阅示例应用

options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

localhost:5000/About/GlobalRouteValue 中请求示例的“关于”页面并检查结果:

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

将应用模型约定添加到所有页面

使用 Conventions 创建 IPageApplicationModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面应用模型构造过程中应用。

为了演示此约定以及本主题后面的其他约定,示例应用包含了一个 AddHeaderAttribute 类。 类构造函数采用 name 字符串和 values 字符串数组。 将在其 OnResultExecuting 方法中使用这些值来设置响应标头。 本主题后面的页面模型操作约定部分展示了完整的类。

示例应用使用 AddHeaderAttribute 类将标头 GlobalHeader 添加到应用中的所有页面:

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Startup.cs

options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the GlobalHeader has been added.

将处理程序模型约定添加到所有页面

使用 Conventions 创建 IPageHandlerModelConvention 并将其添加到 IPageConvention 实例集合中,这些实例将在页面处理程序模型构造过程中应用。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

Startup.cs

options.Conventions.Add(new GlobalPageHandlerModelConvention());

页面路由操作约定

默认路由模型提供程序派生自 IPageRouteModelProvider,可调用旨在为页面路由配置提供扩展点的约定。

文件夹路由模型约定

使用 AddFolderRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于指定文件夹下的所有页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddFolderRouteModelConvention{otherPagesTemplate?} 路由模板添加到 OtherPages 文件夹中的页面:

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果使用路由参数值请求 Pages/OtherPages 文件夹中的任何页面(例如,/OtherPages/Page1/RouteDataValue)时,由于设置了 Order 属性,因此“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 中,而不是 RouteData.Values["otherPagesTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue 中请求示例的 Page1 页面并检查结果:

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

页面路由模型约定

使用 AddPageRouteModelConvention 可创建和添加 IPageRouteModelConvention,它对于具有指定名称的页面,会在 PageRouteModel 上调用操作。

示例应用使用 AddPageRouteModelConvention{aboutTemplate?} 路由模板添加到“关于”页面:

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder 属性设置为 2。 这样可确保,当提供单个路由值时,优先将 {globalTemplate?} 的模板(已在本主题的前面部分设置为 1)作为第一个路由数据值位置。 如果在 /About/RouteDataValue 中使用路由参数值请求“关于”页面,由于设置了 Order 属性,“RouteDataValue”会加载到 RouteData.Values["globalTemplate"] (Order = 1) 而不是 RouteData.Values["aboutTemplate"] (Order = 2) 中。

尽可能不要将 Order 设置为 Order = 0。 依赖路由选择正确的路由。

localhost:5000/About/GlobalRouteValue/AboutRouteValue 中请求示例的“关于”页面并检查结果:

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

配置页面路由

使用 AddPageRoute 配置路由,该路由指向指定页面路径中的页面。 生成的页面链接使用指定的路由。 AddPageRoute 使用 AddPageRouteModelConvention 建立路由。

示例应用为 Contact.cshtml 创建指向 /TheContactPage 的路由:

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

还可在 /Contact 中通过默认路由访问“联系人”页面。

示例应用的“联系人”页面自定义路由允许使用可选的 text 路由段 ({text?})。 该页面还在其 @page 指令中包含此可选段,以便访问者在 /Contact 路由中访问该页面:

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

请注意,在呈现的页面中,为联系人链接生成的 URL 反映了已更新的路由:

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

在常规路由 /Contact 或自定义路由 /TheContactPage 中访问“联系人”页面。 如果提供附加的 text 路由段,该页面会显示所提供的 HTML 编码段:

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

页面模型操作约定

实现 IPageApplicationModelProvider 的默认页面模型提供程序可调用约定,这些约定旨在为页面模型配置提供扩展点。 在生成和修改页面发现及处理方案时,可使用这些约定。

对于此部分中的示例,示例应用使用 AddHeaderAttribute 类(一个 ResultFilterAttribute)来应用响应标头:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

示例演示了如何使用约定将该属性应用于某个文件夹中的所有页面以及单个页面。

文件夹应用模型约定

使用 AddFolderApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于指定文件夹下的所有页面,会在 PageApplicationModel 实例上调用操作。

示例演示了如何使用 AddFolderApplicationModelConvention 将标头 OtherPagesHeader 添加到应用的 OtherPages 文件夹内的页面:

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 中请求示例的 Page1 页面,并检查标头以查看结果:

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

页面应用模型约定

使用 AddPageApplicationModelConvention 可创建和添加 IPageApplicationModelConvention,它对于具有指定名称的页面,会在 PageApplicationModel 上调用操作。

示例演示了如何使用 AddPageApplicationModelConvention 将标头 AboutHeader 添加到“关于”页面:

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that the AboutHeader has been added.

配置筛选器

ConfigureFilter 可配置要应用的指定筛选器。 用户可以实现筛选器类,但示例应用演示了如何在 Lambda 表达式中实现筛选器,该筛选器在后台作为可返回筛选器的工厂实现:

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header", 
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

页面应用模型用于检查指向 OtherPages 文件夹中 Page2 页面的段的相对路径。 如果条件通过,则添加标头。 如果不通过,则应用 EmptyFilter

EmptyFilter 是一种操作筛选器。 由于 Razor Pages 会忽略操作筛选器,因此,如果路径不包含 OtherPages/Page2EmptyFilter 应该无效。

localhost:5000/OtherPages/Page2 中请求示例的 Page2 页面,并检查标头以查看结果:

The OtherPagesPage2Header is added to the response for Page2.

配置筛选器工厂

ConfigureFilter 可配置指定的工厂,以将筛选器应用于所有 Razor Pages。

示例应用提供了一个示例,说明如何使用筛选器工厂将具有两个值的标头 FilterFactoryHeader 添加到应用的页面:

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About 中请求示例的“关于”页面,并检查标头以查看结果:

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC 筛选器和页面筛选器 (IPageFilter)

Razor Pages 会忽略 MVC 操作筛选器,因为 Razor Pages 使用处理程序方法。 可以使用其他类型的 MVC 筛选器:授权异常资源结果。 有关详细信息,请参阅筛选器主题。

页面筛选器 (IPageFilter) 是应用于 Razor Pages 的一种筛选器。 有关详细信息,请参阅 Razor Pages 的筛选方法

其他资源