Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I receiving the exception "A public action method was not found on controller"?

I am using the following article Addition to ASP.NET MVC Localization - Using routing to support multi-culture routes.

If you look at section "Registering routes" you will see that current routes are updated (in the "RegisterRoutes" method) with the "{culture}" segment.

The difference is that I want to keep the current routes and add a duplicate for each one with a "{culture}" segment, so for a route like "foo/bar" I would get a duplicate "{culture}/foo/bar".

You can see I'm also making sure the new route comes first .

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

The "InheritedRoutesProvider" looks like this:

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

My controller looks like this:

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

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

My "RegisterRoutes" method looks like this:

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

Now, if I do:

  • /foo/bar - WORKS!
  • /en-US/foo/bar - HttpException A public action method 'MyAction' was not found on controller 'MyController'
like image 318
Dan Avatar asked Nov 01 '15 22:11

Dan


People also ask

How do I add actions to my controller?

Adding an Action to a Controller You add a new action to a controller by adding a new method to the controller. For example, the controller in Listing 1 contains an action named Index() and an action named SayHello(). Both methods are exposed as actions.

Can action method be public in MVC?

So, every public method inside the Controller is an action method in MVC. Action Method can not be a private or protected method. If you provide the private or protected access modifier to the action method, it will provide the error to the user, i.e., “resource can not be found” as below.

Can action method be private?

All the public methods in the Controller class are called Action methods. The Action method has the following restrictions. - Action method must be public. It cannot be private or protected.

Why action method Cannot be a static method?

The one restriction on action method is that they have to be instance method, so they cannot be static methods. Also there is no return value restrictions. So you can return the string, integer, etc.


1 Answers

I can give you an example of how I would do it. That example you are using is quite old.

  1. Implement in your controllers (use inheritance) the BeginExecuteCore method as below:

    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. Add some routes

            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
            routes.MapRoute(
                name: "Custom",
                url: "{controller}/{action}/{culture}",
                defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Coordinate", action = "Index" }
    
  3. Implement a culture helper class

public static class 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);
    }
}
like image 150
Rober Avatar answered Sep 27 '22 23:09

Rober