Umbraco routes URLs for its content nodes automatically. However, Umbraco Cloud has a limit of 500 content nodes, so it's useful to limit those taken up by stoolball entities (leagues, teams, matches and so on). It's also useful to have access to the Umbraco context when displaying a stoolball entity.
The solution to this is to route all stoolball entity URLs through one Umbraco content node, based on the 'Stoolball router' document type. This content node must be created at the root of the Content section in the Umbraco back office.
In the Stoolball.Web.Routing
namespace ContentFinderComposer
registers StoolballRouteContentFinder
to run before Umbraco's default content finder. This means that if someone creates an Umbraco page that conflicts with a route reserved for a type of stoolball entity, the stoolball route wins.
When StoolballRouteContentFinder
recognises a route reserved for a stoolball entity it sets the 'Stoolball router' content node to respond. StoolballRouterController
takes over, matching the action in the route data to a controller appropriate for the type of stoolball entity, and passes responsibility for the response entirely to that controller.
Responses for stoolball entities are designed to be as similar to a standard Umbraco response as possible. They have access to the Umbraco context as well as the opportunity to read their data from the custom tables in the Umbraco database.
The model behaves similarly to a published content model generated by Models Builder. It should inherit from Stoolball.Web.Routing.BaseViewModel
and requires a constructor that accepts an IPublishedContent
.
using Stoolball.Web.Routing;
using Umbraco.Core.Models.PublishedContent;
namespace Stoolball.Web.Example
{
public class ExampleViewModel : BaseViewModel
{
public ExampleViewModel(IPublishedContent contentModel) : base(contentModel)
{
}
/// ...other properties here
}
}
The view is a normal Umbraco template. Set it as an allowed template on the 'Stoolball router' document type in the Umbraco back office.
@inherits Umbraco.Web.Mvc.UmbracoViewPage<Stoolball.Web.Example.ExampleViewModel>
The controller is a normal Umbraco controller except that it expects the Index
action to be asynchronous. It should inherit from Stoolball.Web.Routing.RenderMvcControllerAsync
and implement its abstract Index
method. It has access to all the usual Umbraco controller properties, such as the UmbracoHelper
, MembershipHelper
and ServiceContext
:
using Stoolball.Web.Routing;
using Stoolball.Web.Security;
using System.Threading.Tasks;
using System.Web.Mvc;
using Umbraco.Web.Models;
namespace Stoolball.Web.Example
{
public class ExampleController : RenderMvcControllerAsync
{
[HttpGet]
[ContentSecurityPolicy]
public override async Task<ActionResult> Index(ContentModel contentModel)
{
var model = new ExampleViewModel(contentModel.Content);
// ...use `await` to fetch data from the database asynchonously
return CurrentTemplate(model);
}
}
}
Finally, wire up this controller to be served via StoolballRouterController
as described above:
-
Register the controller type in
Stoolball.Web.DependencyInjectionComposer
:public void Compose(Composition composition) { /// ...other registrations composition.Register<ExampleController>(Lifetime.Request); }
-
Add a new value to the
Stoolball.Web.Routing.StoolballRouteType
enum, matching the alias of the template (case-insensitive). -
Update
StoolballRouteContentFinder
to recognise the new route. -
Add the new
StoolballRouteType
value and its matching controller type to the dictionary inStoolballRouteTypeMapper
:private readonly Dictionary<StoolballRouteType, Type> _supportedControllers = new Dictionary<StoolballRouteType, Type> { // ...other route types and controllers { StoolballRouteType.Example, typeof(ExampleController) } };