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

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

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

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

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

Scenario 示例演示
模型约定

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

Razor 页面规则是使用 AddRazorPages 配置重载来配置 RazorPagesOptions。 本主题后面将介绍以下约定示例:


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 用于处理(路由匹配)。

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

路由处理是按照约定建立的:

  • 路由按顺序处理 (-1, 0, 1, 2, ...n).
  • 如果路由相同 Order,则首先匹配最具体的路由,后跟不太具体的路由。
  • 当具有相同 Order 和相同参数数量的路由与请求 URL 匹配时,将按照添加到 PageConventionCollection 的顺序进行处理。

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

Razor 页面路由和 MVC 控制器路由共享实现。 有关 MVC 主题中路由顺序的信息,请参阅 控制器操作路由:属性路由排序

模型约定

添加委托IPageConvention以添加适用于 Pages 的Razor。

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

使用 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 被添加到服务集合时,将添加页面选项,例如Conventions和Razor。 有关示例,请参阅 示例应用

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

对于OrderAttributeRouteModel属性设置为1。 这可确保示例应用中的以下路由匹配行为:

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

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

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

  • 例如: /OtherPages/Page1/xyz

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

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

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

About请求样本的localhost:{port}/About/GlobalRouteValue页面并检查结果:

使用 GlobalRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

示例应用使用 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 并检查标头以查看结果:

“关于”页的响应标头显示已添加 GlobalHeader。

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

使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果请求Pages/OtherPages文件夹中的页面,并且带有路由参数值(例如/OtherPages/Page1/RouteDataValue),由于设置了RouteData.Values["globalTemplate"]属性,“RouteDataValue”会加载到Order = 1RouteData.Values["otherPagesTemplate"])而不是Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

访问示例的 Page1 页面 localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue,并检查结果:

在请求 OtherPages 文件夹中的 Page1 时,使用了 GlobalRouteValue 和 OtherPagesRouteValue 两个路由段。呈现的页面显示,这些路由数据值在页面的 OnGet 方法中被捕获。

页面路由模型约定

使用 AddPageRouteModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageRouteModelConvention

示例应用使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果使用路由参数值请求 /About/RouteDataValueAbout 页,则由于设置RouteData.Values["globalTemplate"]属性,“RouteDataValue”将加载到 Order = 1RouteData.Values["aboutTemplate"])而不是 Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

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

使用 GlobalRouteValue 和 AboutRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

记录器输出显示:

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

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

请参阅 参数转换器

配置页面路由

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

示例应用为/TheContactPageContact页面创建到Razor的路由:

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

Contact页面也可以通过其默认路由在/Contact1访问。

示例应用的自定义路由到 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 反映了更新后的路由:

导航栏中的示例应用联系人链接

检查呈现的 HTML 中的“联系人”链接指示 href 设置为“/TheContactPage”

访问Contact页,可以通过其普通路线/Contact或自定义路线/TheContactPage。 如果提供其他 text 路由段,页面将显示提供的 HTML 编码段:

Edge 浏览器中提供 URL 可选“text”路由部分“TextValue”的示例。渲染页面显示该“text”部分的值。

页面模型操作约定

实现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来演示的使用:

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

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

OtherPages/Page1 页的响应标头显示已添加 OtherPagesHeader。

页面应用模型约定

使用 AddPageApplicationModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageApplicationModelConvention

此示例演示如何通过将标题AddPageApplicationModelConvention添加到“关于”页来使用AboutHeader

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

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

“关于”页的响应标头显示已添加 AboutHeader。

配置筛选器

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操作筛选器,因此如果路径不包含EmptyFilterOtherPages/Page2将不会生效。

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

OtherPagesPage2Header 已添加到 Page2 的响应中。

配置过滤器工厂

ConfigureFilter将指定的工厂配置为对所有应用Razor。

示例应用提供了一个通过添加标头(具有两个值)到应用页面中来使用筛选器工厂的示例。

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 并检查标头以查看结果:

“关于”页的响应标头显示已添加两个 FilterFactoryHeader 标头。

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

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

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

其他资源

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

如果需要为各个页面配置自定义页面路由,请使用本主题后面介绍的 AddPageRoute 约定 配置到页面的路由。

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

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

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

Scenario 该示例演示...
模型约定

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

