[Из песочницы] Umbraco CMS MVC — собственный контроллер и красивые url
CMS Umbraco я изучаю несколько месяцев. Так получилось, что основное приложение моих исследовательских усилий направлено на последнюю на сей день 7-ю версию этой системы, причём в контексте её работы на MVC-движке, как альтернативе веб-формам прежних версий.В какой-то момент, разрабатывая сайт одной компании, мне захотелось вынести репозиторий новостей этой компании в отдельный элемент верхнего уровня в content tree. В примерах и видеоинструкциях, размещённых на сайте Umbraco, предлагается самый простой вариант такой организации — новости хранятся как дочерние элементы какого-нибудь пункта меню (рис. 1). В качестве примера в разделе «С какой стороны подойти к Umbraco» такой подход оправдывается, но на живом сайте, где новостей будут десятки и сотни, это выглядит довольно неуклюже. Чтобы работать с новостями, редактору сайта нужно будет опускаться вглубь по дереву контента — Главная-Новости-ОтдельнаяНовость. Да и сама концепция такого подхода меня не очень устраивает — в разделе пунктов многоуровневого меню вдруг появляется список новостей…
Более предпочтительным выглядит другой вариант, как на рис. 2 — узел, содержащий новости, является отдельным элементом верхнего уровня дерева контента.
Более того — при организации доступа к новостям по ссылке, захотелось, чтобы они имели красивый канонический вид:
/News — список всех новостей; /News/1234 — вывод отдельной новости; /News/Page12 — переход на страницу списка новостей. Парадигма MVC для решения данной задачи предполагает наличие контроллера и представления (модель в данном случае не используем). Поскольку всё выполняется «под крылом» Umbraco, мы не можем взять и внедрить контроллер новостей, наследуя его от класса Controller — ничего не произойдёт. Документация по Umbraco предлагает наследоваться от специально существующего в инфраструктуре данной CMS класса SurfaceController. Всё бы хорошо, но в таком случае ссылки, по которым вызываются действия контроллера, приобретают вид »/umbraco/surface/_controllername_/_actionname_». Такая структура url выглядит довольно громоздкой, да и с точки зрения поисковой индексации страница с подобным url воспринимается как глубоко упрятанная в структуре сайта, что, вероятно, понижает её поисковую значимость.Исследование данного вопроса привело к следующему решению — наследоваться нужно не от класса SurfaceController, а от PluginController. В этом случае ссылки генерируются по канонам MVC, так, как и требуется. Однако при этом необходимо учитывать некоторые дополнительные обстоятельства. Ниже предоставлено полное решение данной задачи. Итак:
РоутыРоуты прописываются не в файле App_Start/RouteConfig.cs, который стандартно вызывается как RouteConfig.RegisterRoutes (RouteTable.Routes) в Global.asax. В Umbraco MVC для классов PluginController роуты прописываются в файле App_Code/Startup.cs. В этом файле объявляем класс, имплементирующий интерфейс IApplicationEventHandler. Выглядит это так: 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) { } } Контроллер Контроллер создаётся обычным образом в файле, расположенном в папке Controllers. Как было сказано, класс контроллера наследует класс PluginController. Выглядит это примерно так: 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;
}
}
Здесь требуется одно уточнение. Код поиска узлов с новостями возвращает объект типа интерфейса IPublishedContent или IEnumerable