Предотвратить отправку двойных форм

У меня есть приложение ASP.NET MVC 3, в котором есть действие публикации под названием Create:

[HttpPost]
public virtual ActionResult Create(Issues issues)
{
    if (ModelState.IsValid)
    {
        context.Issues.Add(issues);
        context.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(issues);
}

Если я дважды нажму на кнопку отправки по ошибке, это создаст 2x проблемы. Есть ли способ предотвратить это без использования javascript и отключения кнопки, я думал, что все использование RedirectToAction было для предотвращения этого с помощью шаблона проектирования Post/Redirect/Get?


person CallumVass    schedule 21.03.2012    source источник
comment
вы можете просто добавить что-то вроде if(!context.Issues.Exists(logic))?   -  person Chris Gessler    schedule 21.03.2012
comment
Просто отключите кнопку отправки при нажатии!   -  person gdoron is supporting Monica    schedule 21.03.2012
comment
Нет такого метода под названием Exists?   -  person CallumVass    schedule 21.03.2012


Ответы (5)


Упомянутый вами шаблон (шаблон проектирования Post/Redirect/Get) предотвращает двойную публикацию при обновлении страницы, поскольку последнее действие — это GET, а не POST.

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

1. Disable the button after click
2. Maintain a unique index on 'Issues' and look for duplicates
3. Maintain something in session that gives you an indication of a double post

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

person Chris Gessler    schedule 21.03.2012
comment
Это нормально для приведенного выше примера, но я имел в виду более сложные примеры, например Ajax.BeginForm, который будет обновлять только часть страницы. - person CallumVass; 21.03.2012
comment
@BiffBaffBoff. Чтобы предотвратить двойной щелчок на стороне клиента (AJAX POST), вы можете просто заблокировать пользовательский интерфейс до тех пор, пока не вернется ответ (синхронно), или отключить кнопку (асинхронно), или реализовать что-то на стороне сервера, чтобы обнаружить это. - person Chris Gessler; 21.03.2012
comment
Привет Крис, это именно то, что я сделал сейчас. Я отключил кнопку, пока не пришел ответ. Спасибо еще раз - person CallumVass; 04.04.2012

Простое решение!

Отключите кнопку отправки при нажатии. Конец.

$('submitButtonId').click(function(){
    $(this).prop('disabled', true);
    $(this).attr('disabled', 'disabled'); // for jQuery versions < 1.6.
});
person gdoron is supporting Monica    schedule 21.03.2012
comment
С помощью этого решения, если у пользователя возникла проблема с сетью в промежутке между щелчком мыши и загрузкой информации на сервер, то нет возможности сохранить информацию в форме. Если пользователь потратил всего 5 минут на заполнение формы, он будет расстроен. Лучший подход, вероятно, состоит в том, чтобы отключить его только до тех пор, пока сеть не преуспеет или не выйдет из строя. - person Justin Grant; 01.05.2014
comment
@AlexAngas, ничего не будет работать, если javascript отключен. Я не думаю, что есть хороший способ сделать это без js. (Ответ Shadow Wizard ниже — это жесткое кодирование 1000 мс, что далеко от идеала, что, если у пользователя медленное сетевое соединение?) - person gdoron is supporting Monica; 29.02.2016

Самый простой способ, который я могу придумать, - это использовать сеанс:

if (ModelState.IsValid)
{
    DateTime now = DateTime.Now;
    if (Session["LastAdd"] == null || (now - (DateTime)Session["LastAdd"]).TotalMilliseconds > 1000)
    {
        //first time, or more than a second passed since last addition
        context.Issues.Add(issues);
        context.SaveChanges();
        Session["LastAdd"] = now;
    }
    return RedirectToAction("Index");
}
person Shadow Wizard Wearing Mask V2    schedule 21.03.2012
comment
Извините, я не использую сеансы, поскольку это приложение для проверки подлинности Windows. - person CallumVass; 21.03.2012
comment
@BiffBaffBoff — Начните использовать Session переменных. - person Security Hound; 21.03.2012
comment
Переменные сеанса @Biff не имеют ничего общего с аутентификацией. что заставляет вас так думать? - person Shadow Wizard Wearing Mask V2; 21.03.2012
comment
Я просто подумал, что сеансы использовались с проверкой подлинности с помощью форм. - person CallumVass; 21.03.2012
comment
@Бифф, нет. Это неотъемлемая часть любого веб-приложения, начиная с классического ASP. Аутентификация с помощью форм может использовать сеанс для отслеживания пользователя и удержания его в системе, но это все равно, что вы скажете, что string существует только в приложениях Windows Forms. - person Shadow Wizard Wearing Mask V2; 21.03.2012

Вы можете сгенерировать IssueId, когда показываете форму пользователю, а затем проверить, что у вас еще нет задачи с таким идентификатором в вашем методе Create, и, например, пропустить такие запросы.

person Vladimir Perevalov    schedule 21.03.2012
comment
Что ж, он просто создаст добавление идентификатора, поскольку IssueId — это мое поле PK, поэтому для каждой новой созданной задачи у нее будет новый идентификатор (2, 3, 4, 5 и т. д.). - person CallumVass; 21.03.2012

Шаблон PRG не предотвратит этого, так как действие P в нем требует времени (что обычно и бывает), и пользователь может снова отправить форму (через щелчок или обновление браузера), что приведет к «сбою» шаблона PRG.

Обратите внимание, что злоумышленники также могут обойти все ваши меры на стороне клиента, запустив несколько сообщений HTTP в быстрой последовательности.

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

Для вашего удобства цитирую:

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

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

person CShark    schedule 26.04.2016