【问题标题】:ASP.NET MVC Ajax.BeginForm eats params of submit button clicked. Looks like bugASP.NET MVC Ajax.BeginForm 吃掉点击提交按钮的参数。看起来像bug
【发布时间】:2010-10-09 12:24:20
【问题描述】:

如果您正在使用带有多个提交按钮的 Ajax.BeginForm(),类似于:

// View.aspx
<% using (Ajax.BeginForm("Action", "Controller", 
           new AjaxOptions { UpdateTargetId = "MyControl",  }))
{ %>
    <span id="MyControl">
       <% Html.RenderPartial("MyControl"); %>
    </span>
<% } %>

//MyControl.ascx
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input name="prev" type="submit" value="prev" />
<input name="next" type="submit" value="next" />
//...

一切都很好地提交给控制器,但是请求中没有单击的提交按钮的参数。换句话说,Request["next"] 和 Request["prev"] 始终为 null。

我查看了 Microsoft.MvcAjax.js 中的 JavaScript,看起来函数 Sys_Mvc_MvcHelpers$_serializeForm 完全跳过了“提交”类型的输入。

这似乎根本不合逻辑。还有什么方法可以知道点击了哪个按钮?

对我来说它看起来像一个错误。跳过这些表单参数有什么合乎逻辑的理由吗?

【问题讨论】:

  • 我的印象是 AJAX 表单通常使用按钮输入而不是提交输入,还是我误解了您要执行的操作?
  • 你的意思是
  • 似乎仍然是 MVC 2.0 中的一个错误

标签: asp.net-mvc ajax


【解决方案1】:

更新:2009-11-21 我下载了 MVC Release 2 Preview 2 并查看此问题是否已解决。 我做了一个快速测试,发现与 MVC Release 2 Preview 1 相似的结果。 我认为它还没有修复。

更新:2009-08-07 我下载了 MVC Release 2 Preview 1 并查看此问题是否已解决。 我在脚本 MicrosoftMvcAjax.debug.js 中看到了一个名为 _serializeSubmitButton 的新函数,我看到当 Ajax.BeginForm() 呈现输出时,会有一个 onclick 事件,但是当此事件触发时,它会生成一个错误“ Microsoft JScript 运行时错误:“Sys.Mvc.AsyncForm”为空或不是对象”。

简而言之,它看起来像是尝试了修复,但它还没有工作,或者我需要做更多的事情。坏消息是,如果不是后者,那么在修复完成之前,每个人的 Ajax 表单都会被破坏。

更新:2009-05-07 我今天收到来自 Microsoft 的反馈,确认这是一个错误。他们已经记录了这个缺陷,并表示希望在未来的版本中修复它。

作为参考,我将提交给 Microsoft 的调查详细信息留下。对长篇文章的请求,但也许它对任何试图创建解决方法的人都有用..

MVC 中的 Ajax 支持存在一些问题。为了说明,请考虑网络上几个示例中说明的模式:

//===========
// View.aspx
//===========
<% using (Ajax.BeginForm("Action", "Controller", 
    new AjaxOptions { UpdateTargetId  = "MyControl", HttpMethod = "POST"}))
{ %>
    <span id="MyControl">
       <% Html.RenderPartial("MyControl"); %>
    </span>
<% } %>

//================
// MyControl.ascx
//================
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<input name="startIndex" type="hidden" value="0" />
<%= Ajax.ActionLink("Prev", "PrevAction",
    new AjaxOptions() { UpdateTargetId="MyControl", HttpMethod="POST"}) %>
<%= Ajax.ActionLink("Next", "NextAction", 
    new AjaxOptions() { UpdateTargetId="MyControl", HttpMethod="POST"}) %>
//...

预期:
只是一个列表,用户可以在不更新整个页面的情况下向前和向后翻页。

鉴于此设置。我希望有 2 个链接标记为“上一个”和“下一个”。单击“Prev”应该会触发控制器中的 PrevAction 方法作为帖子,并且名为“startIndex”的隐藏字段中的值应该在请求参数中可用。单击“下一步”链接时,我希望得到类似的结果。

实际:
现实情况是,请求对象不包含任何表单参数,即使它显示它是作为 POST 进来的。

为了使用操作链接获取任何参数,必须通过包含参数的 ActionLink 变体显式提供它们。使用此参数时,参数将成为链接 URL 的一部分,这违背了 POST 的目的。

那么为什么 javascript 出错了?
我深入研究了用于处理提交问题的示例的 javascript 代码,现在我更好地理解了为什么它不处理它。原因似乎与他们连接事件的方式以及我认为 Internet Explorer 的一个缺点有关。

目前的工作方式是 Ajax.BeginForm() 辅助类生成一个带有 onsubmit() 函数的表单标签来拦截表单提交事件。当用户单击提交按钮时,onsubmit() 函数会触发并接收参数,其中之一就是事件。

MicrosoftMvcAjax 脚本查看事件,捆绑应该提交的表单属性并将请求发送到服务器。问题在于,根据 WC3 标准,应该只发布 成功 控件。在提交按钮的情况下,这是实际单击的按钮。在 Internet Explorer 下,无法确定是哪个按钮实际触发了提交事件,因此脚本只会跳过所有提交按钮。

(在 Firefox 中,该事件包含一个名为“explictOriginalTarget”的属性,该属性指向首先实际导致该事件的按钮)

