Как вы обрабатываете несколько кнопок отправки в ASP.NET MVC Framework?

Есть ли простой способ обработки нескольких кнопок отправки из одной формы? Пример:

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

Любая идея, как это сделать в ASP.NET Framework Beta? Все примеры, на которые я гуглил, содержат отдельные кнопки.

35 ответов

Решение

Вот в основном чистое, основанное на атрибутах решение проблемы с несколькими кнопками отправки, основанное на публикации и комментариях от Maartin Balliauw.

[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 страниц выглядит так, чтобы обеспечить такую ​​же функциональность из коробки. Для новых разработок это может быть предпочтительнее.

Дайте вашим кнопкам отправки имя, а затем проверьте отправленное значение в методе вашего контроллера:

<% 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 форм и 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") %>

Эйлон предлагает сделать это так:

Если у вас есть более одной кнопки, вы можете различить их, дав каждой кнопке имя:

<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 ...)
{ ... }

Если какое-либо значение публикуется в одном из этих параметров, это означает, что кнопка была нажата. Веб-браузер будет публиковать значение только для одной кнопки, которая была нажата. Все остальные значения будут нулевыми.

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

Мне нравится этот метод, так как он не опирается на свойство value кнопок отправки, которое с большей вероятностью изменится, чем назначенные имена, и не требует включения JavaScript

См.: http://forums.asp.net/p/1369617/2865166.aspx

Только что написал пост об этом: Несколько кнопок отправки с ASP.NET MVC:

В основном вместо использования 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(…) {
        //…
    } 
}

Как видите, атрибут вообще не требует указывать что-либо. Кроме того, названия кнопок переводятся прямо в имена методов. Кроме того (я не пробовал это), они также должны работать как обычные действия, так что вы можете публиковать сообщения на любом из них напрямую.

Это коротко и люкс:

На это ответил Jeroen Dop

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

и делать это в коде

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

Удачи.

Я хотел бы предложить заинтересованным сторонам взглянуть на решение Мартена Баллиау. Я думаю, что это очень элегантно.

Если ссылка исчезает, она использует MultiButton Атрибут применяется к действию контроллера, чтобы указать, к какому нажатию кнопки должно относиться это действие.

Вы должны быть в состоянии назвать кнопки и дать им значение; затем сопоставьте это имя в качестве аргумента действия. В качестве альтернативы, используйте 2 отдельные action-ссылки или 2 формы.

Вы могли бы написать:

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

А затем на странице проверьте, если имя == "Отправить" или имя == "Отмена"...

Я также столкнулся с этой "проблемой", но нашел довольно логичное решение, добавив name приписывать. Я не мог вспомнить наличие этой проблемы на других языках.

http://www.w3.org/TR/html401/interact/forms.html

  • ...
  • Если форма содержит более одной кнопки отправки, успешной будет только активированная кнопка отправки.
  • ...

Имеется в виду следующий код value атрибуты могут быть изменены, локализованы, интернационализированы без необходимости дополнительной проверки кода строго типизированных файлов ресурсов или констант.

