1

In my controller, I have an action method that receives a response (200 OK) from a view, but it never runs. The view has a Url.Action that sends a request to the action method, but the method is never called. Triggering the action results in a blank page.

View:

@model Project.Web.ViewModels.SomeModel

<div class="modal fade" id="@("RemoveThingModal" + Model.Id")" tabindex="-1" role="Dialog" aria-labelledby="@(RemoveThingModal" + Model.Id)">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <form id="@("DeleteForm" + Model.Id)" action="@Url.Action("RemoveThing", "Foo")" method="post">
                @Html.HiddenFor(m => m.Id)
                @Html.HiddenFor(m => m.SomeNameString)

                <div class="modal-header">
                    <button type="button" class="Close" data-dismiss="modal" aria-label="Close"><span aria-hidden="True">&times;</span></button>
                    <h4 class="modal-title" id="@("RemoveThingModal" + Model.Id)">@MainResource.DeleteModalTitle</h4>
                </div>
                <div class="modal-body col-md-12">
                    <div>
                        @MainResource.DeleteModalMessage
                        <h3>@string.Format("{0}: {1}", @MainResource.DataTableColumnThingName, Model.Name)</h3>
                    </div>
                </div>

                <div class="modal-footer col-md-12">
                    <button type="button" class="btn btn-default" data-dismiss="modal">@MainResource.CancelLink</button>
                    <button type="submit" id="#@("deleteSubmitButton" + Model.Id)" class="btn btn-danger">@MainResource.RemoveButton</button>
                </div>
            </form>
        </div>
    </div>
</div>

Controller (FooController):

[HttpPost]
public ActionResult RemoveThing(int barId)
{
    System.Diagnostics.Debug.WriteLine("DEBUG: aaaa");    

    try {
        return RedirectToAction("List");
    }
    catch (Exception e)
    {
        throw new Exception(e.Message);
    }
}

Not even the debug print statement is called. At some point, it worked, but the changes were not committed and the undo history was lost.

  • Changing the view's Url.Action to another action in a different controller works.
  • Other actions in the controller work when called from other views.
Erik Humphrey
  • 345
  • 5
  • 18
  • 1
    Check how the cshtml is rendered on the browser. Check the final html form tag has pointing to the correct action method or not. Also, use the dev tools and see the call status when you submit the page. If you can check all these and post the results back to your post will help for someone to point what's wrong. – Thangadurai Dec 18 '19 at 14:55
  • @Thangadurai The form tag seems to render correctly in the DOM. – Erik Humphrey Dec 18 '19 at 15:20
  • @Alex The intent is for the action to run when the user confirms deletion/removal in the modal dialog. I'm not sure of the advantages of `@using (Html.BeginForm()` but it seems to be used elsewhere (albeit much less) in the system. – Erik Humphrey Dec 18 '19 at 15:23
  • I changed it to use Html.BeginForm, but the issue doesn't seem to have resolved. Just `@using (Html.BeginForm("RemoveThing", "Foo", FormMethod.Post))` with no additional arguments. The method is getting a request (still 200 OK) according to VS, but doesn't run. – Erik Humphrey Dec 18 '19 at 15:29
  • 1
    Your 'barId' isn't getting bound. Try this: `@Html.HiddenFor(m => m.Id, new { id = "barId", Name = "barId" })`. Note that `Name` is used by MVC when the form is submitted. – Alex Dec 18 '19 at 15:34

1 Answers1

2

Your action is expecting an integer with name of barId; you're sending it Id so the action isn't being found. Here's what you need:

@model Project.Web.ViewModels.SomeModel

<div class="modal fade" id="@("RemoveThingModal" + Model.Id")" tabindex="-1" role="Dialog" aria-labelledby="@(RemoveThingModal" + Model.Id)">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            @using (Html.BeginForm("RemoveThing","Foo", FormMethod.Post)
            {
                @Html.AntiForgeryToken()
                @Html.HiddenFor(m => m.Id, new { id = "barId", Name = "barId" })
                @Html.HiddenFor(m => m.SomeNameString)

                <div class="modal-header">
                    <button type="button" class="Close" data-dismiss="modal" aria-label="Close"><span aria-hidden="True">&times;</span></button>
                    <h4 class="modal-title" id="@("RemoveThingModal" + Model.Id)">@MainResource.DeleteModalTitle</h4>
                </div>
                <div class="modal-body col-md-12">
                    <div>
                        @MainResource.DeleteModalMessage
                        <h3>@string.Format("{0}: {1}", @MainResource.DataTableColumnThingName, Model.Name)</h3>
                    </div>
                </div>

                <div class="modal-footer col-md-12">
                    <button type="button" class="btn btn-default" data-dismiss="modal">@MainResource.CancelLink</button>
                    <button type="submit" id="#@("deleteSubmitButton" + Model.Id)" class="btn btn-danger">@MainResource.RemoveButton</button>
                </div>
            }
        </div>
    </div>
</div>

And on the action:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RemoveThing(int barId)
{
    System.Diagnostics.Debug.WriteLine("DEBUG: aaaa");    

    try {
        return RedirectToAction("List");
    }
    catch (Exception e)
    {
        throw new Exception(e.Message);
    }
}

The addition of the antiforgery validation isn't related to your problem but it is good practice to prevent cross-site scripting attacks.

Alex
  • 34,699
  • 13
  • 75
  • 158
  • 2
    [`Url.Action(string, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.urlhelper.action?view=aspnet-mvc-5.2#System_Web_Mvc_UrlHelper_Action_System_String_System_String_) just returns a string that is the URL to the specified controller & action. It is method-agnostic, as the method is defined by the `method=` attribute in the `form` element. See [this SO question](https://stackoverflow.com/questions/7709001/html-actionlink-vs-url-action-in-asp-net-razor). – Nathan Miller Dec 18 '19 at 15:34
  • Thanks, @NathanMiller. Was mixing it up with `@Html.ActionLink`, which is always a GET! Was focused on that being the issue when in fact it was the MVC auto binding. See update above. – Alex Dec 18 '19 at 15:55
  • Yeah, I thought that might have been the issue, but the blank page result and status code 200 seemed odd, like there was an overload somewhere doing something different. I'd have expected a different result with incorrect binding. Ah well, the mysteries of MVC sometimes. – Nathan Miller Dec 18 '19 at 16:22
  • @NathanMiller, you're right; why would it do 200 status and blank? But I've seen enough weirdness with MVC that this can go into that encyclopedia :) – Alex Dec 18 '19 at 16:36