解决方法是什么?
微软应该修复它。但是,如果我们需要更快的东西,我相信唯一的选择是破解 MicrosoftMvcAjax 脚本以不同的方式连接事件。我发现表单可以连接到一个 mousedown 事件句柄,在该事件中,单击的按钮可以保存在一个全局变量中,onsubmit 处理程序可以将其插入到 post 参数中。

这是我测试的一些代码来说明这种技术。我已经确认它适用于 IE8 和 FireFox,但我还没有尝试将它破解到 MVC Ajax 脚本中......如果我有更多时间。我可以在这里发布结果。

   <script type="text/javascript">
     var _clicked = "";
     function onSubmit(e) {
       var targ;
       if (!e) var e = window.event;
       if (e.target) targ = e.target;
       else if (e.srcElement) targ = e.srcElement;
       if (targ.nodeType == 3) //defeat Safari bug
           targ = targ.parentNode;
       alert("OnSubmit:" + _clicked + " was clicked.");
       return false;
     }
     function Click(e) {
       var targ;
       if (!e) var e = window.event;
       if (e.target) targ = e.target;
       else if (e.srcElement) targ = e.srcElement;
       if (targ.nodeType == 3) //defeat Safari bug
           targ = targ.parentNode;
       _clicked = targ.name;
       return true;
   }

【讨论】:

    【解决方案2】:

    为了使您的提交按钮成为符合规范的“成功”控件,它们必须在表单元素中定义:

    http://www.w3.org/TR/html401/interact/forms.html#successful-controls

    如果您不能将提交按钮嵌套在表单中,您可能需要使用 javascript(或 jquery)来提交您的表单并传入一个额外的参数来指示单击了哪个按钮。

    【讨论】:

    • 这正是我认为这是一个错误的原因。提交按钮在表单内,但是当控制器收到请求时,成功的按钮(被点击)没有出现在参数中。根据规格,它们应该在那里。
    • 它们在表单中的具体情况如何?您的 using 语句的结束块应编写表单关闭元素。在我看来,你的按钮在那之后。一个页面上可以有多个表单...
    【解决方案3】:

    我想这在 MVC 2 中已经修复(或者它从未被破坏)。只需确保您的 HTML 标记有效。以下示例应该表明它有效。

    投票.aspx:

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Vote</title>
    </head>
    <body>
    
    <%using (Ajax.BeginForm("Vote", "Voting", new AjaxOptions { UpdateTargetId = "message" }))
      { %>
        <%= Html.Hidden("itemId", "1")%>
        <p>I love ASP.NET MVC!</p>
    
        <input type="submit" name="voteValue" value="+" />
        <input type="submit" name="voteValue" value="-" />
    <%} %>
    
    <p id="message"><%= TempData["message"] %></p>
    
    
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftAjax.js")%>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.js")%>"></script>
    
    </body>
    </html>
    

    VotingController.aspx:

    using System.Web.Mvc;
    
    namespace Examples.FormWithMultipleSubmitButtons.Controllers
    {
        public class VotingController : Controller
        {
            public ViewResult Vote()
            {
                return View();
            }
    
            [HttpPost]
            public ActionResult Vote(int itemId, string voteValue)
            {
                switch(voteValue)
                {
                    case "+":
                        TempData["message"] = "You voted up.";
                        break;
                    case "-":
                        TempData["message"] = "You voted down.";
                        break;
                    default:
                        TempData["message"] = "Your vote was not recognized.";
                        break;
                }
    
                if(Request.IsAjaxRequest())
                {
                    return Content(TempData["message"].ToString());
                }
                else
                {
                    return View();
                }
            }
        }
    }
    

    我今天(2010 年 10 月 8 日)遇到了同样的问题,我的表单带有多个提交按钮。 HTML 未验证。我把它清理干净了。它仍然没有验证(但比原来的错误更少),现在点击按钮的值被提交了。

    【讨论】:

    • 查看我 2009-05-07 的更新评论。我提交给微软,他们确认这是一个错误。此外,我看到了 MVC 2 Preview 1 和 2 中的更改,看起来他们正在努力修复它,但它仍然被破坏。自 2.0 的最终版本发布以来,我已经被淹没并且没有检查过,但我认为它很有可能已经修复。
    【解决方案4】:

    一种可能的解决方法是让每个按钮以单独的形式路由到控制器上的不同操作。 不理想,但可以工作。

    【讨论】:

      【解决方案5】:

      我做了以下事情:

                  <input id="btnSubmit" name="btnSubmit" type="hidden" value="" />
                  <input type="submit" name="btnSubmit" value="Delete" id = "btnDelete"  onclick="$('#btnSubmit').attr('value','Delete');"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  <input type="submit" name="btnSubmit" value="Save New" id = "btnSaveNew"  onclick="$('#btnSubmit').attr('value','Save New');"/>
                  <input type="submit" name="btnSubmit" value="Save" id = "btnSave" onclick="$('#btnSubmit').attr('value','Save');"/>
      

      即定义了一个 id 为“btnSubmit”的隐藏输入类型,并在每个按钮上添加了 onclick 事件为 onclick="$('#btnSubmit').attr('value','Delete');"。这似乎有效 因为我能够获得在控制器中单击的按钮的值:

      public ActionResult SaveCreateBlot(string btnSubmit) {

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-30
        • 1970-01-01
        • 1970-01-01
        • 2011-07-11
        • 2011-05-27
        • 1970-01-01
        • 2011-01-12
        • 2013-11-02
        相关资源
        最近更新 更多