Johnathon Sullinger
Johnathon Sullinger

Reputation: 7414

Dynamic routing in ASP.Net Core

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

Answers (2)

Hamit YILDIRIM
Hamit YILDIRIM

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

adem caglin
adem caglin

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

Related Questions