ASP.net MVC маршрутизации с дополнительным первым параметром

Мне нужно предоставить следующие функции для одного из веб-сайтов.

http://www.example.com/ [спонсор]/{controller}/{action}

В зависимости от [спонсора] веб-страница должна быть настроена.

Я попробовал комбинацию регистрации маршрутов с помощью Application_Start и Session_Start, но не смог заставить ее работать.

public static void RegisterRoutes(RouteCollection routes, string sponsor)
{
        if (routes[sponsor] == null)
    {
      routes.MapRoute(
     sponsor, // Route name
     sponsor + "/{controller}/{action}/{id}", // URL with parameters
     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
     );
    }
}

Кроме того, должно выполняться поведение по умолчанию без [спонсор]. Может кто-то, пожалуйста, сообщите мне, если технически возможно иметь необязательный первый параметр в URL MVC3. Если да, просьба поделиться им. Спасибо.


Обновленный код После внесения изменений, предложенных Сергеем Кудрявцевым, код работает, когда задано значение. Если имя не указано, то MVC не направляет контроллер/действие.

Обратите внимание, что это работает только для домашнего контроллера (оба и не для спонсоров). Для других контроллеров/действий, даже если указан параметр спонсора, он не маршрутизируется.

Пожалуйста, предложите, что нужно изменить.

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        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 }
        );
    }

Метод действий

public ActionResult Index(string sponsor)
    {
    }

Ответ 1

В вашем случае sponsor не следует рассматривать как постоянную часть URL-адреса, а как переменную часть.

В 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 }
     );

...
}

В ваших контроллерах, например, 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.
        }
        ...
    }
}

Обратите внимание, что тип этого параметра всегда будет System.String, а имя компонента шаблона маршрута {sponsor} должно точно соответствовать имени параметра действия string sponsor в ваших контроллерах.

UPD: добавлен второй маршрут для не спонсорского дела.

Обратите внимание, что такая настройка осложнит вашу логику, потому что вы можете путать разные URL-адреса, например URL

http://www.example.com/a/b/c

может быть сопоставлен обоими маршрутами: первый будет иметь спонсор = a, controller = b и action = c; второй - контроллер = a, action = b и id = c.

Эту ситуацию можно избежать, если вы укажете более строгие требования к URL-адресам - например, вы можете захотеть, чтобы идентификаторы были только численными. Ограничения указаны в четвертом параметре функции routes.MapRoute().

Другим подходом для устранения неоднозначности является указание отдельных маршрутов для всех ваших контроллеров (как правило, у вас не будет много их в вашем приложении) до родового маршрута для спонсоров.

UPD:

Самый простой, но наименее поддерживаемый способ различать спонсорские и не спонсорские маршруты - это указать маршруты, специфичные для контроллера, например:

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 }
     );

...
}

Обратите внимание, что здесь все маршруты, специфичные для контроллера, должны быть добавлены до SponsorRoute.

Более сложный, но более чистый способ реализует RouteConstraints для имен спонсоров и контроллеров, как описано в ответе от @counsellorben.

Ответ 2

В моем случае я решил эту проблему, используя следующие два маршрутизатора:

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" }
    );
  }
}

Где класс CultureConstraint выглядит следующим образом:

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);
  }
}

И MultiCultureMvcRouteHandler:

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);
  }
}

Ответ 3

В дополнение к добавлению второго маршрута до маршрута по умолчанию, как сказал Сергей в своем ответе, вы также должны добавить RouteConstraint к первоначальному маршруту, чтобы обеспечить, чтобы токен {sponsor} был именем действительного спонсора.

Вы можете использовать RouteConstraint в этом ответе: Пользовательская маршрутизация Asp.Net и настраиваемая маршрутизация и добавление категории перед контроллером

Помните, что вы также должны применять правило, согласно которому имя спонсора не может быть таким же, как любое имя вашего контроллера.

Ответ 4

я покажу вам в простом примере, что вам не нужно менять Route.config.cs только вам нужно сделать в Route.config.cs просто вставить

Дополнительные параметры URI. Значения по умолчанию и по умолчанию

Route.config.cs

routes.MapMvcAttributeRoutes();

контроллер

[Route("{Name}/Controller/ActionName")]
        public ActionResult Details(string Name)
        {            

              // some code here 

            return View();
        }

Результаты

локальный: 2345/Имя/controllername/ActionName/идентификатор (необязательно)