paginas net component asp asp.net-core asp.net-core-mvc asp.net-core-1.0 asp.net-core-2.1

component - ¿Cómo especificar la ubicación de la vista en asp.net core mvc cuando se usan ubicaciones personalizadas?



paginas razor (4)

Digamos que tengo un controlador que utiliza enrutamiento basado en atributos para manejar una url solicitada de / admin / product como:

[Route("admin/[controller]")] public class ProductController: Controller { // GET: /admin/product [Route("")] public IActionResult Index() { return View(); } }

Ahora, digamos que me gustaría mantener mis vistas organizadas en una estructura de carpetas que refleje aproximadamente las rutas URL con las que están relacionadas. Así que me gustaría que la vista de este controlador se encuentre aquí:

/Views/Admin/Product.cshtml

Para ir más lejos, si tuviera un controlador como este:

[Route("admin/marketing/[controller]")] public class PromoCodeListController: Controller { // GET: /admin/marketing/promocodelist [Route("")] public IActionResult Index() { return View(); } }

Me gustaría que el framework busque automáticamente su vista aquí:

Views/Admin/Marketing/PromoCodeList.cshtml

Idealmente, el enfoque para informar el marco de la ubicación de la vista funcionaría de manera general en función de la información de ruta basada en atributos, independientemente de cuántos segmentos de URL estén involucrados (es decir, qué tan profundamente anidado está).

¿Cómo puedo indicarle al marco Core MVC (actualmente estoy usando RC1) que busque la vista del controlador en tal ubicación?


Buenas noticias ... En ASP.NET Core 2 (EDIT: también confirmado que funciona en 2.1), ya no necesita un ViewEngine personalizado o incluso ExpandViewLocations.

En startup.cs, en ConfigureServices, puedes agregar algo como esto, que utilicé para implementar la Estructura de carpetas de funciones (lo prefiero mucho más que las convenciones predeterminadas):

services.Configure<RazorViewEngineOptions>(o => { // {2} is area, {1} is controller,{0} is the action o.ViewLocationFormats.Clear(); o.ViewLocationFormats.Add("/Controllers/{1}/Views/{0}" + RazorViewEngine.ViewExtension); o.ViewLocationFormats.Add("/Controllers/Shared/Views/{0}" + RazorViewEngine.ViewExtension); // Untested. You could remove this if you don''t care about areas. o.AreaViewLocationFormats.Clear(); o.AreaViewLocationFormats.Add("/Areas/{2}/Controllers/{1}/Views/{0}" + RazorViewEngine.ViewExtension); o.AreaViewLocationFormats.Add("/Areas/{2}/Controllers/Shared/Views/{0}" + RazorViewEngine.ViewExtension); o.AreaViewLocationFormats.Add("/Areas/Shared/Views/{0}" + RazorViewEngine.ViewExtension); });

¡Y eso es! No se requieren clases especiales.

Consejo adicional: si está utilizando ReSharper, puede notar que en algunos lugares ReSharper no puede encontrar sus puntos de vista y le da advertencias molestas. Para solucionarlo, ingrese el paquete Resharper.Annotations y en su startup.cs (o en cualquier otro lugar) agregue uno de estos atributos para cada una de sus ubicaciones de visualización:

[assembly: AspMvcViewLocationFormat("/Controllers/{1}/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Controllers/Shared/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Areas/{2}/Controllers/{1}/Views/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("/Controllers/Shared/Views/{0}.cshtml")]

Espero que esto le ahorre a algunas personas las horas de frustración que acabo de vivir. :)


En .net core puede especificar la ruta completa a la vista.

return View("~/Views/booking/checkout.cshtml", checkoutRequest);


Puede expandir las ubicaciones donde el motor de vista busca vistas implementando un expansor de ubicación de vista. Aquí hay un código de ejemplo para demostrar el enfoque:

public class ViewLocationExpander: IViewLocationExpander { /// <summary> /// Used to specify the locations that the view engine should search to /// locate views. /// </summary> /// <param name="context"></param> /// <param name="viewLocations"></param> /// <returns></returns> public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { //{2} is area, {1} is controller,{0} is the action string[] locations = new string[] { "/Views/{2}/{1}/{0}.cshtml"}; return locations.Union(viewLocations); //Add mvc default locations after ours } public void PopulateValues(ViewLocationExpanderContext context) { context.Values["customviewlocation"] = nameof(ViewLocationExpander); } }

Luego, en el método ConfigureServices(IServiceCollection services) en el archivo startup.cs, agregue el siguiente código para registrarlo en el contenedor IoC. Haga esto justo después de services.AddMvc();

services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new ViewLocationExpander()); });

Ahora tiene una forma de agregar cualquier estructura de directorios personalizada que desee a la lista de lugares donde el motor de visualización busca vistas y vistas parciales. Solo agréguelo a la string[] locations string[] . Además, puede colocar un archivo _ViewImports.cshtml en el mismo directorio o en cualquier directorio principal y se encontrará y combinará con sus vistas ubicadas en esta nueva estructura de directorios.

Actualizar:
Una cosa buena acerca de este enfoque es que proporciona más flexibilidad que el enfoque introducido más tarde en ASP.NET Core 2 (Gracias @BrianMacKay por documentar el nuevo enfoque). Entonces, por ejemplo, este enfoque de ViewLocationExpander permite no solo especificar una jerarquía de rutas para buscar vistas y áreas, sino también diseños y componentes de vista. También tiene acceso al ActionContext completo para determinar cuál podría ser una ruta adecuada. Esto proporciona mucha flexibilidad y poder. Así, por ejemplo, si desea determinar la ubicación de la vista adecuada evaluando la ruta de la solicitud actual, puede obtener acceso a la ruta de la solicitud actual a través de context.ActionContext.HttpContext.Request.Path .


Vas a necesitar un RazorviewEngine personalizado para este.

Primero, el motor:

public class CustomEngine : RazorViewEngine { private readonly string[] _customAreaFormats = new string[] { "/Views/{2}/{1}/{0}.cshtml" }; public CustomEngine( IRazorPageFactory pageFactory, IRazorViewFactory viewFactory, IOptions<RazorViewEngineOptions> optionsAccessor, IViewLocationCache viewLocationCache) : base(pageFactory, viewFactory, optionsAccessor, viewLocationCache) { } public override IEnumerable<string> AreaViewLocationFormats => _customAreaFormats.Concat(base.AreaViewLocationFormats); }

Esto creará un formato de área adicional, que coincide con el caso de uso de {areaName}/{controller}/{view} .

Segundo, registre el motor en el método ConfigureServices de la clase Startup.cs :

public void ConfigureServices(IServiceCollection services) { // Add custom engine (must be BEFORE services.AddMvc() call) services.AddSingleton<IRazorViewEngine, CustomEngine>(); // Add framework services. services.AddMvc(); }

En tercer lugar, agregue el enrutamiento de área a sus rutas MVC, en el método Configure :

app.UseMvc(routes => { // add area routes routes.MapRoute(name: "areaRoute", template: "{area:exists}/{controller}/{action}", defaults: new { controller = "Home", action = "Index" }); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });

Por último, cambie su clase ProductController para usar el AreaAttribute :

[Area("admin")] public class ProductController : Controller { public IActionResult Index() { return View(); } }

Ahora, la estructura de su aplicación puede verse así: