Strongly typed route in @page directive in Razor Pages
Asked Answered
S

4

15

On a ASP.NET Core Razor Page we can do this to change the default folder-based route:

@page "/xxx/yyy"

but trying to do this with a strongly typed property in some class Constants.Routes does not work

public const string Myroute = "/mysuperroute"; 

and

@page @Constants.Routes.Myroute

I am getting an error at compile time The 'page' directive expects a string surrounded by double quotes.

Is there a way around this? I would like to avoid route duplication through the options e.g. https://learn.microsoft.com/en-us/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-2.2#configure-a-page-route Thanks

Sasaki answered 1/3, 2019 at 9:23 Comment(0)
A
11

Little late to the party, but I had the same issue and found the RouteAttribute which associates the component with a given route template.

So you can do the following in your razor component and have it available under {host}:{port}/test:

@attribute [Microsoft.AspNetCore.Components.RouteAttribute(Href)]


@code {
  public const string Href = "test";
}

In a NavLink you can now do this:

<NavLink class="nav-link" href="@TestComponent.Href">
    Test Entry
</NavLink>

Source

Alleman answered 5/2, 2020 at 10:16 Comment(3)
Just so it's clear for future searchers -- with the @attribute there's no need for @page. You can also have the constant in your page model (and you might have to add a @using). And because attribute is already code, you don't need an @ in front of your constant variable.Penney
I apologize for so many comments and deletes: So - this solution works on .razor pages and files, whereas the solution posted bellow works for cshtml files/pages. This is the only way I found that they both work. Thank you for this!Tattler
In core 6 it's simplified to @attribute [Route(Href)]Lucylud
C
3

I don't know if a .NET core 3.1 thing, but RouteAttribute didn't work for RazorPages.

@page
@attribute [Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadata("RouteTemplate", MyRoutes.QualificationQueue)]
Catechumen answered 23/3, 2020 at 22:6 Comment(3)
This worked for .Net 5, passing a constant from our nested class-constant model. Thank you for this!Tattler
Note: This only works for cshtml files, whereas the solution posted above works in .razor files. Seems we will have to use both afterall.Tattler
worked in .net6 Razor pages application.Caulescent
H
2

If you don't like referencing the component/pages directly you can adapt @Radall's excellent answer as

@attribute [Route(StaticRoutes.Dashboard)]

public static class StaticRoutes
{
    public const string Dashboard = "/app/dashboard";

    public static class MyArea
    {
        public const string Home = "/app/my-area";
        public const string Second = "/app/my-area/second";
    }
}

And use it:

<NavLink class="nav-link" href="@StaticRoutes.Dashboard">
    Dashboard
</NavLink>
<NavLink class="nav-link" href="@StaticRoutes.MyArea.Home">
    Area
</NavLink>
Handwriting answered 24/6, 2022 at 12:42 Comment(0)
P
1

I could not find a way to specify a page route in a strongly typed manner for the razor pages in .Net Core 6.0.

  1. The @page directive expects a string and would throw an exception for a variable.

  2. The RouteAttribute is simply ignored

  3. There is a way of adding extra routes in Program.cs like this:

    builder.Services.AddRazorPages().AddRazorPagesOptions(options => { options.Conventions.AddPageRoute("/Index", "default.aspx"); });

But it is still required for you to provide the page name as a parameter.

There is however a way of mapping a PageModel class to the corresponding page name.

You need to implement IPageApplicationModelConvention and add it to the service container:

public class InitStructurePageRouteModelConvention : IPageApplicationModelConvention
    {
        public void Apply(PageApplicationModel model)
        {
            var page = Structure.FindByTypeInfo(model.ModelType);

            if (page != null)
            {
                page.PagePath = model.ViewEnginePath;
                page.Area = model.AreaName;
            }
        }
    }

The PageApplicationModel has everything we need to do the mapping between PageModel and PagePath. The "Structure" is a tree like object holding various metadata (including PageModel type, PagePath and Area) about application pages.

The registration in service container:

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

The "Apply" method will be called for every razor page.

So instead of setting the page routes according to the application structure you can have the structure populated with the PageModels and map the Page names during the Apply call of IPageApplicationModelConvention.

Kind of reverse approach: Not setting the routes according to your structure but updating your structure with existing routes (or page names).

The downside is that you cannot have multiple pages for one PageModel class.

But you can now use the page.PagePath from the Structure object in the routing generation.

Sorry for not providing more details about the Structure object but it would make the post too big while being unrelated to your needs.

Phila answered 13/9, 2022 at 7:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.