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

本文关键字:找不到 操作方法 异常 为什么 控制器 | 更新日期: 2023-09-27 18:20:04

我正在使用以下文章添加到ASP.NET MVC本地化-使用路由来支持多区域性路由。

如果您查看"注册路由"部分,您将看到当前路由(在"RegisterRoutes"方法中)使用"{区域性}"段进行了更新。

不同的是,我想保留当前的路由,并为每个路由添加一个带有"{culture}"段的重复路由,所以对于像"foo/bar"这样的路由,我会得到一个重复的"{culture}/foo/bar"。

你可以看到,我也在确保新路线优先。

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/bar-HttpException在控制器"MyController"上找不到公共操作方法"MyAction"

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

我可以给你一个如何做的例子。你使用的这个例子已经很老了。

  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{private静态只读List_cultures=新列表{"俱乐部列表"};

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

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

我已经确定RouteData需要一个名为"MS_DirectRouteMatches"的密钥,该密钥的值是RouteData的集合。谷歌MS_DirectRouteMatches如果你想进一步挖掘。

最终,我认为这不是一个扩展点。

如果你坚持传统的路线,你会获得更多的成功。我发现你的代码在这种情况下运行得很好,但我想你已经知道了。对不起,我不能给你一个更好的解决方案。

从我所看到的,它没有使用父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!");
    }
}

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

另一种解决方法是使用一个控制器和两种不同格式的方法。