London Dev Meetup Rescheduled! Due to unavoidable reasons, the event has been moved to 21st May. Speakers remain the same—any changes will be communicated. Seats are limited—register here to secure your spot!

Partial Routing {action} is ignored

Vote:
 

Does partial routing supports {action} segment?

I did following:

public class SearchPagePartialRouter : IPartialRouter<PageData, SearchPage>
    {
        public object RoutePartial(PageData content, SegmentContext segmentContext)
        {
            if (ContentReference.StartPage == content.ContentLink)
            {
                var segment = segmentContext.GetNextValue(segmentContext.RemainingPath);

                if (string.Equals("search", segment.Next))
                {
                    segmentContext.RemainingPath = segment.Remaining;
                    return new SearchPage();//that is my non-cms content
                }
            }

            return null;
        }
    }


public class SearchPageController : Controller, IRenderTemplate<SearchPage>
    {
        public ViewResult Index()
        {
           //I have this action executed by url http://<host>/en/search/
        }

        public ViewResult Second()
        {
           //I have 404 response by accessing url http;//<host>/en/search/second
        }
    }

My url does match following "default" route: {language}/{node}/{partial}/{action}.
But if I try to run
http://<hostname>/en/search/second
{node} = start page
{partial} = search
{action} = second

I have 404 response/
Why {action} segment is ignored for my partial route?


    

    

 

#82127
Mar 05, 2014 17:39
Vote:
 

Just to clarify SearchPage is my custom object, not a cms page.

#82132
Mar 05, 2014 18:19
Vote:
 

Looks like problem is inside ExistingActionRouteConstraint constraint.

Reflected code:

public virtual bool Match(Route route, SegmentContext routingContext, string parameterName)
{
    IContent content;
    string str = routingContext.RouteData.Values["action"] as string;
    if (string.IsNullOrEmpty(str))
    {
        return true;
    }
    if (!this._mapContentRouteParameters.ContentLoader.TryGet<IContent>(routingContext.RoutedContentLink, LanguageSelector.MasterLanguage(), out content))
    {
        return false;
    }
    TemplateModel model = this._mapContentRouteParameters.TemplateResolver.Resolve(HttpContext.Current.ContextBaseOrNull(), content, TemplateTypeCategories.MvcController, routingContext.ContextMode);
    if ((model != null) && (model.TemplateType != null))
    {
        return this._actions.GetControllerActions(model.TemplateType).Contains(str);
    }
    string a = (route.Defaults[RoutingConstants.ActionKey] as string) ?? "index";
    return string.Equals(a, str, StringComparison.OrdinalIgnoreCase);
}

It does action lookup not on actual routed data, but on "parent" content.

routingContext.RoutedContentLink

Is it a valid behaviour?

 

 
#82134
Mar 05, 2014 19:29
Vote:
 

If I add "second" (with same name) STUB action on "parent" cms-content controller, it starts working:)

Looks like a bug.

#82135
Edited, Mar 05, 2014 19:36
Vote:
 

Here is workaroung I've found:

    public class Global : EPiServer.Global
    {
        protected override void RegisterRoutes(RouteCollection routes)
        {
            base.RegisterRoutes(routes);
            //go through content routes and apply constraint fix
            FixExistingActionConstraint(routes);
        }

        private static void FixExistingActionConstraint(RouteCollection routes)
        {
            foreach (var route in routes)
            {
                var contentRoute = route as ContentRoute;
                if (contentRoute != null && contentRoute.Constraints.ContainsKey(RoutingConstants.ActionKey))
                {
                    var original = contentRoute.Constraints[RoutingConstants.ActionKey] as ExistingActionRouteConstraint;
                    if (original != null)
                    {
                        //replace constraint with custom wrapper
                        contentRoute.Constraints[RoutingConstants.ActionKey] = new ExistingActionRouteConstraintWrapper(original);
                    }
                }
            }
        }
    }

    public class ExistingActionRouteConstraintWrapper : IContentRouteConstraint
    {
        private readonly ExistingActionRouteConstraint original;

        public ExistingActionRouteConstraintWrapper(ExistingActionRouteConstraint original)
        {
            this.original = original;
        }

        public bool Match(Route route, SegmentContext segmentContext, string parameterName)
        {
            //try to minify "fix" influence and process only "my" partials routes (objects are marked with IVirtualPage interface)
            if (segmentContext.RoutedObject is IVirtualPage)
            {
                return true;
            }

            return original.Match(route, segmentContext, parameterName);
        }
    }

    

Does it make sense? Any pitfalls?

#82139
Mar 05, 2014 21:03
Vote:
 

Hi and thanks for the detailed explanation!

I agree that this should be considered a bug. That is if a partial router has routed to something the validation should be against that controller (if any) and not the "original" cms content.

I have reported a bug for this.

#82170
Mar 06, 2014 11:26
Vote:
 

Fixed in 7.6.5, release note: http://world.episerver.com/Documentation/Release-Notes/ReleaseNote/?releaseNoteId=112923

#85183
Apr 16, 2014 16:01
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.