📜 ⬆️ ⬇️

Umbraco CMS MVC - own controller and beautiful url

CMS Umbraco I have been studying for several months. It so happened that the main application of my research efforts was directed to the latest 7th version of this system to date, moreover in the context of its work on the MVC engine, as an alternative to the web forms of previous versions.

At some point, while developing the site of one company, I wanted to put the news repository of this company into a separate top-level element in the content tree. In the examples and video instructions posted on the Umbraco website, the simplest version of such an organization is offered - news is stored as children of some menu item (Fig. 1). As an example, in the section “From which side to approach Umbraco”, this approach is justified, but on a live site, where there are tens and hundreds of news, it looks rather clumsy. To work with the news, the site editor will need to go deep into the content tree - Home-News-SeparateNews . And the very concept of such an approach does not suit me very much - a list of news suddenly appears in the section of items on the multilevel menu ...


image
')
Another option looks more preferable, as in Figure 2 - the node containing news is a separate top-level element of the content tree.

image

Moreover, when organizing access to news by reference, I wanted them to have a beautiful canonical appearance:


The MVC paradigm for solving this problem assumes the presence of a controller and a view (in this case, the model is not used). Since everything is done “under the wing” of Umbraco, we cannot take and implement the news controller, inheriting it from the Controller class - nothing will happen. Documentation on Umbraco suggests to inherit from the SurfaceController class that specifically exists in the infrastructure of this CMS. Everything would be fine, but in this case, the links for which the controller's actions are called take the form " / umbraco / surface / _controllername _ / _ actionname_ ". Such a url structure looks rather cumbersome, and from the point of view of search indexing, a page with a similar url is perceived as deeply hidden in the structure of the site, which probably reduces its search significance.

The study of this issue led to the following solution - you need to inherit not from the SurfaceController class, but from PluginController . In this case, links are generated by MVC canons, as required. However, it is necessary to take into account some additional circumstances. Below is a complete solution to this problem. So:

Routs


Routes are not registered in the App_Start / RouteConfig.cs file, which is normally called RouteConfig.RegisterRoutes (RouteTable.Routes) in Global.asax . In Umbraco MVC for PluginController classes, routes are written in the file App_Code / Startup.cs . In this file, we declare a class implementing the IApplicationEventHandler interface. It looks like this:

public class MyStartupHandler : IApplicationEventHandler { public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { //Create a custom routes // News controller RouteTable.Routes.MapRoute( "", "News", new { controller = "News", action = "Index", page = 1 }); RouteTable.Routes.MapRoute( "", "News/Index", new { controller = "News", action = "Index", page = 1 }); RouteTable.Routes.MapRoute( "", "News/Page{page}", new { controller = "News", action = "Index", page = UrlParameter.Optional }); RouteTable.Routes.MapRoute( "", "News/{id}", new { controller = "News", action = "News", id = UrlParameter.Optional }); } public void OnApplicationInitialized( UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { } public void OnApplicationStarting( UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { } } 


Controller


The controller is created in the usual way in a file located in the Controllers folder. As mentioned, the controller class inherits the PluginController class. It looks like this:

 public class NewsController : PluginController { public NewsController() : this(UmbracoContext.Current) { } public NewsController(UmbracoContext umbracoContext) : base(umbracoContext) { } public ActionResult Index(string id) { /*   ,            -   ,     .. */ return View("News", CreateRenderModel(renderModel)); } private RenderModel CreateRenderModel(IPublishedContent content) { var model = new RenderModel(content, CultureInfo.CurrentUICulture); //add an umbraco data token so the umbraco view engine executes RouteData.DataTokens["umbraco"] = model; return model; } } 

This requires one clarification. The news node search code returns an IPublishedContent or IEnumerable <IPublishedContent> interface type object. However, Umbraco requires that views called from PluginController 'a be strongly typed and accept a RenderModel type model. To do this, the controller declares the private method CreateRenderModel , which from IPublishedContent creates an object of the required type.

Representation


The representation is created according to the standard scheme, there are no nuances in terms of solving this problem.

Side effect


The use of this approach revealed one side effect, namely, the impossibility of using macros in content on pages that are invoked in this way. An error occurs with the definition of “ PublishedContentRequest missing ”. As I understand it, it is connected with the fact that the document displayed through such a controller does not go through all the stages of parsing Umbraco, in the process of which this most PublishedContentRequest is created, from which the macro generation code is repelled. It is comforting that working with macros when using MVC partial views becomes less popular. In addition, the creators of Umbraco themselves say that the code that implements the macro call from Rich-text-content is rather confused and bears the serious imprint of a heavy pre-emotic past ...

Links


Surface Controllers
Custom MVC routing in Umbraco

Source: https://habr.com/ru/post/227683/


All Articles