Razor 页面的约定是通过配置 AddRazorPagesRazorPagesOptions 重载在 Startup.ConfigureServices 中进行的。 本主题后面将介绍以下约定示例:

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 用于处理(路由匹配)。

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

路由处理是按照约定建立的:

  • 路由按顺序处理 (-1, 0, 1, 2, ...n).
  • 如果路由相同 Order,则首先匹配最具体的路由,后跟不太具体的路由。
  • 当具有相同 Order 和相同参数数量的路由与请求 URL 匹配时,将按照添加到 PageConventionCollection 的顺序进行处理。

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

Razor 页面路由和 MVC 控制器路由共享实现。 有关 MVC 主题中路由顺序的信息,请参阅 控制器操作路由:属性路由排序

模型约定

添加委托IPageConvention以添加适用于 Pages 的Razor。

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

使用 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?}"),
                }
            });
        }
    }
}

对于OrderAttributeRouteModel属性设置为1。 这可确保示例应用中的以下路由匹配行为:

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

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

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

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

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

使用 GlobalRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

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

使用 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 并检查标头以查看结果:

“关于”页的响应标头显示已添加 GlobalHeader。

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

使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果请求Pages/OtherPages文件夹中的页面,并且带有路由参数值(例如/OtherPages/Page1/RouteDataValue),由于设置了RouteData.Values["globalTemplate"]属性,“RouteDataValue”会加载到Order = 1RouteData.Values["otherPagesTemplate"])而不是Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

访问示例的 Page1 页面 localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue,并检查结果:

在请求 OtherPages 文件夹中的 Page1 时,使用了 GlobalRouteValue 和 OtherPagesRouteValue 两个路由段。呈现的页面显示,这些路由数据值在页面的 OnGet 方法中被捕获。

页面路由模型约定

使用 AddPageRouteModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageRouteModelConvention

示例应用使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果使用路由参数值请求 /About/RouteDataValueAbout 页,则由于设置RouteData.Values["globalTemplate"]属性,“RouteDataValue”将加载到 Order = 1RouteData.Values["aboutTemplate"])而不是 Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

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

使用 GlobalRouteValue 和 AboutRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

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

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

PageRouteTransformerConvention页面路由模型约定将参数转换器应用于应用中自动生成的页面路由的文件夹和文件名段。 例如,在Razor的页面文件,其路由将被重写,从/Pages/SubscriptionManagement/ViewAll.cshtml改为/SubscriptionManagement/ViewAll

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

PageRouteTransformerConvention 中,Startup.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();
    }
}

Warning

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

配置页面路由

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

示例应用为/TheContactPage创建到Contact.cshtml的路由。

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 反映了更新后的路由:

导航栏中的示例应用联系人链接

检查呈现的 HTML 中的“联系人”链接指示 href 设置为“/TheContactPage”

访问“联系人”页面,无论是通过其普通路由 /Contact 还是自定义路由 /TheContactPage。 如果提供其他 text 路由段,页面将显示提供的 HTML 编码段:

Edge 浏览器中提供 URL 可选“text”路由部分“TextValue”的示例。渲染页面显示该“text”部分的值。

页面模型操作约定

实现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来演示的使用:

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

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

OtherPages/Page1 页的响应标头显示已添加 OtherPagesHeader。

页面应用模型约定

使用 AddPageApplicationModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageApplicationModelConvention

此示例演示如何通过将标题AddPageApplicationModelConvention添加到“关于”页来使用AboutHeader

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

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

“关于”页的响应标头显示已添加 AboutHeader。

配置筛选器

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操作筛选器,因此如果路径不包含EmptyFilterOtherPages/Page2将不会生效。

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

OtherPagesPage2Header 已添加到 Page2 的响应中。

配置过滤器工厂

ConfigureFilter将指定的工厂配置为对所有应用Razor。

示例应用提供了一个通过添加标头(具有两个值)到应用页面中来使用筛选器工厂的示例。

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 并检查标头以查看结果:

“关于”页的响应标头显示已添加两个 FilterFactoryHeader 标头。

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

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

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

其他资源

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

如果需要为各个页面配置自定义页面路由,请使用本主题后面介绍的 AddPageRoute 约定 配置到页面的路由。

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

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

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

