views:

821

answers:

6

Hi, 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

A: 

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
That is an editing mistake. I was trying to protect the innocent.
Chris Schoon
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
+1  A: 

Having the same problems, the application works like 99% of the time, but when these problems start, they are there for all controllers. Having looked at our logs the error occurs for every controller in the application for about 1 minute. Therefor I am inclined believe the problem has to do with caching or something like that, definitely not routing!

I look at our logs and most user agents are either mozilla or IE but I do also see some user agents which are also sending options verbs.

![Dump of our log][1]

http://img266.yfrog.com/img266/4144/errorlog.png

HtW
+5  A: 

I am on the same team as the original poster, and I have an update to our research on this issue.

We examined our IIS logs and correlated incoming requests with the exceptions that we were getting, and we noticed a pattern with the user agents and the HTTP methods.

The controller method provided in the code sample above ("Fill") accepts GETs and POSTs, but it seems that the bad requests coming in for that method are coming from agents such as MS FrontPage and other MS Office tools. We have previously seen such apps use verbs such as OPTIONS and PROPFIND, which have caused other weird issues before. We can probably chalk up that method's exceptions to atypical user agent behavior.

However, we have another controller method, "Save", which only accepts POSTs...

    [ActionName("Save")]
    [JsonFilter(Param = "data", RootType = typeof(SaveData)), AcceptVerbs(HttpVerbs.Post), UserIdFilter]
    public JsonResult Save(int userId, SaveData data, int formSubscriberId)
    {
        //Do stuff
        return Json(NewFormAJAXResultMessage.Success());
    }

Every single exception we have gotten on this method has been the result of a GET being issued from IE6 rather than a POST. This appears to be isolated to IE6, though I have been unable to reproduce it so far with my copy of IE6. All of the requests for this method are being issued via AJAX calls from this client-side script:

function PostJsonHelper(url, data, showUi, useAsyncPost) {
    $.ajax({
        url: url,
        type: 'POST',
        data: data,
        dataType: 'json',
        contentType: "application/json; charset=utf-8",
        success: function(result) {
            console.log("Save Success.");
            if (showUi) {
                $.blockUI({ message: $('#SavedPopUp'), css: { cursor: 'default'} });
            }
        },
        error: function(result) {
            console.log("Save Fail.");
            if (IsDevServer) {
                if (showUi) {
                    $.unblockUI();
                }
                window.document.clear();
                window.document.write(result.responseText);
            }
            else {
                if (showUi) {
                    $.blockUI({ message: $('#SaveErrorPopUp'), css: { cursor: 'default'} });
                }
            }
        },
        async: useAsyncPost
    });
}

Is anyone aware of the reason that we may intermittently see GETs coming from IE6 where we would expect to see POSTs?

Brent Miller
I am seeing this behavior with Firefox 3.5 as well though I do not see the request on the net tab in firebug. I do see the GET request not finding the action that is marked as POST only in my ELMAH error reports. I never break on an error when debugging though. It only seems to happen on pages that are being hard reset with ctrl-r and not on subsequent requests.
beckelmw
+5  A: 

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  A: 

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
A: 

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