Sitecore, настраиваемые контроллеры MVC и маршруты

У меня есть веб-сайт, определенный в определениях сайтов Sitecore. Путь к нему /localhost/mysite/home. И это работает.

Мне нужно создать собственный контроллер для отправки форм с API в обход Sitecore. Итак, у меня есть FormsController (унаследованный от контроллера MVC) с действием под названием «Тест», не имеющим параметров.

Я определил маршрут в конвейере инициализации следующим образом:

public class Initialize
{
    public void Process(PipelineArgs args)
    {
        MapRoutes();
        GlassMapperSc.Start();
    }

    private void MapRoutes()
    {
        RouteTable.Routes.MapRoute(
                "Forms.Test", 
                "forms/test", 
                new
                {
                    controller = "FormsController",
                    action = "Test"
                },
                new[] { "Forms.Controller.Namespace" });
     }
}

Маршрут правильно добавлен в таблицу маршрутов, и он там, когда я его отлаживаю. Теперь, когда я пытаюсь вызвать метод test, маршрут не найден, и отладчик не достигает точки останова в действии.

Пробую разные маршруты:

  • /localhost/mysite/home/forms/test
  • /localhost/forms/test (веб-сайт по умолчанию)

Но пока безуспешно.

---- ОБНОВЛЕНИЕ ---

Заглянув глубже, я заметил, что что-то не так с поведением Sitecore. Предполагается, что процессор TransferRoutedRequest прервет конвейер httpRequestBegin, вернув управление MVC, в случае, если элемент контекста равен нулю (упрощение). Это происходит после некоторых проверок, среди которых есть проверка данных RoutTable. Но вызов RouteTable.Routes.GetRouteData всегда возвращает null, что заставляет процессор возвращаться без прерывания конвейера. Я переопределил его, чтобы он правильно прервал конвейер, но все же, даже если он вызывает метод args.AbortPipeline(), конвейер не прерывается и маршрут не разрешается.

так выглядел оригинал TransferRoutedRequest:

public class TransferRoutedRequest : HttpRequestProcessor
{
  public override void Process(HttpRequestArgs args)
  {
    Assert.ArgumentNotNull((object) args, "args");
    RouteData routeData = RouteTable.Routes.GetRouteData((HttpContextBase) new HttpContextWrapper(HttpContext.Current));
    if (routeData == null)
      return;
    RouteValueDictionary routeValueDictionary = ObjectExtensions.ValueOrDefault<Route, RouteValueDictionary>(routeData.Route as Route, (Func<Route, RouteValueDictionary>) (r => r.Defaults));
    if (routeValueDictionary != null && routeValueDictionary.ContainsKey("scIsFallThrough"))
      return;
    args.AbortPipeline();
   }
}

и вот как я это отменил:

public class TransferRoutedRequest : global::Sitecore.Mvc.Pipelines.HttpRequest.TransferRoutedRequest
{
    public override void Process(HttpRequestArgs args)
    {
        if (Context.Item == null || Context.Item.Visualization.Layout == null)
            args.AbortPipeline();
        else
            base.Process(args);
    }
}

person Hellraiser    schedule 22.07.2015    source источник


Ответы (3)


Вот рабочий пример из одного из моих проектов.

Пользовательская регистрация маршрута:

namespace Test.Project.Pipelines.Initialize
{
    public class InitRoutes : Sitecore.Mvc.Pipelines.Loader.InitializeRoutes
    {
        public override void Process(PipelineArgs args)
        {
            RegisterRoutes(RouteTable.Routes);
        }

        protected virtual void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                "Test", // Route name
                "api/test/{controller}/{action}/{id}", // URL with parameters
                 new { id = UrlParameter.Optional }
                );
        }
    }
}

Инициализировать конфигурацию конвейера:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
     <pipelines>
         <initialize>
            <processor type="Test.Project.Pipelines.Initialize.InitRoutes, Test.Project"
         patch:after="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc']" />
        </initialize>
     </pipelines>
  </sitecore>
</configuration>
person Martin English    schedule 26.07.2015

Вот код, который создаст для вас маршрут. В global.asax.cs вы вызовете RegisterRoutes из обработчика событий App_Start:

    protected void Application_Start()
    {
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }

И там вы указываете свой маршрут как:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.MapRoute(
             name: "test",
             url: "mvc/Forms/{action}/{id}",
             defaults: new { controller = "Forms", action = "Test", id = UrlParameter.Optional }
           );
    }

