【问题标题】:MVC Ajax post to controller on click eventMVC Ajax 在点击事件上发布到控制器
【发布时间】:2018-12-24 10:11:46
【问题描述】:

单击复选框时,我正在尝试使用 ajax 发布到控制器。它应该更新现有对象。 我的两个问题是:

  • 我无法成功调用控制器操作方法
  • 只有列表中的第一项有点击事件

希望有人能帮忙

使用 ajax 的点击事件

    $('#checkboxID').on('click', function () {
            $.ajax({
                url: '@Url.Action("Attending", "GuestBookings")',
                type: 'POST',
                data: JSON.stringify({
                    "cID": $(this).val('cID'), "eID": $(this).val('eID'), "check": $(this).is(':checked') ? 1 : 0
                }),
                success: function(result) {
                    alert("Succeeded post");
                },
                error: function(result) {
                    alert("Failed to post");
                }
            });
        });

控制器 HttpPost

    [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Attending(string cID, string eID, string check)
        {
            if (ModelState.IsValid)
            {
                var guest = await _context.Guests
                                     .Where(g => g.CustomerId.ToString() == cID && g.EventId.ToString() == eID).FirstOrDefaultAsync();
                if (check == "0")
                {
                    guest.Attended = false;
                }
                else
                {
                    guest.Attended = true;
                }
                try
                {
                    _context.Update(guest);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {

                }
            }
            return Json(true);
        }

复选框

    <tbody>
            @foreach (var item in Model.GuestsBooked)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.Surname)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.FirstName)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Email)
                    </td>
                    <td>
                        @Html.CheckBoxFor(modelItem => item.Attended, new { @id = "checkboxID", @cID = item.Id, @eID = Model.Id })
                    </td>
                    <td>
                        @Html.ActionLink("Details", "Details", "Customers", new { id = item.Id }) |
                        @Html.ActionLink("Unbook", "Delete", "GuestBookings", new { id = item.Id, eventId = Model.Id }) |
                    </td>
                </tr>
            }
        </tbody>

