2015-11-01 2 views
7

Я использую следующую статью Addition to ASP.NET MVC Localization - Using routing для поддержки маршрутов с несколькими культурами.Почему я получаю исключение «Открытый метод действия не найден на контроллере»?

Если вы посмотрите раздел «Регистрация маршрутов», вы увидите, что текущие маршруты обновляются (в методе «RegisterRoutes») с сегментом «{культура]».

Разница в том, что я хочу сохранить текущие маршруты и добавить дубликат для каждого из них с сегментом «{культура]», поэтому для маршрута, такого как «foo/bar», я получаю дубликат «{culture}/Foo/бар».

Вы можете видеть, что я также уверен, что новый маршрут на первом месте.

public static void MapMvcMultiCultureAttributes(this RouteCollection routes, bool inheritedRoutes = true, string defaultCulture = "en-US", string cultureCookieName = "culture") 
{ 
    routes.MapMvcAttributeRoutes(inheritedRoutes ? new InheritedRoutesProvider() : null); 

    var multiCultureRouteHandler = new MultiCultureMvcRouteHandler(defaultCulture, cultureCookieName); 

    var initialList = routes.ToList(); 
    routes.Clear(); 

    foreach (var routeBase in initialList) 
    { 
     var route = routeBase as Route; 
     if (route != null) 
     { 
      if (route.Url.StartsWith("{culture}")) 
      { 
       continue; 
      } 

      var cultureUrl = "{culture}"; 
      if (!String.IsNullOrWhiteSpace(route.Url)) 
      { 
       cultureUrl += "/" + route.Url; 
      } 

      var cultureRoute = routes.MapRoute(null, cultureUrl, null, new 
      { 
       culture = "^\\D{2,3}(-\\D{2,3})?$" 
      }); 

      cultureRoute.Defaults = route.Defaults; 
      cultureRoute.DataTokens = route.DataTokens; 

      foreach (var constraint in route.Constraints) 
      { 
       cultureRoute.Constraints.Add(constraint.Key, constraint.Value); 
      } 

      cultureRoute.RouteHandler = multiCultureRouteHandler; 
      route.RouteHandler = multiCultureRouteHandler; 
     } 

     routes.Add(routeBase); 
    } 
} 

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

private class InheritedRoutesProvider : DefaultDirectRouteProvider 
{ 
    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(ActionDescriptor actionDescriptor) 
    { 
     return actionDescriptor.GetCustomAttributes(typeof(IDirectRouteFactory), true) 
      .Cast<IDirectRouteFactory>() 
      .ToArray(); 
    } 
} 

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

public class MyBaseController: Controller 
{ 
    [HttpGet] 
    [Route("bar")] 
    public virtual ActionResult MyAction(){ 
    { 
     return Content("Hello stranger!"); 
    } 
} 

[RoutePrefix("foo")] 
public class MyController: MyBaseController 
{ 
} 

Мой метод "RegisterRoutes" выглядит следующим образом:

public static void RegisterRoutes(RouteCollection routes) 
{ 
    routes.MapMvcMultiCultureAttributes(); 
    routes.LowercaseUrls = true; 
} 

Теперь, если да:

  • /foo/bar - РАБОТЫ!
  • /EN-US/Foo/бар - HttpException метод общественного действия «MyAction» не найден на контроллере «MyController»
+0

В чем заключается ваше содержание метода "RegisterRoutes'. –

+0

@RezaAghaei Обновлено. – Dan

+0

Где вы объявляли маршрут по умолчанию? –

ответ

1

Я могу дать вам пример того, как я бы это сделать. Этот пример, который вы используете, довольно старый.

  1. Реализовать в контроллерах (используйте наследование) BeginExecuteCore метод, как показано ниже:

    protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) 
    { 
        string cultureName = RouteData.Values["culture"] as string; 
    
        if (cultureName == null) 
         cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? 
           Request.UserLanguages[0] : // obtain it from HTTP header AcceptLanguages 
           null; 
    
        // Validate culture name 
        cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe 
    
        if (RouteData.Values["culture"] as string != cultureName) 
        { 
         // Force a valid culture in the URL 
         RouteData.Values["culture"] = cultureName.ToLower(); // lower case too 
    
         // Redirect user 
         Response.RedirectToRoute(RouteData.Values); 
        } 
    
        // Modify current thread's cultures    
        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName); 
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 
    
        return base.BeginExecuteCore(callback, state); 
    } 
    
  2. Добавить некоторые маршруты

     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
    
         routes.MapRoute(
          name: "Custom", 
          url: "{controller}/{action}/{culture}", 
          defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Coordinate", action = "Index" } 
    
  3. Реализовать вспомогательный класс культуры

общественного статический класс CultureHelper { частного статического только для чтения Списка _cultures = новый список { "listOfClutures" };

public static bool IsRighToLeft() 
    { 
     return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.IsRightToLeft; 
    } 

    public static string GetImplementedCulture(string name) 
    { 
     if (string.IsNullOrEmpty(name)) 
      return GetDefaultCulture(); // return Default culture 
     if (!CultureInfo.GetCultures(CultureTypes.SpecificCultures).Any(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))) 
      return GetDefaultCulture(); // return Default culture if it is invalid 
     if (_cultures.Any(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase))) 
      return name; // accept it 

     var n = GetNeutralCulture(name); 
     foreach (var c in _cultures) 
      if (c.StartsWith(n)) 
       return c; 
     return GetDefaultCulture(); // return Default culture as no match found 
    } 

    public static string GetDefaultCulture() 
    { 
     return "en-GB"; // return Default culture, en-GB 
    } 
    public static string GetCurrentCulture() 
    { 
     return Thread.CurrentThread.CurrentCulture.Name; 
    } 
    public static string GetCurrentNeutralCulture() 
    { 
     return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name); 
    } 
    public static string GetNeutralCulture(string name) 
    { 
     if (!name.Contains("-")) return name; 

     return name.Split('-')[0]; // Read first part only. E.g. "en", "es" 
    } 

    public static List<KeyValuePair<string, string>> GetImplementedLanguageNames() 
    { 
     List<KeyValuePair<string, string>> languageNames = new List<KeyValuePair<string, string>>(); 

     foreach (string culture in _cultures) 
     { 
      languageNames.Add(new KeyValuePair<string, string>(culture, CultureInfo.GetCultureInfo(culture).EnglishName)); 
     } 

     languageNames.Sort((firstPair, nextPair) => 
     { 
      return firstPair.Value.CompareTo(nextPair.Value); 
     }); 

     string currCulture = GetCurrentCulture(); 
     languageNames.Remove(new KeyValuePair<string, string>(currCulture, CultureInfo.GetCultureInfo(currCulture).EnglishName)); 
     languageNames.Insert(0, new KeyValuePair<string, string>(currCulture, CultureInfo.GetCultureInfo(currCulture).EnglishName)); 

     return languageNames; 
    } 

    public static string GetDateTimeUsingCurrentCulture(DateTime dateToConvert) 
    { 
     CultureInfo ci = new CultureInfo(GetCurrentCulture()); 
     return dateToConvert.ToString(ci.DateTimeFormat.ShortDatePattern + ' ' + ci.DateTimeFormat.ShortTimePattern); 
    } 
} 
+0

Спасибо за ваш пример, но я не хочу перенаправлять, если текущий маршрут отличается от указанного (или пустым), и я также хочу, чтобы сегмент {culture} был первым сегментом и работал с любым типом маршрут (обычный маршрут или атрибут маршрута). Таким образом, пользователь сможет получить доступ к моему маршруту с указанной культурой или без нее (без перенаправления). – Dan

0

Я не думаю, что вы будете в состоянии достигнуть этого с маршрутизацией атрибутом, поскольку RouteCollection передается RegisterRoutes и его последующее RouteData довольно сильно отличается и использует внутренние классы, которые вы не можете добраться до.

Я догадался, что RouteData ожидает ключ под названием «MS_DirectRouteMatches», значение которого представляет собой коллекцию RouteData. Google MS_DirectRouteMatches, если вы хотите копать дальше.

В конечном счете, я предполагаю, что это не предназначено для точки расширения.

Вы получите больше успеха, если будете придерживаться обычной маршрутизации. Я обнаружил, что ваш код работал очень хорошо в этом сценарии, но я ожидаю, что вы уже это знали. Извините, я не мог дать вам лучшее решение.

0

Из того, что я вижу, он не использует родительский ActionResult. Я не знаю, почему это происходит. Вы можете переопределить ActionResult в производном классе, но для меня кажется, что в наследовании есть что-то невостребованное ... ошибка в том, как вы управляете своими классами.

public class MyBaseController: Controller 
{ 
    [HttpGet] 
    [Route("bar")] 
    public virtual ActionResult MyAction(){ 
    { 
     return Content("Hello stranger!"); 
    } 
} 

Так предполагаю, что это не будет работать:

[RoutePrefix("foo")] 
public class MyController: MyBaseController 
{ 

    [HttpGet] 
    [Route("foo")] 
    public override ActionResult MyAction(){ 
    { 
     return Content("Hello stranger!"); 
    } 
} 

Я предлагаю сделать это:

[RoutePrefix("foo")] 
public class MyController: MyBaseController 
{ 

    [HttpGet] 
    [Route("foo")] 
    public ActionResult MyAction(){ 
    { 
     return Content("Hello stranger!"); 
    } 
} 

Это, по крайней мере, будет сообщать вам, если наследство несовершенна, и вы можете просмотреть ваш код.

Другим способом будет использование одного контроллера с двумя способами для разных форматов.

Смежные вопросы