Imagine a site that has many domains it binds to. f.e. product1.com, product2.com, ...
When product23 gets out of order the idea is to have a 'landingpagecontroller' serve all the request to product23.com.
The idea is to write a ActionFilter to accomplish this :
public class Landingpage : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var DnsSafeHost = filterContext.HttpContext.Request.Url.DnsSafeHost;
if (DnsSafeHost.NeedsLandingpage())
{
//Do actual redirecting
}
}
}
NeedsLandingpage() return a boolean. True if servicing by landingpage controller is needed. Intelligence will be found in a DB.
I already added a route to the landingpage controller.
routes.MapRoute(
name: "Ladingpage",
url: "Landingpage/{id}/{*dummy}",
defaults: new { controller = "LandingPage", action = "Index" }
);
Any tips on how to change the route settings from the actionfilter so that the above route is triggered or a better solution to accomplish the goal.
********** UPDATED ************
Given the input from you all I came up with the following working solution, but I'm not 100% happy with it. So a better solution is welcome.
I created the following IActionFilter and registered it to be global.
public class Landingpage : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var DnsSafeHost = filterContext.HttpContext.Request.Url.DnsSafeHost;
var _LandingPage = LandingPage(DnsSafeHost);
if (_LandingPage != null)
{
if ((String)filterContext.RouteData.Values["controller"] != "Landingpage")
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Landingpage", action = "Index", id = _LandingPage }));
filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext);
}
}
}
private String LandingPage(String DnsSafeHost)
{
if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
if (DnsSafeHost.Contains("<domain to look for>".ToLower())) return "<viewname>";
return null;
}
}
What I do not like about the solution is the check of (String)filterContext.RouteData.Values["controller"]
which is needed or you get stuck in a loop because.
Browser -> Routing -> Filter -> Redirect -> Routing -> Filter ...
A possible solution I thought would be to do something in Application_BeginRequest (global.asax). But I did not find a way to 'rewrite' the request. Because if that would be possible the flow would be : Browser -> BeginRequest -> RewriteRequest -> Routing -> Controller.
A down-site seems to be that in vNext this would not work anymore if you follow the best-practices, I guess.
Filterconfig.cs
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new Landingpage());
}
}
All the 'landingpage' calls are redirected to the LandingPageController. The call to ViewEngine is there to check if the viewname passed from the actionfilter does exists, if not a default one is used.
public class LandingpageController : Controller
{
public ActionResult Index(string id)
{
if (id == null) return View();
if (ViewEngines.Engines.FindView(ControllerContext, id, null).View == null) return View();
ViewBag.Title = id;
return View(id);
}
}
A sample of a view.
@{
Layout = "_Index_layout.cshtml";
}
<div class="domainname">Landingpage</div>
To give it a 'special' lay-out different from the normal pages (f.e. no menu) I added a custom layout for this page.
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<style>
--- add some styling here ---
</style>
</head>
<body>
@RenderBody()
</body>
</html>
As mentioned in my EDIT I had a solution but was not 100% happy with due to the redirect and the dirty way to get out of the redirect loop.
If you take a look at the request processing pipeline.
http://saulius.sunauskas.com/wp-content/uploads/asp_net__mvc_request_pipeline_4.png
The place to alter the request is the 'IRouteHandler' on the flow-chart.
Found a nice sample of creating a custom one here :
The proto-type of the final solution is:
Create a custom IRouteHandler direving from MVCRouteHandler :
public class LandingPageRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext Context)
{
if ( Context.HttpContext.Request.Url.DnsSafeHost.ToLower().Contains("onecityguide"))
{
Context.RouteData.Values["controller"] = "LandingPage";
Context.RouteData.Values["action"] = "Index";
Context.RouteData.Values["id"] = "onecityguide";
}
return base.GetHttpHandler(Context);
}
}
Hookup the custom RouteHandler to the default route.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).RouteHandler = new LandingPageRouteHandler();