Запретить повторную отправку формы php

Как правильно предотвратить повторную отправку формы?

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

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

— Если после добавления сообщения в базу делать перенаправление на страницу вида index.php?message=Сообщение%20успешно%20добавлено! , то это сообщение после обновления страницы так и будет появляться.

+ Поэтому я хочу сделать так: на сайте используются свои сессии в базе MySQL, но чтобы не напрягать базу, я хочу использовать встроенные в php сессии как раз для этого дела, а именно: в месте вывода сообщения об успешной отправке, то есть после физического добавления информации в базу, стартовать сессию, в нее добавить переменную с сообщением «Сообщение успешно добавлено!», затем сделать перенаправление на эту же страницу, где выводятся все сообщения. А в коде самой страницы, стартовать сессию, и если есть в ней переменная с сообщением, то вывести его и удалить переменную.

+ А еще лучше, сделать массив сообщений и выводить их все, но если вдруг открыто несколько вкладок, то есть очень малая вероятность, что будет что-то одновременно и отобразится сообщение не в той вкладке, поэтому наверно стоит сделать какое-то случайное имя переменной, и передавать это имя GET параметром в url перенаправления, и уже по нему выводить и удалять переменную. Тогда вроде всё нормально.

Читайте также:  Python list sorted lambda

Как Вам такой вариант, и как вообще это правильно делать, может, всё-таки отдельную страницу, как на многих форумах «Сообщение добавлено, сейчас Вы будете перенаправлены»?

Заранее всем спасибо за ответы!

akubintsev

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

Рассмотрим теперь сценарии при попытке отправки с клиента сообщения:
— идентификатор сообщения задан:
— на сервере уже есть сообщение с таким отказ
— на сервере нет такого отказ (возможно попытка поиска уязвимости)
— идентификатор не задан => отказ

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

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

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

akubintsev

@Sekira а как вы будете решать вопрос с горизонтальным масштабированием, когда придется использовать другой обработчик сессии, например на memcached и локами на запись?

memcached особо в этом случае не подходит, а в Вашем варианте БД смысл тот же, ускорить если только использовать таблицы в RAM.

akubintsev

@Sekira по идее может помочь Redis, но все же я веду к тому, что лучше стремиться хранить в сессии только редко изменяющиеся данные

Сравнил сейчас скорости, применительно для своего скрипта, примерно среднее время за ~30 попыток:
Сессии: создание сессии и добавление сообщения 0,0002 + создание сессии и вывод сообщения 0,0002
MySQL: редактирование поля в сессии пользователя 0,0004 + вывод сообщения (строка из таблицы итак считывается и не учитывается) 0,000018

В общем получилось одинаково приблизительно.

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

А забыл после вывода сообщения очистить поле/параметр, тогда к сессиям еще +0,0002, а к БД +0,0004, тогда сессии лучше, только с параметром, чтобы не каждый раз проверять есть ли сообщения, а только когда только что добавили и кто-нибудь обновил страницу эту с параметром.

Тогда может вообще cookie использовать =) Добавлять после добавления сообщения, выводить и удалять после редиректа.

Проверил cookie, самый быстрый способ =) 0.00004+0.00002+0.00004 Главное использовать htmlspecialchars для вывода, если вдруг, кто-то подменит cookie

akubintsev

@Sekira да, как вариант сгодится. А так вообще описанный мной способ применяется более широкого спектра задач, например для работы с платежами по API и таким образом можно избежать задвоения транзакций.

@Sekira, куки не помогут защититься от повторной отправки формы при дабл-клике на кнопке «отправить». @akubintsev дело предложил, но вы о разном говорите. Я вас помирю:
— хранить в сессии нужно «идентификатор нового сообщения» (сессия для этого годится, не вижу проблемы при горизонтальном масштабировании и мемкэше). Тогда чей первый запрос придет, тот и удалит это значение из сессии, таким образом второй запрос не пройдет сверку.
— если же этот идентификатор хранить в куке, то оба запроса пришлют его на сервер вместе с формой (при дабл клике) и я не знаю как без еще одного места (бд или сессии) проверить кто из них дубликат.
— само сообщение об успешном добавлении тоже можно пихнуть в сессию, в фреймворках такое используется (обычно известно как flash message). Но можно и в куку его писать (если выводить с htmlspecialchars, правильно мыслишь 🙂

Если делать следующим способом, то куки очень даже подойдут:
1) Форма без всяких случайных значений и т.п., обычная форма, пользователь заполняет и нажимает кнопку;
2) Всё проверяется, если ошибки, то выводятся и форма остается, если ошибок нет, запись добавляется в базу, ставится кука с сообщением, редирект на страницу со списком сообщений, exit;
3) Список сообщений, если есть переменная в куках с сообщением, то выводим и удаляем, если нет, то ничего не делаем.

