提问



是否有一些简单的方法来处理来自同一表单的多个提交按钮?例:


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>


知道如何在ASP.NET Framework Beta中执行此操作吗?我用谷歌搜索过的所有例子中都有单个按钮。

最佳参考


以下是基于Maartin Balliauw的帖子和评论的多重提交按钮问题的基于属性的解决方案。[91]


[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
    public string Name { get; set; }
    public string Argument { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var keyValue = string.Format("{0}:{1}", Name, Argument);
        var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
            isValidName = true;
        }

        return isValidName;
    }
}


剃刀:


<form action="" method="post">
 <input type="submit" value="Save" name="action:Save" />
 <input type="submit" value="Cancel" name="action:Cancel" />
</form>


和控制器:


[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }

[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }


更新: Razor页面看起来提供了开箱即用的相同功能。对于新的开发,可能更可取。[92]

其它参考1


为提交按钮指定名称,然后在控制器方法中检查提交的值:


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="Send" />
<input type="submit" name="submitButton" value="Cancel" />
<% Html.EndForm(); %>


张贴到


public class MyController : Controller {
    public ActionResult MyAction(string submitButton) {
        switch(submitButton) {
            case "Send":
                // delegate sending to another controller action
                return(Send());
            case "Cancel":
                // call another action to perform the cancellation
                return(Cancel());
            default:
                // If they've submitted the form without a submitButton, 
                // just return the view again.
                return(View());
        }
    }

    private ActionResult Cancel() {
        // process the cancellation request here.
        return(View("Cancelled"));
    }

    private ActionResult Send() {
        // perform the actual send operation here.
        return(View("SendConfirmed"));
    }

}


编辑:


要扩展此方法以使用本地化站点,请将消息隔离在其他位置(例如,将资源文件编译为强类型资源类)


然后修改代码,使其工作如下:


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="<%= Html.Encode(Resources.Messages.Send)%>" />
<input type="submit" name="submitButton" value="<%=Html.Encode(Resources.Messages.Cancel)%>" />
<% Html.EndForm(); %>


你的控制器应该是这样的:


// Note that the localized resources aren't constants, so 
// we can't use a switch statement.

if (submitButton == Resources.Messages.Send) { 
    // delegate sending to another controller action
    return(Send());

} else if (submitButton == Resources.Messages.Cancel) {
     // call another action to perform the cancellation
     return(Cancel());
}

其它参考2


您可以检查操作中的名称,但是您可以考虑这是否是一个好的设计。考虑操作的责任并且不要将此设计过多地与按钮名称等UI方面相结合是一个好主意。因此,请考虑使用2个表单和2个操作:


