We want to migrate a Website from Asp.Net Webforms to Asp.Net Core Webapplication (Razor Pages). We now have readable Urls without visible identifiers (for readability, SEO and to prevent changing Urls after possible database migrations...).
For that i generated dynamically Routes in the Global.Asax at Startup:
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" }
That way users could call the content.aspx like this:
https://example.com/banana
https://example.com/apple
In the content.aspx i got the mapped "id" and "otherid" from the RouteValues.
How can i achieve this in Razor Pages?
Now i have a "Content.cshtml" and there i need access to id and otherid. I added this at the top:
@page "/content/{id:int}/{otherid:int}/{title:string}"
The above code allows mit to call Urls like this:
https://example.com/content/4711/4812/banana // still have the ids in it and prefix "content"
Is there a possibility to add all Routes at Startup with fixed Parameters? I have not been able to find anything similar.
Greetings cpt.oneeye
I found a solution thanks to this thread:
Is there a way to do dynamic routing with Razor Pages?
Another full example can be found here:
https://github.com/dotnet/AspNetCore.Docs/issues/12997
According to my example at the top you first make your own DynamicRouteValueTransformer (see Namespace 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 }
};
}
}
In the Startup.ConfigureServices() you register the new Service:
services.AddScoped<NavigationTransformer>();
In the Startup.Configure() you add the NavigationTransformer to Endpoints:
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
});
Now when you call a url like the following you will came through the Transformer and you are able to reroute on the fly:
https://example.com/banana
https://example.com/apple
Be aware the Routings of existing Pages are stronger. So if we have a Apple.cshtml the second Url will still be routed to Apple.cshtml and not to Content.cshtml