Reputation: 7414
I need to provide a routing mechanic where the routes are generated at runtime from user account creations. Such as http://mysite/username/home
.
I assume this can be done via routing but i'm not sure where to get started on it with ASP.Net Core. I've seen some examples online for MVC 5 but ASP.Net Core seems to handle routing a little bit differently. How can I ensure that the site doesn't get confused between http://mysite/username/news
being the users custom landing page and http://mysite/news
being the sites news page?
Upvotes: 7
Views: 23531
Reputation: 4539
Routing in .net Core is a pretty broad topic but my preference on Dynamic Routing is below!
public class Router : IRouter
{
private readonly IRouter _innerRouter;
IApplicationBuilder b;
public Router(IRouter innerRouter, IApplicationBuilder bb)
{
if (innerRouter == null)
throw new ArgumentNullException("innerRouter");
this._innerRouter = innerRouter;
b = bb;
}
public async Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
{
// Trim the leading slash
requestPath = requestPath.Substring(1);
}
if (requestPath.Trim() == String.Empty)
return;
using (var serviceScope = b.ApplicationServices.CreateScope())
{
DomainDbContext dbContext = serviceScope.ServiceProvider.GetRequiredService<DomainDbContext>();
int type = 0;
int id = 0;
Page page = dbContext.Page.Where(_ => _.Url.ToLower().Contains(requestPath.ToLower()) || requestPath.ToLower().Contains(_.Url.ToLower())).FirstOrDefault();
if (page != null)
{
type = 1; //page
id = page.Id;
}
Blog blog = dbContext.Blog.Where(_ => _.Url.ToLower().Contains(requestPath.ToLower()) || requestPath.ToLower().Contains(_.Url.ToLower())).FirstOrDefault();
if (blog != null)
{
type = 2; //blog
id = blog.Id;
}
if (type == 0)
return;
//Invoke MVC controller/action
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
newRouteData.Routers.Add(this._innerRouter);
newRouteData.Values["controller"] = "Dynamic";
newRouteData.Values["action"] = "Index";
newRouteData.Values["id"] = type + "," + id;
try
{
context.RouteData = newRouteData;
await this._innerRouter.RouteAsync(context);
}
finally
{
// Restore the original values to prevent polluting the route data.
//if (!context.IsHandled)
//{
// context.RouteData = oldRouteData;
//}
}
}
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
VirtualPathData result = null;
var values = context.Values;
var controller = Convert.ToString(values["controller"]);
var action = Convert.ToString(values["action"]);
var id = Convert.ToString(values["id"]);
if ("Item".Equals(controller) && "View".Equals(action))
{
result = new VirtualPathData(this, "abcd?id=" + id);
//context.IsBound = true;
}
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
}
}
Upvotes: 3
Reputation: 24063
I am not sure if the below way is correct. It works for me but you should test it for your scenarios.
First create a user service to check username:
public interface IUserService
{
bool IsExists(string value);
}
public class UserService : IUserService
{
public bool IsExists(string value)
{
// your implementation
}
}
// register it
services.AddScoped<IUserService, UserService>();
Then create route constraint for username:
public class UserNameRouteConstraint : IRouteConstraint
{
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
// check nulls
object value;
if (values.TryGetValue(routeKey, out value) && value != null)
{
var userService = httpContext.RequestServices.GetService<IUserService>();
return userService.IsExists(Convert.ToString(value));
}
return false;
}
}
// service configuration
services.Configure<RouteOptions>(options =>
options.ConstraintMap.Add("username", typeof(UserNameRouteConstraint)));
Finally write route and controllers:
app.UseMvc(routes =>
{
routes.MapRoute("default",
"{controller}/{action}/{id?}",
new { controller = "Home", action = "Index" },
new { controller = @"^(?!User).*$" }// exclude user controller
);
routes.MapRoute("user",
"{username:username}/{action=Index}",
new { controller = "User" },
new { controller = @"User" }// only work user controller
);
});
public class UserController : Controller
{
public IActionResult Index()
{
//
}
public IActionResult News()
{
//
}
}
public class NewsController : Controller
{
public IActionResult Index()
{
//
}
}
Upvotes: 5