7

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?

3
  • You cab solve this by routing right. For example, you would have a route in your controller of [Route("{username}/news")] which differs from the other of Route["news"]. Does that make sense or have I misunderstood. Commented Jul 11, 2016 at 7:56
  • I didn't realize you could use {username}. Does asp.net core handle that by looking at the identity? What happens if http://mysite/johndoe/news is a public facing url so anyone who's not authenticated is allowed to hit it? Commented Jul 11, 2016 at 17:11
  • Sorry I mean you have that as a parameter on your route and redirect as appropriate. That make sense? Commented Jul 11, 2016 at 17:14

2 Answers 2

5

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()
    {
        //
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I know the order will matter in which I place the routes, so I'm wondering now what happens if there is a username called "news" that would conflict with my "News" controller action route. By placing the default routing stuff first, the "News" user would be ignored because a "News" controller action exists. Should I try to route to a user "domain" first before hitting my defaults?
If you change order of routes, in this case News controller route would be ignored and UserService.Exists method would be called for all request(even if it is not required). I think you should handle conflicts in anyway. Using "reserved user names" seems reasonable.
Can also do this with endpoint routing introduced in .net core 3.0 something like app.UseEndpoints(endpoints => { endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); });
3

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;
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.