<% Html.BeginForm("Send", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<% Html.EndForm(); %>

<% Html.BeginForm("Cancel", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>


此外,在取消的情况下,您通常只是不处理表单并转到新的URL。在这种情况下,您根本不需要提交表单,只需要一个链接:


<%=Html.ActionLink("Cancel", "List", "MyController") %>

其它参考3


Eilon建议你可以这样做:



  如果你有多个按钮
  可以通过给予来区分它们
  每个按钮一个名字:


<input type="submit" name="SaveButton" value="Save data" />
<input type="submit" name="CancelButton" value="Cancel and go back to main page" />

  
  在您的控制器操作方法中
  可以添加以。之后命名的参数
  HTML输入标签名称:


public ActionResult DoSomeStuff(string saveButton, string
cancelButton, ... other parameters ...)
{ ... }

  
  如果任何值被发布到其中一个
  那些参数,这意味着
  按钮是被点击的那个。
  Web浏览器只会发布一个值
  对于单击的一个按钮。
  所有其他值将为null。


if (saveButton != null) { /* do save logic */ }
if (cancelButton != null) { /* do cancel logic */ }



我喜欢这种方法,因为它不依赖于提交按钮的value属性,它比指定的名称更有可能更改,并且不需要启用javascript


看到:
http://forums.asp.net/p/1369617/2865166.aspx#2865166[93]

其它参考4


刚写了一篇关于这个的帖子:
ASP.NET MVC的多个提交按钮:[94]


基本上,我没有使用ActionMethodSelectorAttribute,而是使用
ActionNameSelectorAttribute,它允许我假装动作名称是我想要的。幸运的是,ActionNameSelectorAttribute并不只是让我指定操作名称,而是可以选择当前操作是否与请求匹配。


所以有我的课(顺便说一下,我不太喜欢这个名字):


public class HttpParamActionAttribute : ActionNameSelectorAttribute {
    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
} 


要使用只需定义这样的表单:


<% using (Html.BeginForm("Action", "Post")) { %>
  <!— …form fields… -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %> 


和控制器有两种方法


public class PostController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SaveDraft(…) {
        //…
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Publish(…) {
        //…
    } 
}


如您所见,该属性不需要您指定任何内容。此外,按钮的名称将直接转换为方法名称。另外(我还没试过)这些也可以作为正常的动作,所以你可以发布到任何一个
直。

其它参考5


我建议有兴趣的人士看看Maarten Balliauw的解决方案。我认为这很优雅。[95]


如果链接消失,则使用应用于控制器操作的MultiButton属性来指示该操作应该与哪个按钮单击相关联。

其它参考6


它很短且套房:



  这是由Jeroen Dop 回答的



<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />


并在代码behinde中这样做


 if( Request.Form["submitbutton1"] != null)
{
    // Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
       // code for function 2
}


祝你好运。

其它参考7


你应该能够命名按钮并给它们一个值;然后将此名称映射为操作的参数。或者,使用2个单独的动作链接或2个表单。

其它参考8


你可以写:


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>


然后在页面中检查名称==发送或名称==取消...

其它参考9


我对ActionSelectName不喜欢的是为控制器中的每个操作方法调用IsValidName;我不知道为什么它以这种方式工作。我喜欢这样一个解决方案,其中每个按钮都有不同的名称,但是我不喜欢你必须在动作方法中使用与表单中的按钮一样多的参数。我为所有按钮创建了一个枚举按钮类型:


public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}


而不是ActionSelectName,我使用ActionFilter:


public class MultipleButtonsEnumAttribute : ActionFilterAttribute
{
    public Type EnumType { get; set; }

    public MultipleButtonsEnumAttribute(Type enumType)
    {
        EnumType = enumType;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        foreach (var key in filterContext.HttpContext.Request.Form.AllKeys)
        {
            if (Enum.IsDefined(EnumType, key))
            {
                var pDesc = filterContext.ActionDescriptor.GetParameters()
                    .FirstOrDefault(x => x.ParameterType == EnumType);
                filterContext.ActionParameters[pDesc.ParameterName] = Enum.Parse(EnumType, key);
                break;
            }
        }
    }
}


过滤器将在表单数据中找到按钮名称,如果按钮名称与枚举中定义的任何按钮类型匹配,它将在操作参数中找到ButtonType参数:


[MultipleButtonsEnumAttribute(typeof(ButtonType))]
public ActionResult Manage(ButtonType buttonPressed, ManageViewModel model)
{
    if (button == ButtonType.Cancel)
    {
        return RedirectToAction("Index", "Home");
    }
    //and so on
    return View(model)
}


然后在视图中,我可以使用:


<input type="submit" value="Button Cancel" name="@ButtonType.Cancel" />
<input type="submit" value="Button Submit" name="@ButtonType.Submit" />

其它参考10


这对我最有用:


<input type="submit" value="Delete" name="onDelete" />
<input type="submit" value="Save" name="onSave" />


public ActionResult Practice(MyModel model, string onSave, string onDelete)
{
    if (onDelete != null)
    {
        // Delete the object
        ...
        return EmptyResult();
    }

    // Save the object
    ...
    return EmptyResult();
}

其它参考11


如果您对HTML 5的使用没有限制,可以将<button>标签与formaction属性一起使用:


<form action="demo_form.asp" method="get">
   First name: <input type="text" name="fname" /><br />
   Last name: <input type="text" name="lname" /><br />
   <button type="submit">Submit</button><br />
   <button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>


参考:http://www.w3schools.com/html5/att_button_formaction.asp [97]

其它参考12


我也遇到了这个问题,但通过添加name属性找到了一个相当合理的解决方案。我不记得在其他语言中遇到这个问题。


http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2[98]



  • ...

  • 如果表单包含多个提交按钮,则只有激活的提交按钮成功。

  • ...



这意味着以下代码value属性可以在没有 need 的情况下进行更改,本地化,国际化,以便额外的代码检查强类型资源文件或常量。


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="send" value="Send" />
<input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="draft" value="Save as draft" />
<% Html.EndForm(); %>`


在接收端,您只需要检查您的任何已知提交类型是否[[t null


public ActionResult YourAction(YourModel model) {

    if(Request["send"] != null) {

        // we got a send

    }else if(Request["cancel"]) {

        // we got a cancel, but would you really want to post data for this?

    }else if(Request["draft"]) {

        // we got a draft

    }

}

其它参考13


David Findley在他的ASP.Net博客上写了大约3种不同的选项。


阅读同一表格中的文章多个按钮,看看他的解决方案,以及每个按钮的优缺点。恕我直言,他提供了一个非常优雅的解决方案,利用你用你的行动装饰的属性。[99]

其它参考14


这是我使用的技术,我还没有在这里看到它。链接(由Saajid Ismail发布
)激发这种解决方案的是http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx)。它适应了Dylan Beattie的回答,没有任何问题进行本地化。[100]


在视图中:


<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<button name="button" value="send"><%: Resources.Messages.Send %></button>
<button name="button" value="cancel"><%: Resources.Messages.Cancel %></button>
<% Html.EndForm(); %>


在控制器中:


public class MyController : Controller 
{
    public ActionResult MyAction(string button)
    {
         switch(button)
         {
             case "send":
                 this.DoSend();
                 break;
             case "cancel":
                 this.DoCancel();
                 break;
         }
    }
}

其它参考15


此脚本允许指定data-form-action属性,该属性将作为所有浏览器中的HTML5格式属性(以不显眼的方式):


$(document).on('click', '[type="submit"][data-form-action]', function(event) {
    var $this = $(this),
    var formAction = $this.attr('data-form-action'),
    $form = $($this.closest('form'));
    $form.attr('action', formAction);             
});


包含该按钮的表单将发布到data-form-action属性中指定的URL:


<button type="submit" data-form-action="different/url">Submit</button>   


这需要jQuery 1.7。对于以前的版本,您应该使用live()而不是on()

其它参考16


如果您的浏览器支持输入按钮的属性格式(IE 10+,不确定其他浏览器),则以下内容应该有效:


@using (Html.BeginForm()){
    //put form inputs here

<input id="sendBtn" value="Send" type="submit" formaction="@Url.Action("Name Of Send Action")" />

<input id="cancelBtn" value="Cancel" type="submit" formaction="@Url.Action("Name of Cancel Action") />

}

其它参考17


您可以通过三种方式解决上述问题



  1. HTML方式

  2. Jquery方式

  3. ActionNameSelectorAttribute方式



以下是一个视频,以演示的方式总结了所有这三种方法。


https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940[101]


HTML方式: -


在HTML方式中,我们需要创建两个表单,并在每个表单中放置提交按钮。每个表单的操作都将指向不同/相应的操作。您可以看到下面的代码,第一个表单发布到Action1,第二个表单将发布到Action2,具体取决于单击提交按钮。


<form action="Action1" method=post>
<input type=”submit” name=”Submit1”/>
</form>

<form action="Action2" method=post>
<input type=”submit” name=”Submit2”>
</form>


Ajax方式: -


如果你是一个Ajax爱好者,这第二个选项会激发你更多。在Ajax方式中,我们可以创建两个不同的函数Fun1和Fun1,请参阅下面的代码。这些函数将使用JQUERY或任何其他框架进行Ajax调用。这些功能中的每一个都与提交按钮的OnClick事件绑定在一起。这些函数中的每一个都调用各自的动作名称。


<Script language="javascript">
function Fun1()
{
$.post(“/Action1”,null,CallBack1);
}
function Fun2()
{
$.post(“/Action2”,null,CallBack2);
}
</Script>

<form action="/Action1" method=post>
<input type=submit name=sub1 onclick=”Fun2()”/>
</form>
<form action="/Action2" method=post>
<input type=submit name=sub2 onclick=”Fun1()”/>
</form>


使用ActionNameSelectorAttribute: -


这是一个伟大而干净的选择。 ActionNameSelectorAttribute是一个简单的属性类,我们可以编写决策逻辑,决定可以执行哪个操作。


所以第一件事就是HTML,我们需要在提交按钮上加上适当的名称,以便在服务器上识别它们。


您可以看到我们已将保存和删除添加到按钮名称。您还可以注意到,我们刚刚将控制器名称设置为Customer而不是特定的操作名称。我们希望操作名称由ActionNameSelectorAttribute决定。


<form action=”Customer” method=post>
<input type=submit value="Save" name="Save" /> <br />
<input type=submit value="Delete" name="Delete"/>
</form>


因此,当单击提交按钮时,它首先命中ActionNameSelector属性,然后根据触发的提交,它将调用相应的操作。





所以第一步是创建一个继承自ActionNameSelectorAttribute类的类。在这个类中,我们创建了一个简单的属性Name。


我们还需要覆盖返回true或flase的IsValidName函数。这个函数是我们编写逻辑的地方,是否必须执行一个动作。因此,如果此函数返回true,则执行操作,否则执行操作。


public class SubmitButtonSelector : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
        {
            // Try to find out if the name exists in the data sent from form
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
            if (value != null)
            {
                return true;
            }
            return false;

        }
    }


上述函数的主要核心在下面的代码中。 ValueProvider集合包含从表单发布的所有数据。所以它首先查找Name值,如果在HTTP请求中找到它,则返回true,否则返回false。


var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
      {
        return true;
      }
      return false;


然后可以在相应的动作上修饰该属性类,并且可以提供相应的名称值。因此,如果提交正在执行此操作,并且如果名称与HTML提交按钮名称匹配,则它会进一步执行操作,否则执行操作。


public class CustomerController : Controller
{
        [SubmitButtonSelector(Name="Save")]
        public ActionResult Save()
        {
            return Content("Save Called");
        }
        [SubmitButtonSelector(Name = "Delete")]
        public ActionResult Delete()
        {
            return Content("Delete Called");
        }
}

其它参考18


这是我为处理多个图像和/或文本按钮而编写的扩展方法。


这是图像按钮的HTML:


<input id="btnJoin" name="Join" src="/content/images/buttons/btnJoin.png" 
       type="image">


或者用于文本提交按钮:


<input type="submit" class="ui-button green" name="Submit_Join" value="Add to cart"  />
<input type="submit" class="ui-button red" name="Submit_Skip" value="Not today"  />


以下是您使用form.GetSubmitButtonName()从控制器调用的扩展方法。对于图像按钮,它会查找带.x的表单参数(表示单击图像按钮)并提取名称。对于常规input按钮,它会查找以Submit_开头的名称,然后从中提取命令。因为我抽象了确定命令的逻辑,所以您可以在客户端上的图像+文本按钮之间切换,而无需更改服务器端代码。


public static class FormCollectionExtensions
{
    public static string GetSubmitButtonName(this FormCollection formCollection)
    {
        return GetSubmitButtonName(formCollection, true);
    }

    public static string GetSubmitButtonName(this FormCollection formCollection, bool throwOnError)
    {
        var imageButton = formCollection.Keys.OfType<string>().Where(x => x.EndsWith(".x")).SingleOrDefault();
        var textButton = formCollection.Keys.OfType<string>().Where(x => x.StartsWith("Submit_")).SingleOrDefault();

        if (textButton != null)
        {
            return textButton.Substring("Submit_".Length);
        }

        // we got something like AddToCart.x
        if (imageButton != null)
        {
            return imageButton.Substring(0, imageButton.Length - 2);
        }

        if (throwOnError)
        {
            throw new ApplicationException("No button found");
        }
        else
        {
            return null;
        }
    }
}


注意:对于文本按钮,您必须在名称前添加Submit_。我更喜欢这种方式,因为它意味着您可以更改文本(显示)值而无需更改代码。与SELECT元素不同,INPUT按钮只有值而没有单独的文本属性。我的按钮在不同的上下文中表示不同的东西 - 但映射到相同的命令。我更喜欢以这种方式提取名称而不是编码== "Add to cart"

其它参考19


我没有足够的代表在正确的地方发表评论,但我整天都花在这上面,所以想分享。


在尝试实现MultipleButtonAttribute解决方案时ValueProvider.GetValue(keyValue)会错误地回来null


事实证明,当它应该是4.0(其他程序集是4.0)时,我引用的是System.Web.MVC 3.0版。我不知道为什么我的项目没有正确升级,我没有其他明显的问题。


所以如果你的ActionNameSelectorAttribute不起作用......检查一下。

其它参考20


我试图对所有解决方案进行综合,并创建了一个[[ButtenHandler]]属性,可以轻松处理表单上的多个按钮。


我在ASP.NET MVC中的CodeProject多参数化(可本地化)表单按钮上进行了描述。[102]


要处理此按钮的简单情况:


<button type="submit" name="AddDepartment">Add Department</button>


您将拥有类似以下操作方法的内容:


[ButtonHandler()]
public ActionResult AddDepartment(Company model)
{
    model.Departments.Add(new Department());
    return View(model);
}


注意按钮的名称是如何与action方法的名称匹配的。本文还介绍了如何使用带有索引的值和按钮的按钮。

其它参考21


//model
    public class input_element
        {
         public string Btn { get; set; }
        }   

//views--submit btn can be input type also...
    @using (Html.BeginForm())
    {
            <button type="submit" name="btn" value="verify">
             Verify data</button>
            <button type="submit" name="btn" value="save">
             Save data</button>    
            <button type="submit" name="btn" value="redirect">
                 Redirect</button>
    }

//controller

    public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";
            return View();
        }

        [HttpPost]
        public ActionResult About(input_element model)
        {
                if (model.Btn == "verify")
                {
                // the Verify button was clicked
                }
                else if (model.Btn == "save")
                {
                // the Save button was clicked
                } 
                else if (model.Btn == "redirect")
                {
                // the Redirect button was clicked
                } 
                return View();
        }

其它参考22


HttpParamActionAttribute方法的修改版本,但有一个错误修复,没有导致过期/无效会话回发错误。要查看当前站点是否存在问题,请在窗口中打开表单,然后在单击SavePublish之前,打开一个重复的窗口,然后注销。现在回到第一个窗口,尝试使用任一按钮提交表单。对我来说,我收到了一个错误,所以这个改变为我解决了这个问题。为了简洁起见,我省略了一堆东西,但你应该明白这个想法。关键部分是在属性上包含ActionName并确保传入的名称是显示表单的View的名称


属性类


[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
    private readonly string actionName;

    public HttpParamActionAttribute(string actionName)
    {
        this.actionName = actionName;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals(this.actionName, StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
}


控制器


[Authorize(Roles="CanAddContent")]
public ActionResult CreateContent(Guid contentOwnerId)
{
    var viewModel = new ContentViewModel
    {
        ContentOwnerId = contentOwnerId
        //populate rest of view model
    }
    return View("CreateContent", viewModel);
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult SaveDraft(ContentFormModel model)
{
    //Save as draft
    return RedirectToAction("CreateContent");
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult Publish(ContentFormModel model)
{
    //publish content
    return RedirectToAction("CreateContent");
}


查看


@using (Ajax.BeginForm("CreateContent", "MyController", new { contentOwnerId = Model.ContentOwnerId }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(x => x.ContentOwnerId)

    <!-- Rest of your form controls -->
    <input name="SaveDraft" type="submit" value="SaveDraft" />
    <input name="Publish" type="submit" value="Publish" />
}

其它参考23


这是我找到的最佳方式:


http://iwayneo.blogspot.co.uk/2013/10/aspnet-mvc-action-selector-with-list.html[103]


这是代码:


    /// <summary>
    /// ActionMethodSelector to enable submit buttons to execute specific action methods.
    /// </summary>
    public class AcceptParameterAttribute : ActionMethodSelectorAttribute
   {
        /// <summary>
        /// Gets or sets the value to use to inject the index into
        /// </summary>
       public string TargetArgument { get; set; }

       /// <summary>
       /// Gets or sets the value to use in submit button to identify which method to select. This must be unique in each controller.
       /// </summary>
       public string Action { get; set; }

       /// <summary>
       /// Gets or sets the regular expression to match the action.
       /// </summary>
       public string ActionRegex { get; set; }

       /// <summary>
       /// Determines whether the action method selection is valid for the specified controller context.
       /// </summary>
       /// <param name="controllerContext">The controller context.</param>
       /// <param name="methodInfo">Information about the action method.</param>
       /// <returns>true if the action method selection is valid for the specified controller context; otherwise, false.</returns>
       public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
       {

           if (controllerContext == null)
           {
               throw new ArgumentNullException("controllerContext");
           }

           Func<NameValueCollection> formGetter;
           Func<NameValueCollection> queryStringGetter;

           ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);

           var form = formGetter();
           var queryString = queryStringGetter();

           var req = form.AllKeys.Any() ? form : queryString;

           if (!string.IsNullOrEmpty(this.ActionRegex))
           {
               foreach (var key in req.AllKeys.Where(k => k.StartsWith(Action, true, System.Threading.Thread.CurrentThread.CurrentCulture)))
               {
                   if (key.Contains(":"))
                   {
                       if (key.Split(':').Count() == this.ActionRegex.Split(':').Count())
                       {
                           bool match = false;
                           for (int i = 0; i < key.Split(':').Count(); i++)
                           {
                               if (Regex.IsMatch(key.Split(':')[0], this.ActionRegex.Split(':')[0]))
                               {
                                   match = true;
                               }
                               else
                               {
                                   match = false;
                                   break;
                               }
                           }

                           if (match)
                           {
                               return !string.IsNullOrEmpty(req[key]);
                           }
                       }
                   }
                   else
                   {
                       if (Regex.IsMatch(key, this.Action + this.ActionRegex))
                       {
                           return !string.IsNullOrEmpty(req[key]);
                       }
                   }

               }
               return false;
           }
           else
           {
               return req.AllKeys.Contains(this.Action);
           }
       }
   }


享受无需代码气味的多提交按钮未来。


谢谢

其它参考24


我参加聚会很晚了,但是这里......
我的实现借用@mkozicki,但需要较少的硬编码字符串才能出错。 需要使用Framework 4.5+ 。实质上,控制器方法名称应该是路由的关键。


标记即可。按钮名称必须用"action:[controllerMethodName]"键入


(注意使用C#6 nameof API,提供您希望调用的控制器方法名称的特定于类型的引用。[104]


<form>
    ... form fields ....
    <button name="action:@nameof(MyApp.Controllers.MyController.FundDeathStar)" type="submit" formmethod="post">Fund Death Star</button>
    <button name="action:@nameof(MyApp.Controllers.MyController.HireBoba)" type="submit" formmethod="post">Hire Boba Fett</button>
</form>


控制器:


namespace MyApp.Controllers
{
    class MyController
    {    
        [SubmitActionToThisMethod]
        public async Task<ActionResult> FundDeathStar(ImperialModel model)
        {
            await TrainStormTroopers();
            return View();
        }

        [SubmitActionToThisMethod]
        public async Task<ActionResult> HireBoba(ImperialModel model)
        {
            await RepairSlave1();
            return View();
        }
    }
}


属性魔法。注意使用CallerMemberName善良。[105]


[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitActionToThisMethodAttribute : ActionNameSelectorAttribute
{        
    public SubmitActionToThisMethodAttribute([CallerMemberName]string ControllerMethodName = "")
    {
        controllerMethod = ControllerMethodName;
        actionFormat = string.Concat(actionConstant, ":", controllerMethod);
    }
    const string actionConstant = "action";
    readonly string actionFormat;
    readonly string controllerMethod;

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var value = controllerContext.Controller.ValueProvider.GetValue(actionFormat);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[actionConstant] = controllerMethod;
            isValidName = true;
        }
        return isValidName;
    }
}

其它参考25


我的JQuery方法使用扩展方法:


public static MvcHtmlString SubmitButtonFor<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string value) where TController : Controller
{
    RouteValueDictionary routingValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);

    var onclick = string.Format("$('form').first().attr('action', '/{0}')", string.Join("/", routingValues.Values.ToArray().Where(x => x != null).Select(x => x.ToString()).ToArray()));
    var html = "<input type=\"submit\" value=\"" + value + "\" onclick=\"" + onclick + "\" />";

    return MvcHtmlString.Create(html);
}


你可以像这样使用它:


@(Html.SubmitButtonFor<FooController>(c => c.Save(null), "Save"))


它呈现如下:


<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >

其它参考26


对于每个提交按钮,只需添加:


$('#btnSelector').click(function () {

    $('form').attr('action', "/Your/Action/);
    $('form').submit();

});

其它参考27


基于mkozicki答案,我提出了一个不同的解决方案。我仍然使用ActionNameSelectorAttribute但我需要处理两个按钮保存和同步。他们几乎一样,所以我不想做两件事。


属性:


public class MultipleButtonActionAttribute : ActionNameSelectorAttribute
{        
    private readonly List<string> AcceptedButtonNames;

    public MultipleButtonActionAttribute(params string[] acceptedButtonNames)
    {
        AcceptedButtonNames = acceptedButtonNames.ToList();
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {            
        foreach (var acceptedButtonName in AcceptedButtonNames)
        {
            var button = controllerContext.Controller.ValueProvider.GetValue(acceptedButtonName);
            if (button == null)
            {
                continue;
            }                
            controllerContext.Controller.ControllerContext.RouteData.Values.Add("ButtonName", acceptedButtonName);
            return true;
        }
        return false;
    }
}


查看


<input type="submit" value="Save" name="Save" />
<input type="submit" value="Save and Sync" name="Sync" />


控制器


 [MultipleButtonAction("Save", "Sync")]
 public ActionResult Sync(OrgSynchronizationEditModel model)
 {
     var btn = this.RouteData.Values["ButtonName"];


我还想指出,如果行动做不同的事情,我可能会关注mkozicki帖子。

其它参考28


[HttpPost]
public ActionResult ConfirmMobile(string nameValueResend, string nameValueSubmit, RegisterModel model)
    {
        var button = nameValueResend ?? nameValueSubmit;
        if (button == "Resend")
        {

        }
        else
        {

        }
    }


    Razor file Content:
    @using (Html.BeginForm()
    {
        <div class="page registration-result-page">

            <div class="page-title">
                <h1> Confirm Mobile Number</h1>
            </div>

            <div class="result">
                @Html.EditorFor(model => model.VefificationCode)
                @Html.LabelFor(model => model.VefificationCode, new { })
                @Html.ValidationMessageFor(model => model.VefificationCode)
            </div>
            <div class="buttons">
                <button type="submit" class="btn" name="nameValueResend" value="Resend">
                    Resend
                </button>
                <button type="submit" class="btn" name="nameValueSubmit" value="Verify">
                    Submit
                </button>

            </div>
            </div>

    }

其它参考29


当使用ajax表单时,我们可以将ActionLinks与POST HttpMethod一起使用
序列化AjaxOptions.OnBegin事件中的表单。


我们假设你有两个动作,InsertAction和UpdateAction:


<form>
    @Html.Hidden("SomeField", "SomeValue")

    @Ajax.ActionLink(
        "Insert",
        "InsertAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
    @Ajax.ActionLink(
        "Update",
        "UpdateAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
</form>


使用Javascript


function OnBegin(xhr, settings) {
    settings.data = $("form").serialize();
}