为什么我收到异常“控制器上找不到公共操作方法”?

时间:2015-11-01 22:50:58

标签: c# asp.net-mvc exception routing

我使用以下文章Addition to ASP.NET MVC Localization - Using routing来支持多文化路线。

如果您查看“注册路线”部分,您会看到当前路线已更新(在“RegisterRoutes”方法中)与“{culture}”段。

不同之处在于我想保留当前路线并为每个带有“{culture}”段的路线添加副本,因此对于像“foo / bar”这样的路线,我会得到一个重复的“{culture} /富/栏”。

你可以看到我也确定新路线是第一位的。

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 - WORKS!
  • / en-US / foo / bar - HttpException在控制器'MyController'上找不到公共操作方法'MyAction'

3 个答案:

答案 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. 实施文化助手类

  4. 公共静态类CultureHelper {     private static readonly List _cultures = new List {     “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);
        }
    }
    

答案 1 :(得分:0)

我认为您无法通过属性路由实现此目的,因为传递给RegisterRoutes的RouteCollection及其后续的RouteData非常不同,并且使用了您无法获得的内部类。

我确定RouteData需要一个名为&#34; MS_DirectRouteMatches&#34;其值是RouteData的集合。如果您想进一步挖掘,可以使用Google MS_DirectRouteMatches

最终,我认为这并不是一个延伸点。

如果您坚持使用传统路由,您将获得更多成功。我发现你的代码在这种情况下工作得很好,但我希望你已经知道了。对不起,我无法为您提供更好的解决方案。

答案 2 :(得分: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!");
    }
}

这至少会告诉你继承是否存在缺陷,你可以查看你的代码。

另一种方法是使用一个控制器,使用两种方法来处理不同的格式。