What you need to do is to replace the segment that handles {action}. By default {action} is handled by ParameterSegment which always will consume the current segment form the url. In your case you would like the segment handling action that it does not consume the segment but instead passes it on to next segment like {page} that handles paging.
So you need a different segment implementation that could look something like:
public class OptionalActionSegment : ParameterSegment
{
private IContentLoader _contentLoader;
public OptionalActionSegment(string name, IContentLoader contentLoader)
:base(name)
{
_contentLoader = contentLoader;
}
public override bool RouteDataMatch(SegmentContext context)
{
var segmentPair = context.GetNextValue(context.RemainingPath);
if (!string.IsNullOrEmpty(segmentPair.Next))
{
if (IsRequestForEventPage(context))
{
//do not consume segment just add default action
context.RouteData.Values[Name] = context.Defaults[Name];
}
else
{
context.RouteData.Values[Name] = segmentPair.Next;
context.RemainingPath = segmentPair.Remaining;
}
return true;
}
else if (context.Defaults.ContainsKey(Name))
{
context.RouteData.Values[Name] = context.Defaults[Name];
return true;
}
return false;
}
private bool IsRequestForEventPage(SegmentContext context)
{
IContent content = _contentLoader.Get<IContent>(context.RoutedContentLink);
return content is EPiServer.Templates.AlloyTech.PageTypes.EventItemPage;
}
}
Then you can register a route with your segment as:
protected override void RegisterRoutes(RouteCollection routes)
{
base.RegisterRoutes(routes);
var segment = new OptionalActionSegment("action", ServiceLocator.Current.GetInstance<IContentLoader>());
var routingParameters = new MapContentRouteParameters()
{
SegmentMappings = new Dictionary<string, ISegment>(),
UrlSegmentRouter = new DefaultUrlSegmentRouter(ServiceLocator.Current.GetInstance<IContentLoader>(),
new LanguageSelectorFactory(), ContentReference.StartPage, ServiceLocator.Current.GetInstance<IRoutingSegmentLoader>())
};
routingParameters.SegmentMappings.Add("action", segment);
routes.MapContentRoute(
name: "optionalaction",
url: "{language}/{node}/{partial}/{action}/{page}",
defaults: new { action = "index" },
parameters: routingParameters);
}
in your controller you can then get the paging value from RouteValues on Request context where it will be stored with key "page" (if that is the name of your pagining segment).
Thanks a lot for such a detailed answer! I will try this out, hopefully it will go smoooothly :)
// Edit: tried it, went smoothly :)
Hi, Johan,
Is there any other place I could register routes?
ServiceLocator.Current.GetInstance<IContentLoader>() gives me StructureMap exception when I call it in Global.asax. (I am using StructureMap in the solution, too)
I have fixed it finally, simply by adding an if statement before using IContentLoader and IRoutingSegmentLoader.
The code is now:
protected override void RegisterRoutes(RouteCollection routes)
{
base.RegisterRoutes(routes);
IContentLoader contentLoader;
ServiceLocator.Current.TryGetExistingInstance(out contentLoader);
IRoutingSegmentLoader routingSegmentLoader;
ServiceLocator.Current.TryGetExistingInstance(out routingSegmentLoader);
if (contentLoader != null && routingSegmentLoader != null)
{
var segment = new OptionalActionSegment("action", contentLoader);
var routingParameters = new MapContentRouteParameters()
{
SegmentMappings = new Dictionary<string, ISegment>(),
UrlSegmentRouter = new DefaultUrlSegmentRouter(
contentLoader,
new LanguageSelectorFactory(),
ContentReference.StartPage,
routingSegmentLoader)
};
routingParameters.SegmentMappings.Add("action", segment);
routes.MapContentRoute(
name: "optionalaction",
url: "{language}/{node}/{partial}/{action}/{page}",
defaults: new { action = "index" },
parameters: routingParameters);
}
}
Hi, guys,
If I have a pager on the page, is there a possibility to ditch the Index in the URL by defining a custom route, ie have localhost:12345/Events/2 instead of localhost:12345/Events/Index/2
Thank you in advance,
Marija