在 Razor 页面的 Url 中添加没有可见标识符的静态路由

Add static Routes without visible Identifiers in Url in Razor Pages

我们想将网站从 Asp.Net Webforms 迁移到 Asp.Net Core Webapplication (Razor Pages)。我们现在有了没有可见标识符的可读 Urls(为了可读性、SEO 和防止在可能的数据库迁移后更改 Urls...)。

为此,我在启动时在 Global.Asax 中动态生成了路由:

RouteTable.Routes.MapPageRoute("myroute1",
    "banana/",
     "~/content.aspx", false, new RouteValueDictionary { { "id", "4711" }, , { "otherid", "4812" }
     
RouteTable.Routes.MapPageRoute("myroute2",
    "apple/",
     "~/content.aspx", false, new RouteValueDictionary { { "id", "4913" }, , { "otherid", "5014" }  

这样用户可以这样调用 content.aspx:

https://example.com/banana
https://example.com/apple

在 content.aspx 中,我从 RouteValues 中获得了映射的“id”和“otherid”。

我如何在 Razor Pages 中实现这一点?

现在我有一个“Content.cshtml”,我需要访问 id 和 otherid。我在顶部添加了这个:

@page "/content/{id:int}/{otherid:int}/{title:string}"

以上代码允许 mit 像这样调用 Urls:

https://example.com/content/4711/4812/banana // still have the ids in it and prefix "content"

是否可以在启动时使用固定参数添加所有路由?我还没有找到类似的东西。

问候 cpt.oneeye

要获得静态 url,您可以将路由放在启动 class 中。例如:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
         name: "default1",
         pattern: "{controller=Home}/{action=Index}/{id?}"
    );
});

在这种情况下,您可以根据需要定义端点。比如你的路由,你可以这样写:

endpoints.MapControllerRoute(
     name: "default",
     pattern: "{content}/{id?}/{id?}/{banana}"
);

或者您可以使用可用于每个控制器的 Attribute Routingread more about that in

对不起我的英语

感谢这个帖子,我找到了解决方案:

可在此处找到另一个完整示例:

https://github.com/dotnet/AspNetCore.Docs/issues/12997

根据我在顶部的示例,您首先制作自己的 DynamicRouteValueTransformer(请参阅命名空间 Microsoft.AspNetCore.Mvc.Routing):

public class NavigationTransformer : DynamicRouteValueTransformer
{
    private readonly MyDatabaseContext _repository;

    public NavigationTransformer(MyDatabaseContext repository)
    {
        _repository = repository;
    }

    public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
    {
        // all requests which are incoming and fit a specific pattern will go through this method
        
        var slug = values["slug"] as string;

        // just get your needed info according to the "slug"
        // in my case i have to map according to NavigationNodes that are stored in the Database
        var myNavigationNode = _repository.NavigationNodes.FirstOrDefault(nn => nn.Name == slug);

        if (node == null)
        {
            // User will get a 404
            return null;
        }
        
        // set Path to Page according to Type (or whatever fits your logic)
        string pagename = "";
        
        if(node.Type == "ContentPage")
        {
            pagename = "/Content"  // Pages/Content.cshtml
        }
        else if(node.Type == "ContactForm")
        {
            pagename = "/Contact" // Pages/Contact.cshtml
        } 
        else 
        {
            pagename = "/Someotherpage"
        } 

        // return all RouteValues you need on your Page
        return new RouteValueDictionary()
        {
            { "page", pagename },
            { "id", node.Id },
            { "otherid", node.OtherId }
        };
    }
}

在 Startup.ConfigureServices() 中注册新服务:

services.AddScoped<NavigationTransformer>();

在 Startup.Configure() 中,您将 NavigationTransformer 添加到端点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapDynamicPageRoute<NavigationTransformer>("{slug}"); // just "slug" so that every request will be handled by NavigationTransformer, you can also add a prefix-folder
});

现在,当您像下面这样调用 url 时,您将通过 Transformer 并且可以即时重新路由:

https://example.com/banana
https://example.com/apple

请注意现有页面的路由更强大。所以如果我们有一个 Apple.cshtml 第二个 Url 仍然会被路由到 Apple.cshtml 而不是 Content.cshtml