I need to provide following functionality for one of the web sites.
http://www.example.com/[sponsor]/{controller}/{action}
Depending on the
In my case, I've resolved this issue using the following two routers:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "MultiCulture",
url: "{culture}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { culture = new CultureConstraint(CultureFactory.All.Select(item => item.UrlPrefix).ToArray()) }
).RouteHandler = new MultiCultureMvcRouteHandler();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
}
}
Where CultureConstraint
class looks like below:
public class CultureConstraint : IRouteConstraint
{
private readonly string[] values;
public CultureConstraint(params string[] values)
{
this.values = values;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary routeValues, RouteDirection routeDirection)
{
string value = routeValues[parameterName].ToString();
return this.values.Contains(value);
}
}
And MultiCultureMvcRouteHandler
like this:
public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
var culture = CultureManager.GetCulture(requestContext.RouteData);
if (culture != null)
{
var cultureInfo = new CultureInfo(culture.Name);
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);
}
return base.GetHttpHandler(requestContext);
}
}
In addition to adding a second route before the default route, as Sergey said in his answer, you also must add a RouteConstraint
to the initial route, to enforce that the {sponsor}
token is the name of a valid sponsor.
You can use the RouteConstraint in this answer: Asp.Net Custom Routing and custom routing and add category before controller
Remember that you must also enforce a rule that a sponsor name cannot be the same as any of your controller names.
i will show you in simple example you don't have to change in Route.config.cs only you have to do in Route.config.cs just put in
Optional URI Parameters First and Default Values
Route.config.cs
routes.MapMvcAttributeRoutes();
Controller
[Route("{Name}/Controller/ActionName")]
public ActionResult Details(string Name)
{
// some code here
return View();
}
Results
localhost:2345/Name/controllername/actionname/id(optional)
In your case sponsor
should not be treated as a constant part of URL, but as a variable part.
In Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"SponsorRoute",
"{sponsor}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"NonSponsorRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
...
}
In your controllers, for example, HomeController.cs:
namespace YourWebApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index(string sponsor)
{
// Here you can do any pre-processing depending on sponsor value, including redirects etc.
}
...
}
}
Note that type of this parameter will always be System.String
and the name of route template component {sponsor}
must exactly match the name of action parameter string sponsor
in your controllers.
UPD: Added second route for non-sponsor case.
Please note that such setup will complicate your logic, because you might confuse different urls, for example URL
http://www.example.com/a/b/c
could be matched by both routes: first one will have sponsor=a, controller=b and action=c; second one will have controller=a, action=b and id=c.
This situation can be avoided if you specify more strict requirements to URLs - for example, you may want IDs to be numerical only. Restrictions are specified in fourth parameter of routes.MapRoute()
function.
Another approach for disambiguation is specifying separate routes for all of your controllers (usually you won't have much of them in your app) before generic route for sponsors.
UPD:
Most straightforward yet least maintainable way to distinguish between sponsor and non-sponsor routes is specifying controller-specific routes, like this:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"HomeRoute",
"Home/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
routes.MapRoute(
"AccountRoute",
"Account/{action}/{id}", // URL with parameters
new { controller = "Account", action = "Index", id = UrlParameter.Optional, sponsor=string.Empty }
);
...
routes.MapRoute(
"SponsorRoute",
"{sponsor}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
...
}
Note that here all controller-specific routes must be added before SponsorRoute.
More complex yet more clean way is implementing RouteConstraints for sponsor and controller names as described in answer from @counsellorben.