BendEg
BendEg

Reputation: 21088

ASP.NET MVC Page-URL and Controller/Action Routing

I have problems building an ASP.NET MVC page which allows two sorts of routing.

I have a database where all pages are stored with an url-path like: /Site1/Site2/Site3 i tried to use an IRouteConstraint in my first route, to check wether the requested site is a site from my database (permalink).

In the second case, i want to use the default asp.net mvc {controller}/{action} functionality, for providing simple acces from an *.cshtml.

Now i don't know if this is the best way. Furthermore i have the problem, how to root with the IRouteContraint.

Does anyone have any experiance with this?

I'm using asp.net mvc 5.

Problem solved, final solution:

  1. Adding this two routes:

    routes.MapRoute(
        "FriendlyUrlRoute",
        "{*FriendlyUrl}"
    ).RouteHandler = new FriendlyUrlRouteHandler();
    
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Page", action = "Load", id = UrlParameter.Optional },
        namespaces: controllerNamespaces.ToArray()
    );
    
  2. My own Route-Handler:

    public class FriendlyUrlRouteHandler : System.Web.Mvc.MvcRouteHandler
    {
        protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
        {
            var friendlyUrl = (string)requestContext.RouteData.Values["FriendlyUrl"];
    
            WebPageObject page = null;
    
            if (!string.IsNullOrEmpty(friendlyUrl))
            {
                page = PageManager.Singleton.GetPage(friendlyUrl);
            }
    
            if (page == null)
            {
                page = PageManager.Singleton.GetStartPage();
            }
    
            // Request valid Controller and Action-Name
            string controllerName = String.IsNullOrEmpty(page.ControllerName) ? "Page" : page.ControllerName;
            string actionName = String.IsNullOrEmpty(page.ActionName) ? "Load" : page.ActionName;
    
            requestContext.RouteData.Values["controller"] = controllerName;
            requestContext.RouteData.Values["action"] = actionName;
            requestContext.RouteData.Values["id"] = page;
    
            return base.GetHttpHandler(requestContext);
        }
    }
    

Upvotes: 1

Views: 1405

Answers (1)

Ali
Ali

Reputation: 2592

You can use attribute routing which is in MVC 5 and combine attribute routing with convention-based routing to check the condition that you want on controller class or action methods.

And you could make the constraint yourself to use it on the action methods like this:

public class ValuesConstraint : IRouteConstraint
{
    private readonly string[] validOptions;
    public ValuesConstraint(string options)
    {
        validOptions = options.Split('|');
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
        }
        return false;
    }
}

To use attribute routing you just need to call MapMvcAttributeRoutes during configuration and call the normal convention routing afterwards. also you should add your constraint before map the attributes, like the code below:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    var constraintsResolver = new DefaultInlineConstraintResolver();
    constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
    routes.MapMvcAttributeRoutes(constraintsResolver);

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

Now on your controller class you can check the route and decide what to do with different urls like below:

for example: // /mysite/Site1 and /mysite/Site2 but not /mysite/Site3

[Route("mysite/{site:values(Site1|Site2)}")]
public ActionResult Show(string site)
{
    return Content("from my database " + site);
}

And you could do all kind of checking just on you controller class as well.

I hope this gives you a bit of clue to achieve the thing that you want.

Upvotes: 2

Related Questions