Теперь если обновлять страницу то ничего еще раз не добавится, если вернуться назад и еще раз нажать кнопку, то конечно добавится, но от этого я и не хочу защититься.

А если добавить еще один сервер-обработчик (читай, вэб-сервер)? Сессии отпадают. Хранить в куках — не забывайте про то, что они отправляются при каждом запросе к серверу. По моему, вариант с БД предпочтительнее.

Куки создаются и тут же после редиректа и вывода сообщения, удаляются, поэтому всё нормально. Может не очень красиво, зато быстро работает и url чистое.

Источник

Как отправить форму в php, чтобы она не отправлялась повторно, после обновления страницы

Сегодня тема пойдет об отправке формы на html-страничке, чтобы, при повторном обновлении, она не отправлялась заново в БД.

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

Во первых, почти все источники предлагают делать это таким образом:

Но, проблема в нем следующая, если вы, уже где то в начале страницы отправили заголовки, то на вас будут ругаться, мол, «заголовки отправлены, зачем ты хочешь, чтобы я это сделал еще один раз, угомонись!»

1. обнулить форму
2. засунуть все переменные формы в переменную $_SESSION(нам нужно больше переменных сессии!)

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

Меня же, все способы, встретившиеся на просторах интернета, не устроили. И я решила сделать то, что будет удобно мне и не будет занимать большое количество переменных/времени/ресурсов.
Приступим.

Для начала, создадим простенькую форму.

Затем добавим в форму метод отправки, действие и имя:
Действие будет происходить на другой странице, для удобства и дальнейшей функциональности.

Затем, мы создаем файлик redir.php и работаем над ним:

 /*redir.php*/ alert("Заполните все поля формы");'; > else < $name = $_POST["name"]; $phone = $_POST["phone"]; $datetime= date("l dS of F Y h:i:s A"); $mysqly=mysqli_query($connect,"INSERT INTO contacts VALUES ('','$name','$phone','$datetime','')") OR DIE (MYSQLI_ERROR());>> ?>  

Все. Как видите это просто, быстро и удобно!

Надеюсь, вам пригодится данная статья.

Буду рада критике или вашим предложениям.

P.S. Да, можно сделать отправку заголовка на этой, дополнительной, странице, но, почему-то с использованием кода JS мне нравится больше.

P.P.S. Спасибо за ваше внимание.

Источник

Как предотвратить повторную отправку сообщения из формы?

Как предотвратить повторную отправку сообщения из формы?

Многие начинающие веб-мастера задаются вопросом: «Как предотвратить повторную отправку данных из формы?». Очень часто вижу такие вопросы на форумах. В этой статье расскажу об легком и правильном способе решения этой проблемы.

Отправить заново

Допустим, мы делаем скрипт, который принимает данные из формы, переданные методом POST. Скрипт данные принял, обработал и выдал страницу с результатом. Но если пользователь вздумает обновить страницу в этот момент — он увидит сообщение такого плана:

Окно запроса повторной отправки данных отправить заново

Чтобы отобразить эту страницу, Firefox должен отправить информацию, которая повторит любое ранее произведённое действие (например, запрос на поиск или онлайн-покупка).

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

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

Можете отправить форму один раз, а потом нажать Ctrl+R и увидеть злополучное окно. Давайте от него избавляться.

Но сперва слово спонсору поста — сайту с полезным контентом для телефонов Samsung, который предлагает темы для samsung gt s5230, обои и прочий стафф.

Предотвращение повторной отправки формы с помощью серверного редиректа

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

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

Недостаток этого метода состоит в том, что пользователь может нажать кнопку «Назад» и вернуться на страницу с редиректом. Она снова швырнет его вперед и так пользователь с трудом сможет вернуться на две страницы назад, к форме, которую изначально заоплнял.

Предотвращение повторной отправки формы с помощью клиентского редиректа

Клиентский редирект называется клиентским потому что он происходит на стороне клиента. То есть в браузере. Клиентский редирект может происходить при помощи JavaScript и meta-тегов.

У JavaScript есть преимущество — он перезаписывает History браузера так, что даже если пользователь нажмет кнопку «Назад» браузера, он не вернется на страницу, которую отдал обработчик формы. То есть, окошко исчезнет капитально. Но JS у некоторых отключен.

У META-тегов, с другой стороны, есть преимущество в плане универсальности. Они редиректят всех и всегда.

Оптимально будет сочетать эти два способа. Как — описал Александр Шуркаев в заметке оптимальный редирект.

Используем его метод следующим образом.

Пробуем! Теперь, как видно, никакого окна не появляется. Что мы сделали? Мы проверили. Если данные пришли — мы выводим все необходимое для редиректа. В принципе, после этого уже можно даже делать exit, чтобы не грузить браузер лишними данными, которые все равно никто не увидит.

Источник

Оцените статью