Остановить ввод данных в базу данных дважды

Я совершенно новый для PHP, мне было интересно, какие методы / предупреждения используют другие программисты, чтобы остановить ввод данных дважды в базу данных MySQL, когда пользователь обновляется на той же странице, что и форма? Obviouly это случается, и мне нужен хороший способ остановить это.

Спасибо, Бен

    Я называю это золотым правилом веб-программирования:

    Никогда не отвечайте с телом на POST-запрос. Всегда выполняйте работу, а затем отвечайте заголовком «Location:», чтобы перенаправить на обновленную страницу, чтобы браузер запрашивал ее с помощью GET.

    Таким образом, освежение не принесет вам никакого вреда.

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

    Обработать форму, а затем перенаправить на страницу результатов. Перезагрузка затем только повторно отображает страницу результатов.

    Ответ Илья правильный, я просто хотел добавить немного больше, чем вписывался в комментарий:

    Если повторная отправка опасна (возвращаясь и отправляя снова, перезагружая страницу результатов [если вы не приняли совет Илья] и т. Д.), Я использую «nonce», чтобы убедиться, что форма может пройти только один раз.

    На странице формы:

    <?php @session_start(); // make sure there is a session // store some random string/number $_SESSION['nonce'] = $nonce = md5('salt'.microtime()); ?> // ... snip ... <form ... > <input type="hidden" name="nonce" value="<?php echo $nonce; ?>" /> </form> 

    На странице обработки:

     <?php if (!empty($_POST)) { @session_start(); // check the nonce if ($_SESSION['nonce'] != $_POST['nonce']) { // some error condition } else { // clear the session nonce $_SESSION['nonce'] = null; } // continue processing 

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

    Чтобы заявить очевидное (я еще не видел его здесь …): никогда не используйте GET для публикации данных, всегда используйте POST, таким образом пользователь, по крайней мере, получает предупреждение, если он или она пытается обновить / повторно опубликовать страницу (по крайней мере, в Firefox, но я полагаю и в других браузерах).

    Кстати, если вы не можете позволить себе иметь одинаковые данные дважды, вы также должны рассмотреть решение MySQL с уникальным ключом (может быть комбинацией полей) и:

      INSERT INTO ... ON DUPLICATE KEY UPDATE ... 

    Я согласен с Илья и добавлю, что вы должны использовать какой-либо клиентский javascript, чтобы отключить кнопку «отправить», после того как она была нажата или присутствует модальный диалог (css может помочь вам здесь), чтобы избежать нескольких нажатий кнопки отправки.

    Наконец, если вы не хотите, чтобы данные в вашей базе данных дважды, а затем проверьте свою базу данных на данные, прежде чем пытаться ее вставить. Если вы разрешаете дублировать записи, но не хотите, чтобы быстрые повторные вставки из одного источника, я бы использовал поля времени / даты и поля IP-адреса, чтобы разрешить «блокировку» по времени в моем коде отправки, т. Е. Если IP – это то же самое, и последнее время отправки было менее 5 минут назад, после чего не вставляйте новую запись.

    Надеюсь, это даст вам некоторые идеи.

    Возможно, вы захотите проверить шаблон POST / Redirect / GET, который реализует большинство современных веб-приложений, см. http://en.wikipedia.org/wiki/Post/Redirect/Get

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

    Они также известны как GUID в мире Microsoft, а в PHP вы можете генерировать один через uniqid () в PHP. Это шестнадцатеричное значение в 32 символа, которое вы должны хранить в шестнадцатеричном / двоичном формате столбца, но если таблица не будет использоваться, а CHAR (32) будет работать.

    Создайте этот идентификатор, когда вы показываете свою форму как скрытый ввод, и обязательно отметьте столбец базы данных, помеченный как первичный ключ. Теперь, если пользователю удастся полностью вернуться на страницу публикации, INSERT завершится неудачно, потому что у вас не может быть дубликатов ключей.

    Дополнительный бонус к этому заключается в том, что если вы генерируете UUID в коде, то после выполнения вставки вам никогда не понадобится использовать расточительные запросы, извлекающие ключ, который был сгенерирован, потому что вы уже это знаете. Это приятное преимущество, если вам нужно вставить дочерние элементы INSERT в другие таблицы.

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

    Обычно я полагаюсь на индекс UNIQUE индекса sql. http://dev.mysql.com/doc/refman/5.0/en/constraint-primary-key.html

    Вы должны передать переменную uniqid в свой html внутри метода showAddProductForm () и тот же uniqid в ваш $ _SESSION forexample:

     public function showAddProductForm() { $uniId = uniqid(); $_SESSION['token'][$uniId] = '1'; $fields['token'] = 'token['.$uniId.']'; $this->fileName = 'product.add.form.php'; $this->template($fields); die(); } 

    Затем вы должны поместить скрытый ввод в код HTML внутри вашей формы со значением uniqid, который был передан в HTML из метода showAddProductForm.

     <input type="hidden" name="<?=$list['token']?>" value="1"> 

    сразу после события отправки вы проанализируете его в начале метода addProduct (). Если токен существует в $ _SESSION и он имеет равное значение внутри этого массива, то это новые требования. Перенесите его на нужную страницу и отмените токен и продолжите вставку. Кроме того, это от страницы перезагрузки или повторяющегося запроса, перенаправить его на страницу addProdeuct

     public function addProducts($fields) { $token_list = array_keys($fields['token']); $token = $token_list['0']; if (isset($_SESSION['token'][$token]) and $_SESSION['token'][$token] == '1') { unset($_SESSION['token'][$token]); } else { $this->addAnnounceForm($fields, ''); } } с public function addProducts($fields) { $token_list = array_keys($fields['token']); $token = $token_list['0']; if (isset($_SESSION['token'][$token]) and $_SESSION['token'][$token] == '1') { unset($_SESSION['token'][$token]); } else { $this->addAnnounceForm($fields, ''); } } 

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

    Особая благодарность тому, кто выясняет этот метод malekloo

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

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

    Добавьте скрытое поле со случайной строкой (например, созданное md5(uniqid()) ), сделайте поле в базе данных для этой строки и сделайте UNIQUE.

    Вы можете использовать токен, чтобы предотвратить повторную обработку страницы! Такая процедура используется во многих веб-фреймворках!

    Шаблон, который вы должны использовать, это «Шаблон маркера синхронизатора»! Если у вас есть приложение, ориентированное на обслуживание, вы можете сохранить свой статус в базе данных.

    Данные могут быть отправлены через JavaScript или в поле скрытой формы.

    Вы также должны взглянуть на библиотеки с помощью бесплатной поддержки таких вещей! Grails – такой!

    См .: http://www.grails.org/1.1-Beta3+Release+Notes

     <g:form useToken="true"> 

     withForm { // good request }.invalidToken { // bad request } 

    ..

    Мои два цента:

    • Перенаправить пользователя на ту же страницу после отправки данных и проверить, if(isset($_POST['submit'])

    Другая полезная информация для подобных случаев:

    • Посмотрите на LOCK TABLES в MySQL

    • Отключить кнопки через JavaScript после первого щелчка

    POE (Post Once Exactly) – это шаблон HTTP, предназначенный для предупреждения клиенту блокировать двойные отправки с использованием запатентованного заголовка …

     GET /posts/new HTTP/1.1 POE: 1 ... 

    … но все еще в спецификации.

    http://www.mnot.net/drafts/draft-nottingham-http-poe-00.txt

    Я думаю, что вышеупомянутое nonce – хорошее решение. Хотя сохранение nonce как дискретной переменной сеанса приведет к некоторым ошибкам, если клиент пытается выполнять одновременные сообщения из нескольких вкладок. Может быть, лучше …

     $_SESSION['nonces'][] = $nonce; 

    … а также …

     if (in_array($_POST['nonce'], $_SESSION['nonces'])) { 

    … разрешить множественные числа (nonci? noncei?).

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

    Лучший способ избежать повторной записи в обновление страницы – после добавления записей в базу данных при нажатии кнопки просто добавьте эту строку:

     Response.Write("<script>location.href='yourpage.aspx'</script>");