Reputation: 16066
I'm trying to implement my own dynamic router, my plan is to pull routes from my database and create a set of dynamic landing pages, my issue is that I'm getting 404 after setting up context.RouteData to my new route data.
I just want to redirect to my LandingPageController and the Index IActionResult everytime I found a route.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
using System.Collections.Generic;
using System.Linq;
namespace Myproject.Web.Main.Config
{
public class LandingPageRouter : IRouter
{
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return null;
}
public Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
requestPath = requestPath.Substring(1);
}
var pagefound = GetPages().Any(x => x == requestPath);
if (pagefound)
{
//TODO: Handle querystrings
var routeData = new RouteData();
routeData.Values["controller"] = "LandingPage";
routeData.Values["action"] = "Index";
context.RouteData = routeData;
}
return Task.FromResult(0);
}
private IEnumerable<string> GetPages()
{
//TODO: pull from database
return new List<string> { "page-url-title", "another-dynamic-url" };
}
}
}
I looked at this answer but it seems outdated some properties in the context doesn't even exist anymore in RC2.
What Am I missing?
Upvotes: 0
Views: 916
Reputation: 16066
Based on this answer and @sock approach, I passed the route builder which is going to pass at the same time the router context
public LandingPageRouter(IRouteBuilder routeBuilder)
{
_routeBuilder = routeBuilder;
}
public Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
requestPath = requestPath.Substring(1);
}
var pagefound = GetPages().SingleOrDefault(x => x.Name == requestPath);
if (pagefound!=null)
{
//TODO: Handle querystrings
var routeData = new RouteData();
routeData.Values["controller"] = "LandingPage";
routeData.Values["action"] = "Index";
routeData.Values["id"] = pagefound.Id;
context.RouteData = routeData;
return _routeBuilder.DefaultHandler.RouteAsync(context);
}
return Task.FromResult(0);
}
Routes configuration in startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.Routes.Add(new LandingPageRouter(routes));
}
Upvotes: 0
Reputation: 5413
It doesn't seem like the nicest solution but from my testing it should work for your requirements.
I injected the default MVC IRouter
into your LandingPageRouter
. Then, once you have updated the route data, just call the default router and pass in the context:
public class LandingPageRouter : IRouter
{
private readonly IRouter _router;
public LandingPageRouter(IRouter router)
{
_router = router;
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return null;
}
public Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
requestPath = requestPath.Substring(1);
}
var pagefound = GetPages().Any(x => x == requestPath);
if (pagefound)
{
//TODO: Handle querystrings
var routeData = new RouteData();
routeData.Values["controller"] = "LandingPage";
routeData.Values["action"] = "Index";
context.RouteData = routeData;
return _router.RouteAsync(context);
}
return Task.FromResult(0);
}
private IEnumerable<string> GetPages()
{
//TODO: pull from database
return new List<string> { "page-url-title", "another-dynamic-url" };
}
}
Then just insert the default route wherever you are adding your route in Startup.Configure
:
app.UseMvc(routes =>
{
routes.Routes.Add(new LandingPageRouter(routes.DefaultHandler));
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Upvotes: 2