2016-09-01 11:25:00 +02:00
|
|
|
|
using System;
|
2016-09-01 19:06:08 +02:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
2016-09-01 11:25:00 +02:00
|
|
|
|
using System.Web.Http;
|
2016-09-01 19:06:08 +02:00
|
|
|
|
using System.Web.Http.Dispatcher;
|
|
|
|
|
|
using System.Web.Mvc;
|
2019-08-08 08:56:08 +01:00
|
|
|
|
using System.Web.Routing;
|
2016-09-01 11:25:00 +02:00
|
|
|
|
using Umbraco.Core;
|
2017-05-30 15:46:25 +02:00
|
|
|
|
using Umbraco.Core.Composing;
|
2017-12-28 09:27:57 +01:00
|
|
|
|
using Umbraco.Core.Configuration;
|
2020-04-03 11:03:06 +11:00
|
|
|
|
using Umbraco.Core.Hosting;
|
2019-12-20 17:36:44 +01:00
|
|
|
|
using Umbraco.Core.Strings;
|
2019-01-15 22:08:08 +11:00
|
|
|
|
using Umbraco.Core.IO;
|
2020-08-23 15:58:37 +02:00
|
|
|
|
using Umbraco.Web.Configuration;
|
2019-08-08 08:56:08 +01:00
|
|
|
|
using Umbraco.Web.Install;
|
2016-09-01 11:25:00 +02:00
|
|
|
|
using Umbraco.Web.Mvc;
|
|
|
|
|
|
using Umbraco.Web.WebApi;
|
2018-10-29 17:27:33 +11:00
|
|
|
|
|
2020-03-02 17:17:14 +01:00
|
|
|
|
using Constants = Umbraco.Core.Constants;
|
2017-05-30 18:13:11 +02:00
|
|
|
|
using Current = Umbraco.Web.Composing.Current;
|
2016-09-01 11:25:00 +02:00
|
|
|
|
|
2017-12-28 09:27:57 +01:00
|
|
|
|
namespace Umbraco.Web.Runtime
|
2016-09-01 11:25:00 +02:00
|
|
|
|
{
|
2019-03-05 08:47:31 +01:00
|
|
|
|
public sealed class WebInitialComponent : IComponent
|
2016-09-01 11:25:00 +02:00
|
|
|
|
{
|
2019-08-08 08:56:08 +01:00
|
|
|
|
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
|
|
|
|
|
private readonly SurfaceControllerTypeCollection _surfaceControllerTypes;
|
|
|
|
|
|
private readonly UmbracoApiControllerTypeCollection _apiControllerTypes;
|
2020-08-23 13:55:36 +02:00
|
|
|
|
private readonly IGlobalSettings _globalSettings;
|
2020-04-03 11:03:06 +11:00
|
|
|
|
private readonly IHostingEnvironment _hostingEnvironment;
|
2019-12-20 17:36:44 +01:00
|
|
|
|
private readonly IShortStringHelper _shortStringHelper;
|
2019-01-07 09:30:47 +01:00
|
|
|
|
|
2019-12-18 18:55:00 +01:00
|
|
|
|
public WebInitialComponent(
|
|
|
|
|
|
IUmbracoContextAccessor umbracoContextAccessor,
|
|
|
|
|
|
SurfaceControllerTypeCollection surfaceControllerTypes,
|
|
|
|
|
|
UmbracoApiControllerTypeCollection apiControllerTypes,
|
2020-08-23 13:55:36 +02:00
|
|
|
|
IGlobalSettings globalSettings,
|
2020-04-03 11:03:06 +11:00
|
|
|
|
IHostingEnvironment hostingEnvironment,
|
2020-04-02 21:19:42 +11:00
|
|
|
|
IShortStringHelper shortStringHelper)
|
2016-09-01 11:25:00 +02:00
|
|
|
|
{
|
2019-08-08 08:56:08 +01:00
|
|
|
|
_umbracoContextAccessor = umbracoContextAccessor;
|
|
|
|
|
|
_surfaceControllerTypes = surfaceControllerTypes;
|
|
|
|
|
|
_apiControllerTypes = apiControllerTypes;
|
2020-08-23 13:55:36 +02:00
|
|
|
|
_globalSettings = globalSettings;
|
2020-04-03 11:03:06 +11:00
|
|
|
|
_hostingEnvironment = hostingEnvironment;
|
2019-12-20 17:36:44 +01:00
|
|
|
|
_shortStringHelper = shortStringHelper;
|
2019-01-07 09:30:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Initialize()
|
2019-02-14 12:11:06 +01:00
|
|
|
|
{
|
2016-09-01 19:06:08 +02:00
|
|
|
|
// setup mvc and webapi services
|
|
|
|
|
|
SetupMvcAndWebApi();
|
|
|
|
|
|
|
|
|
|
|
|
// Disable the X-AspNetMvc-Version HTTP Header
|
|
|
|
|
|
MvcHandler.DisableMvcResponseHeader = true;
|
|
|
|
|
|
|
|
|
|
|
|
// wrap view engines in the profiling engine
|
|
|
|
|
|
WrapViewEngines(ViewEngines.Engines);
|
|
|
|
|
|
|
|
|
|
|
|
// add global filters
|
|
|
|
|
|
ConfigureGlobalFilters();
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
|
|
|
|
|
// set routes
|
2020-04-03 11:03:06 +11:00
|
|
|
|
CreateRoutes(_umbracoContextAccessor, _globalSettings, _shortStringHelper, _surfaceControllerTypes, _apiControllerTypes, _hostingEnvironment);
|
2016-09-01 19:06:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-07 09:30:47 +01:00
|
|
|
|
public void Terminate()
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2016-09-01 19:06:08 +02:00
|
|
|
|
private static void ConfigureGlobalFilters()
|
|
|
|
|
|
{
|
|
|
|
|
|
GlobalFilters.Filters.Add(new EnsurePartialViewMacroViewContextFilterAttribute());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// internal for tests
|
|
|
|
|
|
internal static void WrapViewEngines(IList<IViewEngine> viewEngines)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (viewEngines == null || viewEngines.Count == 0) return;
|
|
|
|
|
|
|
2018-12-12 14:28:57 +01:00
|
|
|
|
var originalEngines = viewEngines.ToList();
|
2016-09-01 19:06:08 +02:00
|
|
|
|
viewEngines.Clear();
|
|
|
|
|
|
foreach (var engine in originalEngines)
|
|
|
|
|
|
{
|
|
|
|
|
|
var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine);
|
|
|
|
|
|
viewEngines.Add(wrappedEngine);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-04 14:03:39 +01:00
|
|
|
|
private void SetupMvcAndWebApi()
|
2016-09-01 19:06:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
//don't output the MVC version header (security)
|
|
|
|
|
|
MvcHandler.DisableMvcResponseHeader = true;
|
|
|
|
|
|
|
|
|
|
|
|
// set master controller factory
|
|
|
|
|
|
var controllerFactory = new MasterControllerFactory(() => Current.FilteredControllerFactories);
|
|
|
|
|
|
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
|
|
|
|
|
|
|
|
|
|
|
|
// set the render & plugin view engines
|
2020-04-03 11:03:06 +11:00
|
|
|
|
ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment));
|
2016-09-01 19:06:08 +02:00
|
|
|
|
ViewEngines.Engines.Add(new PluginViewEngine());
|
|
|
|
|
|
|
|
|
|
|
|
//set model binder
|
2018-03-27 10:04:07 +02:00
|
|
|
|
ModelBinderProviders.BinderProviders.Add(ContentModelBinder.Instance); // is a provider
|
2016-09-01 19:06:08 +02:00
|
|
|
|
|
|
|
|
|
|
////add the profiling action filter
|
|
|
|
|
|
//GlobalFilters.Filters.Add(new ProfilingActionFilter());
|
|
|
|
|
|
|
|
|
|
|
|
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
|
|
|
|
|
|
new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration));
|
2016-09-01 11:25:00 +02:00
|
|
|
|
}
|
2018-03-27 10:04:07 +02:00
|
|
|
|
|
2019-08-08 08:56:08 +01:00
|
|
|
|
// internal for tests
|
|
|
|
|
|
internal static void CreateRoutes(
|
|
|
|
|
|
IUmbracoContextAccessor umbracoContextAccessor,
|
2020-08-23 13:55:36 +02:00
|
|
|
|
IGlobalSettings globalSettings,
|
2019-12-20 17:36:44 +01:00
|
|
|
|
IShortStringHelper shortStringHelper,
|
2019-08-08 08:56:08 +01:00
|
|
|
|
SurfaceControllerTypeCollection surfaceControllerTypes,
|
2020-01-07 13:08:21 +01:00
|
|
|
|
UmbracoApiControllerTypeCollection apiControllerTypes,
|
2020-04-03 11:03:06 +11:00
|
|
|
|
IHostingEnvironment hostingEnvironment)
|
2019-08-08 08:56:08 +01:00
|
|
|
|
{
|
2020-08-23 15:58:37 +02:00
|
|
|
|
var umbracoPath = ConfigModelConversions.ConvertGlobalSettings(globalSettings).GetUmbracoMvcArea(hostingEnvironment);
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
|
|
|
|
|
// create the front-end route
|
|
|
|
|
|
var defaultRoute = RouteTable.Routes.MapRoute(
|
|
|
|
|
|
"Umbraco_default",
|
|
|
|
|
|
umbracoPath + "/RenderMvc/{action}/{id}",
|
|
|
|
|
|
new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional }
|
|
|
|
|
|
);
|
2019-12-20 17:36:44 +01:00
|
|
|
|
defaultRoute.RouteHandler = new RenderRouteHandler(umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory(), shortStringHelper);
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
2020-02-29 14:26:35 +01:00
|
|
|
|
// register no content route
|
|
|
|
|
|
RouteNoContentController(umbracoPath);
|
|
|
|
|
|
|
2019-08-08 08:56:08 +01:00
|
|
|
|
// register install routes
|
2020-04-20 12:20:47 +02:00
|
|
|
|
// RouteTable.Routes.RegisterArea<UmbracoInstallArea>();
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
|
|
|
|
|
// register all back office routes
|
2020-04-03 11:03:06 +11:00
|
|
|
|
RouteTable.Routes.RegisterArea(new BackOfficeArea(globalSettings, hostingEnvironment));
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
|
|
|
|
|
// plugin controllers must come first because the next route will catch many things
|
2020-04-03 11:03:06 +11:00
|
|
|
|
RoutePluginControllers(globalSettings, surfaceControllerTypes, apiControllerTypes, hostingEnvironment);
|
2019-08-08 08:56:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-29 14:26:35 +01:00
|
|
|
|
private static void RouteNoContentController(string umbracoPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
RouteTable.Routes.MapRoute(
|
2020-03-02 17:17:14 +01:00
|
|
|
|
Constants.Web.NoContentRouteName,
|
|
|
|
|
|
umbracoPath + "/UmbNoContent",
|
2020-02-29 14:26:35 +01:00
|
|
|
|
new { controller = "RenderNoContent", action = "Index" });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-08 08:56:08 +01:00
|
|
|
|
private static void RoutePluginControllers(
|
2020-08-23 13:55:36 +02:00
|
|
|
|
IGlobalSettings globalSettings,
|
2019-08-08 08:56:08 +01:00
|
|
|
|
SurfaceControllerTypeCollection surfaceControllerTypes,
|
2020-01-07 13:08:21 +01:00
|
|
|
|
UmbracoApiControllerTypeCollection apiControllerTypes,
|
2020-04-03 11:03:06 +11:00
|
|
|
|
IHostingEnvironment hostingEnvironment)
|
2019-08-08 08:56:08 +01:00
|
|
|
|
{
|
2020-08-23 15:58:37 +02:00
|
|
|
|
var umbracoPath = ConfigModelConversions.ConvertGlobalSettings(globalSettings).GetUmbracoMvcArea(hostingEnvironment);
|
2019-08-08 08:56:08 +01:00
|
|
|
|
|
|
|
|
|
|
// need to find the plugin controllers and route them
|
|
|
|
|
|
var pluginControllers = surfaceControllerTypes.Concat(apiControllerTypes).ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
// local controllers do not contain the attribute
|
|
|
|
|
|
var localControllers = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace());
|
|
|
|
|
|
foreach (var s in localControllers)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TypeHelper.IsTypeAssignableFrom<SurfaceController>(s))
|
|
|
|
|
|
RouteLocalSurfaceController(s, umbracoPath);
|
|
|
|
|
|
else if (TypeHelper.IsTypeAssignableFrom<UmbracoApiController>(s))
|
|
|
|
|
|
RouteLocalApiController(s, umbracoPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// get the plugin controllers that are unique to each area (group by)
|
|
|
|
|
|
var pluginSurfaceControlleres = pluginControllers.Where(x => PluginController.GetMetadata(x).AreaName.IsNullOrWhiteSpace() == false);
|
|
|
|
|
|
var groupedAreas = pluginSurfaceControlleres.GroupBy(controller => PluginController.GetMetadata(controller).AreaName);
|
|
|
|
|
|
// loop through each area defined amongst the controllers
|
|
|
|
|
|
foreach (var g in groupedAreas)
|
|
|
|
|
|
{
|
|
|
|
|
|
// create & register an area for the controllers (this will throw an exception if all controllers are not in the same area)
|
2020-04-03 11:03:06 +11:00
|
|
|
|
var pluginControllerArea = new PluginControllerArea(globalSettings, hostingEnvironment, g.Select(PluginController.GetMetadata));
|
2019-08-08 08:56:08 +01:00
|
|
|
|
RouteTable.Routes.RegisterArea(pluginControllerArea);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void RouteLocalApiController(Type controller, string umbracoPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
var meta = PluginController.GetMetadata(controller);
|
|
|
|
|
|
var url = umbracoPath + (meta.IsBackOffice ? "/BackOffice" : "") + "/Api/" + meta.ControllerName + "/{action}/{id}";
|
|
|
|
|
|
var route = RouteTable.Routes.MapHttpRoute(
|
|
|
|
|
|
$"umbraco-api-{meta.ControllerName}",
|
|
|
|
|
|
url, // url to match
|
|
|
|
|
|
new { controller = meta.ControllerName, id = UrlParameter.Optional },
|
|
|
|
|
|
new[] { meta.ControllerNamespace });
|
|
|
|
|
|
if (route.DataTokens == null) // web api routes don't set the data tokens object
|
|
|
|
|
|
route.DataTokens = new RouteValueDictionary();
|
|
|
|
|
|
route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "api"); //ensure the umbraco token is set
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void RouteLocalSurfaceController(Type controller, string umbracoPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
var meta = PluginController.GetMetadata(controller);
|
|
|
|
|
|
var url = umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}";
|
|
|
|
|
|
var route = RouteTable.Routes.MapRoute(
|
|
|
|
|
|
$"umbraco-surface-{meta.ControllerName}",
|
|
|
|
|
|
url, // url to match
|
|
|
|
|
|
new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
|
|
|
|
|
|
new[] { meta.ControllerNamespace }); // look in this namespace to create the controller
|
|
|
|
|
|
route.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, "surface"); // ensure the umbraco token is set
|
|
|
|
|
|
route.DataTokens.Add("UseNamespaceFallback", false); // don't look anywhere else except this namespace!
|
|
|
|
|
|
// make it use our custom/special SurfaceMvcHandler
|
|
|
|
|
|
route.RouteHandler = new SurfaceRouteHandler();
|
|
|
|
|
|
}
|
2016-09-01 11:25:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|