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

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.