Как вы обрабатываете несколько кнопок отправки в 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? Все примеры, которые я искал в Google, имеют в них одиночные кнопки.

Ответ 1

Вот, в основном, чистое решение на основе атрибутов для проблемы с несколькими кнопками отправки, основанной в основном на сообщении и комментариях от 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) { ... }

Обновление: Страницы бритвы, похоже, предоставят ту же функциональность из коробки. Для новой разработки это может быть предпочтительнее.

Ответ 2

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

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

}

EDIT:

Чтобы расширить этот подход для работы с локализованными сайтами, изолируйте свои сообщения где-нибудь в другом месте (например, скомпилируйте файл ресурсов в строго типизированный класс ресурсов)

Затем измените код, чтобы он работал как:

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

Ответ 3

Вы можете проверить имя в действии, как было упомянуто, но вы можете подумать, хорош ли этот дизайн. Это хорошая идея, чтобы рассмотреть ответственность за действие, а не придавать этому дизайну слишком много аспектов, таких как имена кнопок. Поэтому рассмотрим возможность использования двух форм и двух действий:

<% 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") %>

Ответ 4

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

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

<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#2865166

Ответ 5

Только что написал сообщение об этом: Несколько кнопок отправки с 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(…) {
        //…
    } 
}

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

Ответ 6

Я предлагаю заинтересованным сторонам взглянуть на решение Maarten Balliauw. Я думаю, что это очень элегантно.

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

Ответ 7

он короткий и люкс:

На него ответил 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
}

Удачи.

Ответ 8

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

Ответ 9

Вы можете написать:

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

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

Ответ 10

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

Ответ 11

Вот что мне лучше всего подходит:

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

Ответ 12

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

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

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

Значение следующего кода 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

    }

}

Ответ 13

Дэвид Финдли пишет о трех разных вариантах, которые вы делаете для этого, в своем блоге ASP.Net.

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

Ответ 14

Это техника, которую я бы использовал, и я ее еще не вижу. Ссылка (размещена 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;
         }
    }
}

Ответ 15

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

Ответ 16

Этот script позволяет указать атрибут формы данных, который будет работать как атрибут форматирования 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().

Ответ 17

Существует три способа решения указанной проблемы:

  • HTML-способ
  • Способ JQuery
  • "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", а затем в зависимости от того, какой из них отправлен, он вызывает соответствующее действие.

enter image description here

Итак, первым шагом является создание класса, который наследуется от класса 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-запросе, он возвращает 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.

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

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

Ответ 20

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

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

Наслаждайтесь будущей передачей без кода.

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

Ответ 21

Я попытался синтезировать все решения и создал атрибут [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);
}

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

Ответ 22

Модифицированная версия метода 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" />
}

Ответ 23

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

}

Ответ 24

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

Разметка. Имя кнопки должно вводиться с помощью "action:[controllerMethodName]"

(обратите внимание на использование С# 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;
    }
}

Ответ 25

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

Ответ 26

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

Ответ 27

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

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

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

});

Ответ 28

Основываясь на ответе 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.

Ответ 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();
}

Ответ 30

Я создал метод 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

С# (код С# просто декомпилируется из 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));
}

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