В этом случае у вас будет префикс / mvc /, который будет обрабатывать ваш маршрут для указания контроллера, поэтому вы назовете его как:

/mvc/Forms/Test/{you_may_pass_some_optional_GUID_here}

Это приведет к переходу к методу действия класса FormsController Test (строковый идентификатор), но вы можете опустить параметр id

person Martin Miles    schedule 22.07.2015
comment
Вы воспроизводите мой (неработающий) пример с добавлением НЕПРАВИЛЬНОЙ практики: изменить метод Application_Start. У меня нет доступа к глобальному asax, и даже если бы он был, я бы его не трогал. Правильный способ сделать это - через конвейер инициализации, добавив конфигурацию в папку App_Config / include. Или, как второй вариант, украсив свой собственный класс init с помощью assembly: PreApplicationStartMethod - person Hellraiser; 22.07.2015
comment
Конечно, я понимаю, что лучше избегать модификаций global.asax.cs по крайней мере для целей обслуживания - sirchicken.blogspot.co.uk/2014/07/ - я просто упростил его для понимания. Пример сниппета - это рабочий код, я его заранее протестировал. - person Martin Miles; 22.07.2015
comment
Вы читали, что я написал? Это тот же пример, который вы написали, но он не работает для меня. Итак, суть в следующем: я знаю, как работает обычный метод, но в моем случае он не работает. Так какой смысл отвечать обычным методом? Более того: вы создали новую конфигурацию сайта в sitecore? Если да, то проверяли ли вы свой метод на новом созданном веб-сайте? Я так не думаю ... - person Hellraiser; 22.07.2015
comment
Дорогой восставший из ада, @MartinMiles пытается тебе помочь, а ты недобрый. На вашем месте я бы не ожидал, что кто-то еще попытается помочь - person Marek Musielak; 24.07.2015
comment
Дорогие Марек и Мартин. Мне жаль. Я не хотел показаться грубым. Но я отправляю здесь сообщение только тогда, когда у меня есть настоящая проблема. Как я уже указывал, я прекрасно знаю, как это должно работать. Но по неизвестной причине это не так. Более того, целью StackOverflow должно быть не только решение проблем, но и научить людей делать это правильно. Я заметил, что Мартин написал неправильное упражнение, и сообщил ему об этом. Иногда здесь сложно понять чьи-то намерения и настроение. Так что если я кого-то обидел, прошу прощения. Я не собирался этого делать. - person Hellraiser; 27.07.2015

Наконец-то он работает правильно. Как я уже писал, TransferRoutedRequest работает не так, как ожидалось, поэтому мне пришлось переопределить это. Тем не менее, даже если это сработало, как предполагалось, маршрут не был определен. Проблема заключалась в конфигурации трубопровода. Благодаря коллеге я открыл инструмент конвейера SitecoreRocks, и он показал, что конвейер был зарегистрирован в позиции слишком далеко от того места, где он должен был быть, поэтому он никогда не был задействован (я зарегистрировал его после ItemResolver, как это было в исходной конфигурации) . Я пропатчил его до LayoutResolver, и это помогло. Маршрут был разрешен. Тем не менее, Sitecore не смог создать экземпляр типа, как это было в другой сборке. Даже указание пространства имен контроллера не решило проблему. Поэтому мне пришлось внести некоторые изменения и переопределить метод CreateController класса ControllerFactory и переопределить процессор InitializeControllerFactory (который я уже модифицировал для работы с контейнером DI), написав новый ControllerFactory и новый SitecoreControllerFactory.

Окончательный код выглядит так:

public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller = SC.Context.Item == null || SC.Context.Item.Visualization.Layout == null
                        ? base.GetControllerType(requestContext, controllerName)
                        : TypeHelper.GetType(controllerName);

        return GetControllerInstance(requestContext, controller);
    }

В случае, если я имею дело с элементом Sitecore, я использую TypeHelper для возврата текущего типа контроллера из рендеринга или макета контроллера. В противном случае я использую DefaultControllerFactory.GetControllerType для разрешения настраиваемого маршрута.

Единственное, с чем следует быть осторожным: в случае, если в 2 или более пространствах имен есть контроллер с тем же именем и действием, обязательно добавить пространство имен для их идентификации.

person Hellraiser    schedule 27.07.2015