Mike Hofer Mike Hofer - 3 months ago 30
Ajax Question

How to correctly invoke POST action method via AJAX in ASP.NET MVC

I am unsuccessfully attempting to invoke an MVC action via Ajax. When I do so, the following error occurs:

HttpException: A public action method 'Find' was not found on controller 'MyProject.Controllers.LoanController'.


The action method I'm attempting to invoke has the following signature:

[Route("Loan/Find/{loanNumber:int}")]
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
[ActionName("Find")]
[AllowAnonymous]
public ActionResult Find(int loanNumber)
{
var model = new LoanStatusModel
{
Record = Repository.GetRecord(loanNumber),
User = this.SecurityPrincipal
};

return this.PartialView("LoanStatusSearchResult", model);
}


The view code that attempts to invoke it is as follows:

@using(Ajax.BeginForm("Find", new AjaxOptions
{
HttpMethod = "POST",
AllowCache = false,
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "ajaxTarget",
LoadingElementId="loader"

}))
{
<fieldset>
<div class="col-sm-3">
<input type="text"
id="loanNumber"
name="loanNumber"
value="@Model.LoanNumber"
class="form-control"
minlength="8"
maxlength="10"/>
</div>
<div class="col-sm-7">
<input type="submit"
value="Search"
class="btn btn-primary"/>
</div>
</div>
</fieldset>
}

<div id="loader" style="display:none">
Working, please wait...
</div>
<div id="ajaxTarget">
</div>


This is the content of my RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapMvcAttributeRoutes();

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);

}


Fidler reports that the request for the route results in HTTP 401 Not Authorized, which kind of makes sense, since this is an Intranet site that was originally configured with Anonymous Access disabled. However, Anonymous Access has been enabled, and the error still occurs.

To resolve the issue, I have tried the following after searching all over the Internet (StackOverflow in particular):


  • I changed IIS to accept anonymous requests and restarted the server. This had no bearing on the outcome.

  • Originally, the action method was decorated with
    HttpPostAttribute
    . Changing it to use
    AcceptVerbs
    has had no bearing on the outcome.

  • I added
    AllowAnonymous
    to the method, and it had no bearing on the outcome.



How do I resolve this issue? Is it resolvable? What other information do I need to provide?

Answer

You have an attribute route definition

[Route("Loan/Find/{loanNumber:int}")]

So when you try to access your action method with yourSite/Loan/Find/123123313 it will go to the action method, but if you try the normal yourSite/Loan/Find?loanNumber=123123123 , It won't work!

Your Ajax.BeginForm html helper generates the form tag's action value as "/Home/Find' and when you submit the form, the value of loanNumber will be submitted in the post body. It is not in the route pattern you defined in the attribute routing definition.

Your code will work if you remove the attribute routing route definition.Attribute routing is mostly used to create nice seo friendly kind url's. So it is best for GET actions, not a form post scenario.

If you still want to keep the attribute routing route pattern, I suggest you to not use the Ajax.BeginForm helper method and handle the ajax call your self where you can build the url the way you want.

So build a normal form, give an id to the form.

@using (Html.BeginForm("Find","Home", FormMethod.Post,new {id="mySearchForm"}))
{
   <div class="col-sm-3">
         <input type="text" id="loanNumber" name="loanNumber"value="@Model.LoanNumber" />
    </div>
    <div class="col-sm-7">
       <input type="submit" value="Search" />
    </div>
}

and have the javascript to listen to the submit event on this form, stop the default behaviour and make an ajax call with the url we want!

$(function () {

    $("#mySearchForm").submit(function(e) {
            e.preventDefault();
            var url = "@Url.Action("Find","Home")";    //change to your controller name     
            url = url + "/" + $("#loanNumber").val();
            $.post(url, function(res) {
                    $("#ajaxTarget").html(res);
            });
    });

});
Comments