93

I'm getting an intermittent exception saying that asp.net mvc can’t find the action method. Here’s the exception:

A public action method 'Fill' could not be found on controller 'Schoon.Form.Web.Controllers.ChrisController'.

I think I have the routing set up correctly because this application works most of the time. Here is the controller’s action method.

[ActionName("Fill")]
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post), UserIdFilter, DTOFilter]
public ActionResult Fill(int userId, int subscriberId, DisplayMode? mode)
{
     //…
}

The route:

routes.MapRoute(
        "SchoonForm",
        "Form/Fill/{subscriberId}",
        new { controller = "ChrisController", action = "Fill" },
        new { subscriberId = @"\d+" }
    );

And here is the stack:

System.Web.HttpException: A public action method 'Fill' could not be found on controller 'Schoon.Form.Web.Controllers.ChrisController'. at System.Web.Mvc.Controller.HandleUnknownAction(String actionName) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\Controller.cs:line 197 at System.Web.Mvc.Controller.ExecuteCore() in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\Controller.cs:line 164 at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\ControllerBase.cs:line 76 at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\ControllerBase.cs:line 87 at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\MvcHandler.cs:line 80 at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\MvcHandler.cs:line 68 at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) in C:\dev\ThirdParty\MvcDev\src\SystemWebMvc\Mvc\MvcHandler.cs:line 104 at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Here is an example of my filters they all work the same way:

public class UserIdFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        const string Key = "userId";

        if (filterContext.ActionParameters.ContainsKey(Key))
        {
            filterContext.ActionParameters[Key] = // get the user id from session or cookie
        }

        base.OnActionExecuting(filterContext);
    }
}

Thanks, Chris