<% 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(); %>`

На принимающей стороне вам нужно будет только проверить, не является ли какой-либо из ваших известных типов отправки не 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

    }

}

Что мне не нравится в 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" />

Вот что работает лучше всего для меня:

<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();
}

Если ваш браузер поддерживает формирование атрибутов для кнопок ввода (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") />

}

Если у вас нет ограничений на использование 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

Есть три способа решения вышеуказанной проблемы.

  1. HTML способ
  2. Jquery способ
  3. Способ "ActionNameSelectorAttribute"

Ниже видео, которое демонстрирует все три подхода в демонстративной форме.

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

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", см. Код ниже. Эти функции будут выполнять вызовы Ajax с использованием JQUERY или любого другого фреймворка. Каждая из этих функций связана с событиями "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, это указать имена кнопок для отправки на сервер.

Вы можете видеть, что мы поместили "Сохранить" и "Удалить" в названия кнопок. Также вы можете заметить, что в действии мы просто указали имя контроллера "Клиент", а не конкретное имя действия. Мы ожидаем, что имя действия будет определяться "ActionNameSelectorAttribute".

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

Поэтому, когда нажимается кнопка "Отправить", она сначала обращается к атрибуту "ActionNameSelector", а затем, в зависимости от того, какая отправка запущена, вызывает соответствующее действие.

Поэтому первым шагом является создание класса, который наследуется от класса "ActionNameSelectorAttribute". В этом классе мы создали простое свойство "Имя".

Нам также нужно переопределить функцию "IsValidName", которая возвращает true или flase. Эта функция - то, где мы пишем логику, должно ли действие быть выполнено или нет. Таким образом, если эта функция возвращает 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" содержит все данные, отправленные из формы. Поэтому он сначала ищет значение "Имя" и, если оно найдено в HTTP-запросе, возвращает значение "истина" или же возвращает "ложь".

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");
        }
}

Дэвид Финдли (David Findley) пишет о 3 различных вариантах для этого в своем блоге ASP.Net.

Прочитайте в статье несколько кнопок в одной форме, чтобы увидеть его решения, а также преимущества и недостатки каждой из них. ИМХО, он предлагает очень элегантное решение, которое использует атрибуты, которыми вы украшаете свое действие.

Это техника, которую я бы использовал, и я пока не вижу ее здесь. Ссылка (опубликованная Saajid Ismail), которая вдохновляет это решение, является http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx). Это адаптирует ответ Дилана Битти, чтобы сделать локализацию без каких-либо проблем.

В представлении:

<% 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;
         }
    }
}
[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>

    }

Этот скрипт позволяет указать атрибут 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);             
});

Форма, содержащая кнопку, будет размещена по URL-адресу, указанному в атрибуте data-form-action:

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

Это требует jQuery 1.7. Для предыдущих версий вы должны использовать live() вместо on(),

У меня недостаточно представителей, чтобы комментировать в правильном месте, но я потратил на это целый день, поэтому хочу поделиться.

При попытке реализовать решение "MultipleButtonAttribute" ValueProvider.GetValue(keyValue) неправильно вернется null,

Оказалось, что я ссылался на System.Web.MVC версии 3.0, когда она должна была быть 4.0 (другие сборки - 4.0). Я не знаю, почему мой проект не обновился правильно, и у меня не было других очевидных проблем.

Так что если ваш ActionNameSelectorAttribute не работает... проверьте это.

Вот метод расширения, который я написал для обработки нескольких кнопок изображения и / или текста.

Вот 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_ и извлекает команду из впоследствии. Поскольку я абстрагируюсь от логики определения "команды", вы можете переключаться между кнопками image + text на клиенте, не меняя код на стороне сервера.

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",

Я довольно поздно на вечеринку, но здесь идет... Моя реализация заимствует из @mkozicki, но требует меньше жестко закодированных строк, чтобы ошибиться. Framework 4.5+ требуется. По сути, имя метода контроллера должно быть ключом к маршрутизации.

Разметка Имя кнопки должно быть набрано с "action:[controllerMethodName]"

(обратите внимание на использование C# 6 nameof API, предоставляющего специфическую для типа ссылку на имя метода контроллера, который вы хотите вызвать.

<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 благость.

[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;
    }
}

Это лучший способ, который я нашел:

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

Вот код:

    /// <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);
           }
       }
   }

Наслаждайтесь несколькими кнопками отправки без запаха.

благодарю вас

Я попытался обобщить все решения и создал атрибут [ButtenHandler], который позволяет легко обрабатывать несколько кнопок в форме.

Я описал это на многопараметрических (локализуемых) кнопках формы CodeProject в ASP.NET MVC.

Чтобы обработать простой случай этой кнопки:

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

У вас будет что-то вроде следующего метода действия:

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

Обратите внимание, как имя кнопки совпадает с именем метода действия. В статье также описано, как иметь кнопки со значениями и кнопки с индексами.

//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();
        }

Модифицированная версия HttpParamActionAttribute метод, но с исправлением ошибки, которая не вызывала ошибку при истекших / недействительных обратных передачах сеанса. Чтобы увидеть, если это проблема с вашим текущим сайтом, откройте форму в окне и перед тем, как перейти к Save или же Publishоткройте двойное окно и выйдите из системы. Теперь вернитесь к первому окну и попробуйте отправить форму, используя любую кнопку. Для меня я получил ошибку, поэтому это изменение решает эту проблему для меня. Я опускаю кучу вещей для краткости, но вы должны понять. Ключевыми частями являются включение ActionName на атрибуте и убедитесь, что переданное имя является именем представления, которое показывает форму

Атрибут Класс

[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" />
}

Исходя из ответа 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.

Мой подход 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')" >

Для каждой кнопки отправки просто добавьте:

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

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

});

Я создал метод ActionButton для HtmlHelper. Он сгенерирует обычную кнопку ввода с небольшим количеством javascript в событии OnClick, которое отправит форму указанному контроллеру / действию.

Вы используете помощник, как это

@Html.ActionButton("MyControllerName", "MyActionName", "button text")

это сгенерирует следующий HTML

<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">

Вот код метода расширения:

VB.Net

<System.Runtime.CompilerServices.Extension()>
Function ActionButton(pHtml As HtmlHelper, pAction As String, pController As String, pRouteValues As Object, pBtnValue As String, pBtnName As String, pBtnID As String) As MvcHtmlString
    Dim urlHelperForActionLink As UrlHelper
    Dim btnTagBuilder As TagBuilder

    Dim actionLink As String
    Dim onClickEventJavascript As String

    urlHelperForActionLink = New UrlHelper(pHtml.ViewContext.RequestContext)
    If pController <> "" Then
        actionLink = urlHelperForActionLink.Action(pAction, pController, pRouteValues)
    Else
        actionLink = urlHelperForActionLink.Action(pAction, pRouteValues)
    End If
    onClickEventJavascript = "this.form.action = '" & actionLink & "'; this.form.submit();"

    btnTagBuilder = New TagBuilder("input")
    btnTagBuilder.MergeAttribute("type", "button")

    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript)

    If pBtnValue <> "" Then btnTagBuilder.MergeAttribute("value", pBtnValue)
    If pBtnName <> "" Then btnTagBuilder.MergeAttribute("name", pBtnName)
    If pBtnID <> "" Then btnTagBuilder.MergeAttribute("id", pBtnID)

    Return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal))
End Function

C# (код C# просто декомпилируется из VB DLL, так что он может получить некоторое украшение... но время так мало:-))

public static MvcHtmlString ActionButton(this HtmlHelper pHtml, string pAction, string pController, object pRouteValues, string pBtnValue, string pBtnName, string pBtnID)
{
    UrlHelper urlHelperForActionLink = new UrlHelper(pHtml.ViewContext.RequestContext);
    bool flag = Operators.CompareString(pController, "", true) != 0;
    string actionLink;
    if (flag)
    {
        actionLink = urlHelperForActionLink.Action(pAction, pController, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    else
    {
        actionLink = urlHelperForActionLink.Action(pAction, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    string onClickEventJavascript = "this.form.action = '" + actionLink + "'; this.form.submit();";
    TagBuilder btnTagBuilder = new TagBuilder("input");
    btnTagBuilder.MergeAttribute("type", "button");
    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript);
    flag = (Operators.CompareString(pBtnValue, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("value", pBtnValue);
    }
    flag = (Operators.CompareString(pBtnName, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("name", pBtnName);
    }
    flag = (Operators.CompareString(pBtnID, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("id", pBtnID);
    }
    return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal));
}

Эти методы имеют различные параметры, но для простоты использования вы можете создать некоторую перегрузку, которая принимает только те параметры, которые вам нужны.

Другие вопросы по тегам