【问题讨论】:

  • 您没有将验证令牌传递给控制器​​,请参阅以下问题的已接受答案:stackoverflow.com/questions/4074199/…
  • 您正在创建多个具有相同 ID 的复选框。将 checkboxID 设为类:@Html.CheckBoxFor(modelItem => item.Attended, new {@class= "checkboxID" } 然后使用 $('.checkboxID').on('click', function () { ... . 这应该可以解决问题 2。

标签: c# ajax asp.net-mvc


【解决方案1】:

我发现您的代码存在一些问题。

首先,在您的视图代码中,您在循环中呈现表格行。在表格行中,您正在使用 CheckBoxFor 辅助方法从 Attended 属性值呈现和绑定复选框。但是您正在使用重载,您将匿名对象作为 html 属性传递,并且将Id 属性值作为checkboxID 传递。这意味着它会将checkboxID 呈现为所有复选框的Id。 Id 值应该是唯一的。如果多个元素的 Id 值相同,则 HTML 无效。这也将导致您的 jQuery click 事件仅适用于集合中的第一项(在第一个表格行中呈现的复选框)。

让我们先解决这个问题。您可以使用另一个属性来连接您的 jQuery click 事件处理程序。例如,这里我添加了一个名为 ajaxupdate 的属性,而不是 Id

@Html.CheckBoxFor(modelItem => item.Attended, 
                                 new {  ajaxupdate="true", 
                                        @cID = item.Id, @eID = Model.Id })

通过此更改,它将像这样呈现复选框元素的 HTML 标记(为了便于阅读,省略了一些属性)。这里重要的部分是 ajaxupdate 属性的存在。

<input ajaxupdate="true" checked="checked" 
       cid="1" eid="2" name="item.Active" type="checkbox" >

现在连接click 事件处理程序,我们可以使用这个数据属性选择器。

$('[ajaxupdate="true"]').on('click', function () {
   // to do : code for ajax call
});

一旦我们进行了这个修复,我们会发现点击事件至少在执行事件处理程序代码。但是您会注意到 ajax 调用从服务器收到 400 bad request 响应。发生这种情况是因为您的操作方法装饰有 ValidateAntiForgeryToken 属性。当一个action方法用这个修饰时,框架会检查提交的请求数据,如果没有找到有效的防伪token,则认为是一个错误的请求,并返回一个400响应。

要解决此问题,您可以显式读取 __RequestVerificationToken 隐藏输入的值(由您视图中的 @Html.AntiForgeryToken() 方法调用生成)并将其发送到您的 ajax 请求标头中。

var t = $("input[name='__RequestVerificationToken']").val();

$.ajax({  url: '@Url.Action("Attending", "Home")',
          type: 'POST',
          headers:
                {
                    "RequestVerificationToken": t
                },
        //existing code for ajax
       })

另一个选项是删除ValidateAntiForgeryToken。但在安全方面受到了打击。'

现在通过上述步骤,我们必须解决 400 响应问题。现在您可能会收到 500 Internal server error 因为您收到一些空引用错误,因为您的操作方法参数未正确绑定您期望的值,并且您的 LINQ 查询可能会返回一个空对象并且您正在尝试更新 @ 987654339@该对象的属性值,没有先检查它是否为NULL对象。

让我们首先修复客户端代码。在复选框的单击事件处理程序中,this 表达式表示被单击的复选框元素。在您的 Razor 代码中,您将 cIDeID 值设置为元素的属性。所以你应该用attr 方法阅读那些。此外,您不需要发送 JSON 字符串版本。只需将 JavaScript 对象作为 data 属性传递即可。

所以客户端代码的最终清理版本将是

$(function () {

    $('[ajaxupdate="true"]').on('click', function () {
        var t = $("input[name='__RequestVerificationToken']").val();
        var $this = $(this);
        $.ajax({
            url: '@Url.Action("Attending", "GuestBookings")',
            type: 'POST',
            headers:
            {
                "RequestVerificationToken": t
            },
            data: {
                "cID": $this.attr('cID'),
                "eID": $this.attr('eID'),
                "check": $this.is(':checked') ? 1 : 0
            },
            success: function(result) {
                alert("Succeeded post");
                console.log(result);
            },
            error: function(xhr,status,errorThrown) {
                alert("Failed to post");
                console.log(errorThrown);
            }
        });
    });

});

我的另一个建议是在您的服务器端代码中使用正确的类型。您正在使用 string 类型作为参数,然后对您的数字属性执行 ToString() 调用以进行比较(使用 Where 子句中的谓词)。不要这样做,而是使用适当的数字类型作为参数。

public async Task<IActionResult> Attending(int cID, int eID, int check)
{
    var guest = await _context.Guests
                              .Where(g => g.CustomerId == cID 
                                       && g.EventId == eID)
                              .FirstOrDefaultAsync();
    if(guest!=null)
    {
       guest.IsActive = check == 0;
       try
       {
           _context.Update(guest);
           await _context.SaveChangesAsync();
       }
       catch (Exception ex)
       {
          // Make sure to log the exception and not leak the ToString version to client.
          return Json(new { status = false, message = ex.ToString() });
       }
       return Json(new { status = true, message = "Updated" });
    }
    return Json(new { status = false, message = "Customer not found!" });
}

在上面的代码中,我将一个带有状态和消息属性的 JSON 对象返回给客户端。对于异常用例,我在消息属性中发送异常的ToString() 版本。只有当您想在客户端看到它时(主要是在您的开发过程中),您才应该这样做。当您将其运送到生产环境时,您不希望将此信息泄露给客户。而是记录它。

在 ajax 调用的 success 处理程序中,您可以根据需要检查 statusmessage 属性。

success: function(result) {
    alert(result.message);                   
},

【讨论】:

  • 这已经做到了!我第一次以这种方式使用 ajax,我从中学到了很多。我认为操作方法是错误的,但我不认为使用 stringify 会导致它出现的问题。谢谢!
猜你喜欢
  • 2016-06-12
  • 1970-01-01
  • 2012-12-30
  • 2017-12-09
  • 2021-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
相关资源
最近更新 更多