Chris Schoon
  • 2,145
  • 1
  • 17
  • 11
  • 29
    I had a similar problem which I think is worth noting here as this was the first result that came up in Google when searching for the above exception. My application threw this exception when submitting an invalid form. This was due to the page that was being (re-)rendered calling RenderAction and the action that was called to render a partial view was marked with the HttpGet attribute, removing this attribute resolved the problem. – s1mm0t Apr 26 '10 at 13:51
  • 3
    I've noticed this behaviour as well - perhaps its best to not apply any Http attributes to controller methods that return PartialViewResults. – Stuart Jun 16 '10 at 10:38
  • 1
    @s1mm0t: is right. for my case, his comment solved the problem – Mazdak Shojaie Apr 14 '17 at 13:01
  • @s1mm0t - please send me your postal address immediately. A bottle of Scotch is on it's way for you this Christmas!!!!! – Shane Nov 29 '17 at 07:43
  • We found something similar: in certain cases returning another actionresult instead of a redirect to that action was causing the problem. Ex `PostSomething { return HomePageActionMethod() }` fails where `PostSomething { return RedirectToAction(nameof(HomePageActionMethod)); }` works. (in our case the offending action in the view is located in a different controller, and presumably that controller is not fully initialized with the first method of calling. – jleach Dec 06 '17 at 08:01

13 Answers13

64

We found the answer. We looked into our web logs. It showed that we were receiving some weird http actions (verbs/methods) like OPTIONS, PROPFIND and HEAD.

This seems to the cause of some of theses exceptions. This explains why it was intermittent.

We reproduced the issue with the curl.exe tool:

curl.exe -X OPTIONS http://localhost/v2.3.1.0/(S(boztz1aquhzurevtjwllzr45))/Form/Fill/273
curl.exe -X PROPFIND http://localhost/v2.3.1.0/(S(boztz1aquhzurevtjwllzr45))/Form/Fill/273
curl.exe -X HEAD http://localhost/v2.3.1.0/(S(boztz1aquhzurevtjwllzr45))/Form/Fill/273

The fix we used was to add an authorization section to web.config:

<authorization>
  <deny users="*" verbs="OPTIONS, PROPFIND, HEAD"/>
</authorization>
Chris Schoon
  • 2,145
  • 1
  • 17
  • 11
  • 5
    We've also found that bots will sometimestimes crawl your site -- and even javascript -- to find links. They then try sending requests to these URIs with the wrong HTTP verb. For example, if you have a jQuery call to some action -- e.g. /some-action and this method requires a POST, the bot may attempt to send a GET, which will cause this error to show up. Your web logs could definitely help confirm if this was the case. We even see googlebot doing this. – jakejgordon Mar 01 '16 at 20:17
  • I am having this same error only on Live server (IIS 7.5). The deployment works fine on my development machine as well as on another support machine. adding these verbs and removing HttpGet didn't fix the issue. Any further suggestions please. – bjan Nov 08 '16 at 12:42
  • An an alternative to denying incoming HEAD requests, you may wish to serve an appropriate response. See https://stackoverflow.com/a/3197128/12484 – Jon Schneider Aug 17 '17 at 15:22
15

We had a similar issue, but found that it was happening because a user was posting to a controller after his login had timed out. The system then redirected to the login screen. After logging in it redirected back to the URL the user was trying to post to, but this time it was doing a GET request instead and therefore not finding the action which was marked with an [HttpPost] attribute.

Johann Strydom
  • 1,482
  • 14
  • 18
  • My current solution is as far as possible to always do a redirect back to the Index action at the end of an action. Sorry for the late response. – Johann Strydom Jul 20 '11 at 08:00
7

I'v got the same problem in asp.net mvc. this error - 404 not found. I resolve problem this way - put this code in MyAppControllerBase (MVC)

    protected override void HandleUnknownAction(string actionName)
    {
        this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<PagesController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Pages");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }
Dmitriy
  • 552
  • 1
  • 6
  • 20
6

We just had the same issue on our application and I was able to trace it to a javascript/jquery issue. We have links in our application defined using Html.ActionLink() that are later overridden into POSTs by jquery.

First we had defined the link:

Html.ActionLink("Click Me", "SomeAction", new { id = Model.Id})

Later we override the default action with our SomePostEventHandler function:

 $(document).ready(function() {
      $('#MyLink').click(SomePostEventHandler);
 }

This was hitting our MVC action that had a HttpPost filter:

 [HttpPost]
 public ActionResult SomeAction(int id)
 {
      //Stuff
 }

What we found is that most of the time this worked great. However, on some slow page loads (or really fast users), the user was clicking the link before the jquery $(document).ready() event was firing, meaning that they were trying to GET /Controller/SomeAction/XX instead of posting.

We don't want the user to GET that url, so removing the filter is not an option for us. Instead we just wired the onclick event of the action link directly (we had to change SomePostEventHandler() slightly for this to work):

string clickEvent = "return SomePostEventHandler(this);";

Html.ActionLink("Click Me", "SomeAction", new { id = Model.Id}, new { onclick = clickEvent })

So, moral of the story, for us at least, is that if you are seeing these errors, track down the URL that you THINK you are POSTing to and make sure you are.

jslatts
  • 9,307
  • 5
  • 35
  • 38
  • In general you should be careful when you POST from an html hyperlink. Hyperlinks exist to take the user to another page (http get) and html buttons should submit a form (http post). – stevie_c Sep 28 '20 at 10:07
2

From the IIS logs our problem was caused by Googlebot attempting POST and a GET for a POST only controller action.

For this case I recommend handling the 404 like Dmitriy suggestion.

Zoom
  • 401
  • 5
  • 9
2

I too had this issue.

In my case it was related to verb restrictions on the requested action, where the view was a POST but the partial view being requested within supported GET and HEAD only. Adding the POST verb to the AcceptVerbsAttribute (in MVC 1.0) resolved the problem.

wolfyuk
  • 746
  • 1
  • 8
  • 26
1

The currently accepted answer does work as expected but isn't the primary use case for the feature. Instead use the feature defined by ASP.NET. In my case, I denied everything but GET and POST:

  <system.webServer>
  <security>
      <requestFiltering>
          <verbs allowUnlisted="false">
              <add verb="GET" allowed="true"/>
              <add verb="POST" allowed="true"/>
          </verbs>
      </requestFiltering>
  </security>
 </system.webServer>

With the code snippet above, MVC will correctly return a 404

Valchris
  • 1,451
  • 2
  • 14
  • 33
0

My root cause was similar to the one mentioned in the comment.

I was ajaxSubmitting a form upon the click of a button. One of the form fields was of type Date. However, because of the difference in the date formats between the client and server machine it did not execute the POST method in the controller. The server sent back a 302 response and then sent a GET request for the same method again.

However, the action in the controller was decorated with the HttpPost attribute and hence it could not find the method and sent back a 404 response.

I just fixed the code such that the mismatch in the Date formats would not cause an error and the problem was fixed.

mridula
  • 3,203
  • 3
  • 32
  • 55
0

Shouldn't it be

routes.MapRoute(
        "SchoonForm",
        "Form/Fill/{subscriberId}",
        new { controller = "Chris", action = "Fill" },

Also, what do your filters do? Can't they hide action, like ActionMethodSelectorAttribute?

queen3
  • 15,333
  • 8
  • 64
  • 119
  • That is an editing mistake. I was trying to protect the innocent. – Chris Schoon Nov 16 '09 at 22:20
  • They populate some of the parameters. For example, the UserIdFilter is a helper to get the user id from session / cookie / etc. It populates the first parameter. I’ll edit the post to include it. – Chris Schoon Nov 16 '09 at 22:25
0

Remove the [HttpGet] attributes and it will work :)

Undo
  • 25,519
  • 37
  • 106
  • 129
Cătălin Rădoi
  • 1,804
  • 23
  • 43
  • 1
    While this "solves" the errors, the likelihood is that you (or someone before you) put those `[HttpGet]` attributes there on purpose, to prevent the actions from being called through any other VERBs – Nick Orlando Jul 07 '20 at 15:58
0

For anyone having this problem with angularjs, MVC and {{imagepath}} type inserts in image src attributes, eg:

"A public action method '{{imagepath}}previous.png' was not found on controller"

The solution is to use ng-src instead of src.

Hope this helps someone :)

davaus
  • 1,145
  • 13
  • 16
0

See if simply browsing to the URL in question is enough to reproduce the error. It would if the action was only defined as a POST action. Doing this allows you to reproduce the error at will.

In any case, you can globally handle the error as below. Another answer here that references HandleUnknownAction only handles URLs with bad action names, not bad controller names. The following approach handles both.

Add this to your base controller (view code omitted here):

public ActionResult Error(string errorMessage)
{
    return View("Error");  // or do something like log the error, etc.
}

Add a global exception handler to Global.asax.cs that calls the method above or does whatever else you want to do with the caught 404 error:

void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();  // get the exception object
    HttpException httpException = ex as HttpException;

    if (httpException != null && httpException.GetHttpCode() == 404)  // if action not found
    {
        string errorMessage = "The requested page was not found.";

        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Base");
        routeData.Values.Add("action", "Error");
        routeData.Values.Add("errorMessage", errorMessage);

        Server.ClearError();
        Response.TrySkipIisCustomErrors = true;

        // Go to our custom error view.
        IController errorController = new BaseController();
        errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
}
Tawab Wakil
  • 1,737
  • 18
  • 33
0

I have the similar issue with qq File Upload

When the post action is /Document/Save I get the exception A public action method 'Save' was not found on controller 'Project.Controllers.DocumentController'.

But if the post action is /Document/Save/, the post is correct and works!

God save the / ?

Tuizi
  • 1,643
  • 4
  • 22
  • 34