tutorial - mvc c# windows forms
Sitecore, controladores MVC personalizados y rutas (3)
Tengo un sitio web definido en las definiciones de sitios de Sitecore. La ruta de acceso es /localhost/mysite/home
. Y funciona.
Necesito crear un controlador personalizado para enviar formularios con una API sin pasar por Sitecore. Así que tengo FormsController
(heredando del controlador MVC) con una acción llamada "Test" sin parámetros.
Definí la ruta en la tubería de inicialización como esta:
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" });
}
}
La ruta se agrega correctamente a la tabla de rutas y está allí cuando la depuro. Ahora, cuando trato de llamar al método "prueba", la ruta no se encuentra y el depurador no llega al punto de interrupción en la acción.
Estoy probando diferentes rutas:
-
/localhost/mysite/home/forms/test
-
/localhost/forms/test
(sitio web predeterminado)
Pero sin suerte hasta el momento.
---- ACTUALIZACIÓN ---
Al profundizar en esto, noté que hay algo mal con el comportamiento de Sitecore. Se supone que el procesador TransferRoutedRequest
interrumpe la tubería httpRequestBegin
, devolviendo el control a MVC, en caso de que el elemento de contexto sea nulo (simplificando). Ocurre después de algunos controles, entre los que se encuentra uno en los datos de RoutTable. Pero la llamada a RouteTable.Routes.GetRouteData
devuelve siempre nulo, lo que hace que el procesador regrese sin interrumpir la canalización. Lo anulé para que abortara la canalización correctamente, pero aun así, incluso si llama al método args.AbortPipeline()
, la args.AbortPipeline()
no se args.AbortPipeline()
y la ruta no se resuelve.
Así es como se veía TransferRoutedRequest
original:
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();
}
}
y así es como lo anulé:
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);
}
}
Aquí está el código que creará una ruta para usted. En global.asax.cs, llamará a RegisterRoutes desde el controlador de eventos App_Start:
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Y allí usted especifica su ruta como:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "test",
url: "mvc/Forms/{action}/{id}",
defaults: new { controller = "Forms", action = "Test", id = UrlParameter.Optional }
);
}
En este caso, tendrá / mvc / prefix que manejará su ruta para especificar el controlador, por lo que lo llamará así:
/mvc/Forms/Test/{you_may_pass_some_optional_GUID_here}
Esto dirigirá al método de acción de clase FormsController Prueba (ID de cadena) pero puede omitir el parámetro de id.
Aquí hay un ejemplo de trabajo tomado de uno de mis proyectos.
Registro de ruta personalizada:
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 }
);
}
}
}
Inicializar la configuración de Pipeline:
<?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>
Finalmente lo tengo funcionando correctamente. Como escribí TransferRoutedRequest
no funcionaba como se esperaba, así que tuve que anularlo. Aún así, incluso si funcionó como se supone que debía hacerlo, la ruta no se estaba resolviendo. El problema estaba en la configuración de la tubería. Gracias a un colega, abrí la herramienta de tuberías de SitecoreRocks y mostró que la tubería estaba registrada en una posición demasiado alejada de donde debería haber estado, por lo que nunca se golpeó (la había registrado después de ItemResolver
, como estaba en la configuración original) . Lo LayoutResolver
antes de LayoutResolver
y eso fue lo que hizo. La ruta fue resuelta. Aún así, Sitecore no pudo crear una instancia del tipo, como lo fue en otro ensamblaje. Incluso la especificación del espacio de nombres del controlador no resolvió el problema. Así que tuve que hacer algunas modificaciones y anular el método CreateController de la clase ControllerFactory e invalidar el procesador InitializeControllerFactory (que ya había modificado para poder trabajar con un contenedor DI), escribiendo un nuevo ControllerFactory
y un nuevo SitecoreControllerFactory
.
El código final se ve así:
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);
}
En caso de que trate con un elemento de Sitecore, uso TypeHelper
para devolver el tipo de controlador actual desde una representación o diseño de controlador. De lo contrario, uso DefaultControllerFactory.GetControllerType
para resolver la ruta personalizada.
Lo único que debe tener cuidado: en caso de que 2 o más espacios de nombres tengan un controlador con el mismo nombre y acción, es obligatorio agregar un espacio de nombres para identificarlos.