November Happy Hour will be moved to Thursday December 5th.

Basic routing conflict

Vote:
 

Hey guys,

i want some very basic routing like explained by Joel here in his article : http://joelabrahamsson.com/custom-routing-for-episerver-content/#basic-routing

now i created my route like this for a virtual page (external source):

routes.MapContentRoute(
                  "Portfolio", "{language}/{node}/{action}/{name}/{pid}",
                  new { action = "showcase"});

so urls like http://abc.local/company/showcase/i-test-showcase/234 are getting routed to my controller , because it checks for the action name 'showcase' in the url.

public abstract class PageControllerBase : PageController where T : SitePageData
    {
        
        public ActionResult Showcase(SitePageData currentPage, string name, int? pid)
        {
            if (pid == null)
            {
                return HttpNotFound();
            }
            var pageModel = new PortfolioDetailModel(currentPage)
            {                
                PID = (int)pid
            };
            return View("~/Views/Portfolio/Detail.cshtml", pageModel);
        }
}

Until here everything works great. I can do further rendering of my page in detail.cshtml. 

Now i have another kinda virtual page (from another source) which has the same kinda url pattern only it should match on another action called EPIB. So now my routing is extended like so.

routes.MapContentRoute(
                  "Portfolio", "{language}/{node}/{action}/{name}/{pid}",
                  new { action = "showcase"});

            routes.MapContentRoute(
                    "EPIB", "{language}/{node}/{action}/{name}/{sid}",
                    new { action = "epib" });

Now urls like http://abc.local/about/epib/a-new-case-epib/980  i would like to be hit by the new route and handled in the controller by its own action.

the controllers now is extended like so:

public abstract class PageControllerBase : PageController where T : SitePageData
    {
        
        public ActionResult Showcase(SitePageData currentPage, string name, int? pid)
        {
            if (pid == null)
            {
                return HttpNotFound();
            }
            var pageModel = new PortfolioDetailModel(currentPage)
            {                
                PID = (int)pid
            };
            return View("~/Views/Portfolio/Detail.cshtml", pageModel);
        }

 public ActionResult Epib(SitePageData currentPage, string name, int? sid)
        {
            if (sid == null)
            {
                return HttpNotFound();
            }
            var pageModel = new EPIBDetailModel(currentPage)
            {
                SID = (int)sid
            };
            return View("~/Views/EPIB/Detail.cshtml", pageModel);
        }
}

The problem as you can now see is that if i call my EPIB route, my sid is always null. If i switch my routes and put the EPIB route first then it works, but then my Showcase route doesnt work.. 

Am i missing something?

#122676
Jun 10, 2015 9:32
Vote:
 

In asp.net mvc, if you have two routes with the same number of parameters and no constraints, first route will be called.

Can you check this thread: http://world.episerver.com/Modules/Forum/Pages/Thread.aspx?id=121971

#122682
Jun 10, 2015 9:53
Vote:
 

Hi Dejan, i checked your thread, and i found your last post there interesting.. about adding constraints. 

But i don't quite follow how to apply constraints in my case. How can i differentiate between my routes. As you can see in my PageContollerBase. i have my actions defined which are hit using matching url pattern.

I'm running latest EPiServer 8.8.x version at the moment.

Hope you can give some more insight in my case.

#122691
Jun 10, 2015 13:46
Vote:
 

Hi,

Something like this?

public class CustomConstraint : IContentRouteConstraint
{
    private readonly Type _contentType;
    private readonly string _actionName;


    private readonly IContentLoader _contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();

    public CustomConstraint(Type contentType, string actionName)
    {
        _contentType = contentType;
        _actionName = actionName;
    }

    public bool Match(Route route, SegmentContext segmentContext, string parameterName)
    {
        if (ContentReference.IsNullOrEmpty(segmentContext.RoutedContentLink))
        {
            return false;
        }

        var content = _contentLoader.Get<IContent>(segmentContext.RoutedContentLink);

        // constraint 1: content types must match
        if (_contentType.IsInstanceOfType(content))
        {
            // constraint 2: actions must match
            if (segmentContext.RouteData.Values.ContainsKey("action") &&
                string.Equals(segmentContext.RouteData.Values["action"] as string, _actionName,
                                StringComparison.CurrentCultureIgnoreCase))
            {
                return true;
            }
        }

        return false;
    }
}

[InitializableModule]
[ModuleDependency(typeof(ServiceContainerInitialization))]
public class CustomRoutingModule : IInitializableModule
{
    public void RegisterRoutes(RouteCollection routes)
    {
        RouteTable.Routes.MapContentRoute(null, "{language}/{node}/{action}/{name}/{pid}",
                                            new
                                            {
                                                action = "Index",
                                                name = UrlParameter.Optional,
                                                pid = UrlParameter.Optional
                                            },
                                            new MapContentRouteParameters
                                            {
                                                Constraints = new
                                                {
                                                    x = new CustomConstraint(typeof(SitePageData), "showcase")
                                                }
                                            });

        RouteTable.Routes.MapContentRoute(null, "{language}/{node}/{action}/{name}/{sid}",
                                            new
                                            {
                                                action = "Index",
                                                name = UrlParameter.Optional,
                                                sid = UrlParameter.Optional
                                            },
                                            new MapContentRouteParameters
                                            {
                                                Constraints = new
                                                {
                                                    x = new CustomConstraint(typeof(SitePageData), "epib")
                                                }
                                            });
    }

    public void Initialize(InitializationEngine context)
    {
        RegisterRoutes(RouteTable.Routes);
    }

    public void Uninitialize(InitializationEngine context)
    {
    }

    public void Preload(string[] parameters)
    {
    }
}

I'm sure there's a much more easier / epi-friendly way to solve this, but this should do the trick in the meantime :)

#122695
Edited, Jun 10, 2015 14:36
Vote:
 

awesome dejan...

i will dive into what exactly we are doing here, but i implemented this and it works... why do we have the action="Index" now for the routes? I thought the action name must match the action in my controller... How does that work?

#122697
Jun 10, 2015 15:21
Vote:
 

C/p, sorry :)

You can remove action="Index", you don't need it (at least in version 8.8.1). It seems that action="something" doesn't have any effect here.

#122698
Jun 10, 2015 15:32
Vote:
 

i thought so... thanks for getting back... :-)

#122702
Jun 10, 2015 15:38
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.