Scenario 该示例演示...
模型约定

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

Razor页面约定是通过在AddRazorPagesOptions类的AddMvc服务集合中使用Startup扩展方法添加和配置的。 本主题后面将介绍以下约定示例:

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 用于处理(路由匹配)。

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

路由处理是按照约定建立的:

  • 路由按顺序处理 (-1, 0, 1, 2, ...n).
  • 如果路由相同 Order,则首先匹配最具体的路由,后跟不太具体的路由。
  • 当具有相同 Order 和相同参数数量的路由与请求 URL 匹配时,将按照添加到 PageConventionCollection 的顺序进行处理。

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

Razor 页面路由和 MVC 控制器路由共享实现。 有关 MVC 主题中路由顺序的信息,请参阅 控制器操作路由:属性路由排序

模型约定

添加委托IPageConvention以添加适用于 Pages 的Razor。

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

使用 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?}"),
                }
            });
        }
    }
}

对于OrderAttributeRouteModel属性设置为1。 这可确保示例应用中的以下路由匹配行为:

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

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

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

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

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

使用 GlobalRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

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

使用 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 并检查标头以查看结果:

“关于”页的响应标头显示已添加 GlobalHeader。

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

使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果请求Pages/OtherPages文件夹中的页面,并且带有路由参数值(例如/OtherPages/Page1/RouteDataValue),由于设置了RouteData.Values["globalTemplate"]属性,“RouteDataValue”会加载到Order = 1RouteData.Values["otherPagesTemplate"])而不是Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

访问示例的 Page1 页面 localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue,并检查结果:

在请求 OtherPages 文件夹中的 Page1 时,使用了 GlobalRouteValue 和 OtherPagesRouteValue 两个路由段。呈现的页面显示,这些路由数据值在页面的 OnGet 方法中被捕获。

页面路由模型约定

使用 AddPageRouteModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageRouteModelConvention

示例应用使用 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?}"),
            }
        });
    }
});

对于OrderAttributeRouteModel属性设置为2。 这保证在提供单一路由值时,位置为 {globalTemplate?} 的模板(在主题中先前设置为 1)将被优先用于第一个路由数据值位置。 如果使用路由参数值请求 /About/RouteDataValueAbout 页,则由于设置RouteData.Values["globalTemplate"]属性,“RouteDataValue”将加载到 Order = 1RouteData.Values["aboutTemplate"])而不是 Order = 2Order)。

尽可能不要设置Order,这会导致Order = 0。 依靠路由来选择正确的路径。

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

使用 GlobalRouteValue 和 AboutRouteValue 的路由段请求“关于”页。呈现的页面显示路由数据值是在页面的 OnGet 方法中捕获的。

配置页面路由

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

示例应用为/TheContactPage创建到Contact.cshtml的路由。

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 反映了更新后的路由:

导航栏中的示例应用联系人链接

检查呈现的 HTML 中的“联系人”链接指示 href 设置为“/TheContactPage”

访问“联系人”页面,无论是通过其普通路由 /Contact 还是自定义路由 /TheContactPage。 如果提供其他 text 路由段,页面将显示提供的 HTML 编码段:

Edge 浏览器中提供 URL 可选“text”路由部分“TextValue”的示例。渲染页面显示该“text”部分的值。

页面模型操作约定

实现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来演示的使用:

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

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

OtherPages/Page1 页的响应标头显示已添加 OtherPagesHeader。

页面应用模型约定

使用 AddPageApplicationModelConvention 创建并添加一个用于调用具有指定名称的页面上的动作的 IPageApplicationModelConvention

此示例演示如何通过将标题AddPageApplicationModelConvention添加到“关于”页来使用AboutHeader

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

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

“关于”页的响应标头显示已添加 AboutHeader。

配置筛选器

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操作筛选器,因此如果路径不包含EmptyFilterOtherPages/Page2将不会生效。

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

OtherPagesPage2Header 已添加到 Page2 的响应中。

配置过滤器工厂

ConfigureFilter将指定的工厂配置为对所有应用Razor。

示例应用提供了一个通过添加标头(具有两个值)到应用页面中来使用筛选器工厂的示例。

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 并检查标头以查看结果:

“关于”页的响应标头显示已添加两个 FilterFactoryHeader 标头。

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

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

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

其他资源