フォームの重複送信を防ぐための js や jquery プログラムを数多く見てきましたが、これは単なる方法であり、このページからフォームを送信し、データを受け入れるページを直接見つけないと、この js 処理方法は無効になります。以下では、php を使用してそれを解決します。 繰り返し送信を防ぐために以前に使用されていたjsフォームメソッド コードは次のとおりです コードをコピー <br> var checkSubmitFlg = false;<br> 関数 checkSubmit() {<br> if (!checkSubmitFlg) {</p> <p>// 初投稿<br> checkSubmitFlg = true;<br> true を返します;<br> } その他 {</p> <p>//重複送信<br> alert("再送信!");<br> false を返します;<br> }<br> }<br> </スクリプト></p> <p>//以下の3つのメソッドがそれぞれ呼び出されます</p> <p><フォーム onsubmit="return checkSubmit();"></p> <p><input type="submit" onclick="return checkSubmit(); /></p> <p><input type="button" onclick="document.forms[0].action='./test';document.forms[0].submit();return checkSubmit(); /></p> </td> </tr> </table> <p><font color="#ff0000">直接フォームを作成して /test に送信すると、</font>上記のエージェントは単なる飾りなので、この問題をどう解決すればよいでしょうか</p> <p>すでに解決方法を知っている場合は、この記事はあなたの好みに合わないかもしれません。Paperen は基礎から説明するつもりなので、解決策を一気に見たい場合には適さないかもしれません。注目してください。それでは~始めましょう~</p> <p>フォームとは何かを知っておく必要があると思います。フォーム要素は、Web ページで入力が必要な場所で使用する必要があります。一般的なコードは次のとおりです。 </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr>コードは次のとおりです<td width="464" height="27" bgcolor="#FFE7CE"> </td>コードをコピー<td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy9988')"> </td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy9988"> <br> メソッド="投稿"><br> <p><br> <label for="test">好きなものを入力してください www.bKjia.c0m</label><br> <input type="text" name="data" id="test" /><br> </p><br> <p><br> <input type="submit" value="submit" name="submit" /><br> </p><br> </ul><br> </フォーム><br> </td> </tr> </table> 重要な点は、実際には、paperen によって個人的に追加されただけであり、いわゆる input が入力を意味することは完全に理解できます。ユーザー入力に使用される要素は、一部の属性の (タイプ) が入力として使用できないだけであり (ここでは submit)、すべてのユーザー入力データはバッグとして理解できます。処理のためにサーバーに送信しますが、form 要素について注目すべき点は、get と post の 2 つのメソッドです。これについて深く理解する必要はありません。また、その後の内容とはあまり関係がありません。興味がある場合は、ブラウザのデバッグ ツールを使用して、リクエスト ヘッダー情報を表示し、firebug などの情報を送信することもできます。パフォーマンスとしては、get を使用してフォームを送信すると、すべての入力要素の値がアドレス バーに表示されますが、たとえば、get <p> を使用してこのフォームを送信した後のブラウザのアドレス バーには表示されません。 </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr>コードは次のとおりです<td width="464" height="27" bgcolor="#FFE7CE"> </td>コードをコピー<td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy2294')"> </td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy2294">http://localhost/mytest/token/form.php?data=test&submit=%E6%8F%90%E4%BA%A4<p> </p> </td> </tr> <p>投稿はアドレスバーに表示されなくなりました。fiebug を使用して次の情報を確認してください</p> <p><img class="www.bKjia.c0m" style="max-width:90%" style="max-width:90%" type="image" alt="" src="http://www.bKjia.c0m/blog/upload/image/20131130101410.jpeg"></p> <p> get は明示的にデータを送信し、post は暗黙的にデータを送信すると単純に考えることができますが、もう 1 つの大きな違いは、post がより多くの大規模なデータ送信をサポートしていることです。 </p> <p>次に、フォームのコードが記述できたら、サーバースクリプト(ここではPHP)を記述していきます。とってもシンプル〜</p> <p> </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr> <td width="464" height="27" bgcolor="#FFE7CE">コードは次のとおりです</td> <td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy1940')">コードをコピー</td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy1940"><?php<br /> If ( isset( $_POST['submit'] ) ) {<br /> //フォーム送信処理<br /> $data = isset( $_POST['data'] ) htmlspecialchars( $_POST['data'] ) :<br /> <br /> '';<br /> //データベースを挿入または更新します<br /> $sql = "テスト (`string`) 値に挿入 ('$data')";<br /> //クエリを実行します<br /> エコー $sql;<br /> }<br /> ?></td> </tr> </table><p>ここではデータはポストによって送信されるため、PHP の $_POST グローバル変数を使用して、フォームによって送信されたデータを取得できます。post メソッドを使用してサーバーに送信されたすべてのフォーム データは、この $_POST グローバル変数に保存されます。 print_r($_POST) 変数を試してみると理解できるでしょう。 </p> <p>まず $_POST 配列に submit があるかどうかを確認します。存在する場合は、asp.net に ispostback と呼ばれるものがあるようですが、それほど厳密ではありません。この問題は後で解決します。 </p> <p>入力ボックスでデータを受け取った後、それは $_POST['data'] です。HTML タグや JavaScript の入力によって引き起こされる問題を防ぐために、これに対して htmlspecialchars を使用して HTML フィルタリングを実行することを忘れないでください。 XSS の脆弱性)。最後に、SQL ステートメントに結合され、実行のためにデータベースに送信されます (mysql_query など、ここでデータベースを詳細に操作するための一部の関数を Paperen が使用していないだけなので、自分で完成させることに興味があります) 。おめでとうございます。これでデータ入力機能は正常に完了しました。ただし、データを挿入した後、オペレーターにプロンプトを表示する必要があるため、改善する必要があります。少なくとも、操作が失敗したか成功したかを確認するようにしてください。したがって、コード全体は次のように記述されます。 </p> <p> </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr> <td width="464" height="27" bgcolor="#FFE7CE">コードは次のとおりです</td> <td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onClick="doCopy('copy8987')">コードをコピー</td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id=copy8987> <p><?php<br /> If ( isset( $_POST['submit'] ) ) {<br /> //フォーム送信処理<br /> $data = isset(<br /> <br /> $_POST['data'] ) htmlspecialchars( $_POST['data'] ) : '';<br /> //接続<br /> Mysql_connect('localhost', 'root', 'root' );<br /> <br /> //データベースを選択<br /> Mysql_select_db( 'テスト' );<br /> //文字化けを防ぐための文字セットを設定します<br /> Mysql_query( '名前を「utf8」に設定' );<br /> //SQL<br /> $sql = "挿入<br /> <br /> `トークン` (文字列) 値に ('$data')";<br /> //クエリ<br /> Mysql_query( $sql );<br /> $insert_id = mysql_insert_id();<br /> もし(<br /> <br /> $insert_id ) {<br /> $state = 1;<br /> } その他 {<br /> $state = 0;<br /> }<br /> }<br /> ?></p> <p> <?php if ( isset( $state )</p> <p> && $state ) { //データは正常に挿入されましたか?> <p> <p>正常に挿入されました <a href="form.php">戻る</a></p></p> <p> <?php } else { //失敗したか、アクションが挿入されませんでした</p> <p> ?></p> <p> <フォームメソッド="post"></p> <p> <p></p> <p> <label for="test">必要なものを入力</label></p> <p> <input type="text"</p> <p> name="データ" id="テスト" /></p> <p> </p></p> <p> <p></p> <p> <input type="submit" value="submit" name="submit" /></p> <p> </p></p> <p> </ul></p> <p> </form></p> <p> <?php } ?></p> </td> </tr> </table> <p>最初のコードと比較すると、html の宣言と head と body が省略されています。実際には、実際のデータベースへの挿入と操作のフィードバック ($state 変数を介して) を実現します。コードを自分でコピーして、試してみてください(もちろんデータベース操作部分のコードは実際の状況に合わせて修正してください)。コードは正常でロジックも問題ありませんが、問題があります。つまり、挿入が成功したことを表示した後にページを更新すると、フォーム処理アクションが再度実行され、データが再度挿入されます。これは重複挿入問題と呼ばれます。ソリューションをリリースする前に、自分で解決する方法を考えることができます。 </p> <p>この問題は、このページでデータを受信して処理結果を表示することが原因だと思いますか?はい、いくつかのデバッグ ツールを使用すると、ブラウザにも投稿データが保持されることがわかり、送信後にフォームを更新すると、投稿データが再送信されます。 </p> <p><input class="" src="/blog/upload/image/20131130101432.jpeg" width="425" height="152" type="image"></p> <p>ブラウザに一時的に保存された投稿データをクリアする方法があれば問題は解決しますが、これはブラウザ独自のものであるため、サーバーはこれを行うことができません。そうしないと、再度更新しても送信されます。データを繰り返し実行します。 </p> <p>ここまでで、繰り返し送信の意味と問題の深刻さを理解できたかもしれません。リダイレクト方法を選択しない場合は、別の方法を考える必要があるため、これがトークンの解決策です。 </p> <p>トークン自体が権限、操作権限、アイデンティティフラグなどを表すのと同じように、クライアントがこのフォームをリクエストするたびに、そのようなアイデンティティフラグをフォームに追加して、同時にトークンフックを生成できますか?正しい場合は、フォームを受信して処理します。実装の本質はこんな感じで、これを具体的な実装に反映させるにはセッションと呼ばれるものを使う必要があります。セッション分析については、wiki を参照してください</p> <p>簡単に理解すると、セッションもトークンの概念なので、「トークンを何に使ったんだ?!」と驚かれるかもしれませんが、私たちが達成したいのはセッションだけではなく、その基盤です。達成するためにいくつかのデータを添付します。必要なフォームトークン。それでは、やってみましょう! </p> <p>セッションは PHP のスーパーグローバル変数 $_SESSION にも保存されます。これを有効にするには session_start() を使用してください。原理は他のサーバーサイド スクリプトでも同じですが、呼び出しメソッド名が一致しない可能性があります。トークンを追加した後のコードは次のとおりです: </p> <p> </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr> <td width="464" height="27" bgcolor="#FFE7CE">コードは次のとおりです</td> <td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy1722')">コードをコピー</td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy1722"> <?php<br /> //オープンセッション<br /> Session_start();<br /> If ( isset( $_POST['submit'] ) && valid_token() ) {<br /> //フォーム<br /> <br /> 処理のために送信します<br /> }<br /> <br /> /**<br /> * トークンを生成します <br /> * @return string MD5 暗号化タイムスタンプ<br /> */<br /> 関数 create_token() {<br /> //現在のタイムスタンプ<br /> <br /> $timestamp = time();<br /> $_SESSION['トークン'] = $タイムスタンプ;<br /> md5( $timestamp );<br />を返します }<br /> /**<br /> * トークンは有効ですか? * @return bool<br /> <br /> <br /> */<br /> 関数 valid_token() {<br /> If ( isset( $_SESSION['token'] ) && isset( $_POST['token'] ) && $_POST['token'] == md5(<br /> <br /> $_SESSION['トークン'] ) )<br /> {<br /> // 正しい場合、このトークンは破棄されます<br /> $_SESSION['トークン'] = '';<br /> true を返します;<br /> }<br /> false を返します;<br /> }<br /> ?><br /> <?php if ( isset( $state ) && $state ) { //データは正常に挿入されました?><br> <p>正常に挿入されました <a href="form.php">戻る<br> <br> </a></p><br> <?php } else { //失敗したか、挿入アクションがありませんか?><br> <フォームメソッド="投稿"><br> <p><br> <label for="test">何でも<br> <br> 何かを入力してください</label><br> <input type="text" name="data" id="test" /><br> </p><br> <p><br> <input type="submit" value="Ti<br /> <br /> " 送信" name="送信" /><br> </p><br> </ul><br> <!-- トークン --><br> <input type="hidden" value="<?php echo create_token(); //生成 <br> <br> トークン ?>" name="トークン" /><br> <!-- トークン --><br> </フォーム><br> <?php } ?><br> </td> </tr> <p>ここではコードの一部は焦点ではないので省略しています。実際には、追加されるのは 3 つだけです。 </p>まず、フォームの末尾の前に入力要素を追加します。タイプが非表示 (非表示フィールド) であることに注意してください <p> </p>2 番目に、create_token と valid_token という 2 つの関数が追加されます。前者はトークンの生成に使用され、後者はトークンの検証に使用されます<p> </p> 3 番目に、if にもう 1 つの条件、valid_token を追加します<p> </p> これで完了です。非常にシンプルで、新しく追加された 2 つの関数にすべてがまとめられています。ここで Paperen が使用するトークンは単なるタイムスタンプです。フォームをリクエストしたときのタイムスタンプは $_SESSION['token'] に保存されます。これにより、クライアントによって送信された $_POST['token] がチェックされます。 . '] は、md5 以降の $_SESSION['token'] と一致します。 もちろん、2 つの変数 $_POST['token'] と $_SESSION['token'] の存在も追加する必要があります。 <p> </p>この単純なトークン パターンをカプセル化して、フォーム送信タイムアウト検証の追加など、機能を拡張することもできます。これも試してみる良い機会です。 <p> </p>最後に、paperen が codeingeter を拡張するために使用した Form_validation クラス ファイルを添付します。主にトークンとフォーム タイムアウトを拡張します。圧縮パッケージ内の Welcome.php はコントローラー ファイルです。applicationcontroller に配置してください (このコントローラーを追加したくない場合は、それを開いてトークン メソッドをコピーし、他の既存のコントローラーに配置してください)。 applicationlibraries の .php 。 <p> </p>codeingeter の Form_validation クラス ファイル コード<p> </p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr>コードは次のとおりです<td width="464" height="27" bgcolor="#FFE7CE"> </td>コードをコピー<td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy6080')"> </td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy6080"><?php if ( !定義('BASEPATH')) exit('直接スクリプトアクセスは許可されません');<p> </p>クラス Welcome extends CI_Controller {<p> </p><p> パブリック関数index()<br /> {<br /> $this->load->view('welcome_message');<br /> }<br /> </p> パブリック関数トークン()<p> {<br /> $this->load->helper( array('form') );<br /> $this->load->library('form_validation');<br /> If ( $this->input->post( 'submit' ) && $this->form_validation->valid_token() )<br> {<br> //何も<br> //valid_token には、トークンのタイムアウトとトークンの正当性の判定が既に含まれています。トークンのタイムアウトを有効にするには、token_validtime を 0 以外に設定してください<br>。 「ok」をエコーします;<br> }<br> // フォームトークンを生成する <p> $token = $this->form_validation->create_token();<br> </p> //フォーム例<p> echo form_open();<br> echo form_input('token', '');<br> echo $token;<br> echo form_submit('submit', 'submit');<br> echo form_close();<br> }<br> }<br> </p> </td> </tr> <p>form_validation_token</p> <table width="620" align="center" border="0" cellpadding="1" cellspacing="1" style="background:#FB7"> <tr> <td width="464" height="27" bgcolor="#FFE7CE">コードは次のとおりです</td> <td width="109" align="center" bgcolor="#FFE7CE" style="cursor:pointer;" onclick="doCopy('copy1009')">コードをコピー</td> </tr> <tr> <td height="auto" colspan="2" valign="top" bgcolor="#FFFFFF" style="padding:10px;" class="copyclass" id="copy1009"> <p><?php if ( !定義('BASEPATH')) exit('直接スクリプトアクセスは許可されません');<br /> /**<br /> * @abstract CI の Form_validation クラスを継承し、それに基づいてトークンを追加します <br /> */<br /> クラス MY_Form_validation は CI_Form_validation を拡張します {</p> <p> /**<br /> * トークンキー<br /> * @var 文字列<br /> */<br /> var $key = 'トークン';<br /> <br /> /**<br /> *フォームトークンの有効時間(秒)<br /> * @abstract 一部のフォームで入力時間を制限する必要がある場合は、この値を設定します。0 の場合、制限はありません<br /> * @var int 秒<br /> */<br /> var $token_validtime = 5;</p> <p> /**<br /> * デバッグモード <br /> * @var ブール<br /> */<br /> var $debug = false;</p> <p> /**<br /> * CI オブジェクト <br /> * @var <type><br /> */<br /> プライベート $_CI;</p> <p>パブリック関数__construct()<br /> {<br /> 親::__construct();<br /> $this->_CI =& get_instance();<br /> //設定にencryption_keyが入力されていない場合<br /> $encryption_key = config_item('encryption_key');<br /> If ( empty( $encryption_key ) ) $this->_CI->config->set_item( 'encryption_key', $this->key );<br /> //セッションがロードされていない場合<br /> If ( !isset( $this->_CI->session ) ) $this->_CI->load->library('session');<br /> }</p> <p> /**<br /> *トークンの有効期限を設定します<br /> * @param int $sec 秒数<br /> */<br /> パブリック関数 set_token_validtime( $sec )<br /> {<br /> $this->token_validtime = intval( $second );<br /> }</p> <p> /**<br /> * フォームトークンの有効時間を取得します<br /> * @return int 秒<br /> */<br /> パブリック関数 get_token_validtime()<br /> {<br /> return $this->token_validtime;<br> }</p> <p> /**<br> * フォームトークンが合法かどうかを確認してください<br> * @return bool<br> */<br> パブリック関数 valid_token()<br> {<br> if ( $this->debug ) return true;<br> //セッションでハッシュを取得します<br> $source_hash = $this->_CI->session->userdata( $this->key );<br> if ( empty( $source_hash ) ) return false;<br> <br> //タイムアウトしたかどうかを判断します<br> ( $this->is_token_exprie() ) が false を返す場合;</p> <p> // 送信されたハッシュ<br> $post_formhash = $this->_CI->input->post( $this->key );<br> if ( empty( $post_formhash ) ) return false;</p> <p> if ( md5( $source_hash ) == $post_formhash )<br> {<br> $this->_CI->session->unset_userdata( $this->key );<br> true を返します;<br> }<br> false を返します;<br> }</p> <p> /**<br> * フォームトークンを生成します (input 要素とともに) <br> * @戻り文字列<br> */<br> パブリック関数 create_token( $output = false )<br> {<br> $code = time() 。 '|' 。 $this->get_random( 5 );<br> $this->_CI->session->set_userdata( $this->key , $code);<br> $result = function_exists('form_hidden') ? form_hidden( $this->key, md5( $code ) ) : '<input type="hidden" name="token" value="'. md5( $code ) .'" />';<br> if ( $output )<br> {<br> エコー $result;<br> }<br> それ以外<br> {<br> $result を返します;<br> }<br> }<br> <br> /**<br> * 乱数を取得(自分で拡張可能)<br> * @param int $number 上限<br> * @戻り文字列<br> */<br> パブリック関数 get_random( $number )<br> {<br> return rand( 0, $number );<br> }<br> <br> /**<br> * フォームトークンの有効期限が切れているかどうかを確認します<br> * @return bool<br> */<br> パブリック関数 is_token_exprie()<br> {<br> if ( empty( $this->token_validtime ) ) return false;<br> $token = $this->_CI->session->userdata( $this->key );<br> if ( empty( $token ) ) return false;<br> $create_time = array_shift(explode('|', $token) );<br> return ( time() - $create_time > $this->token_validtime );<br> }<br> }</p> </td> </tr> </table> </table> <p align="left"></p> <div style="display:none;"> <span id="url" itemprop="url">http://www.bkjia.com/PHPjc/632826.html</span><span id="indexUrl" itemprop="indexUrl">www.bkjia.com</span><span id="isOriginal" itemprop="isOriginal">true</span><span id="isBasedOnUrl" itemprop="isBasedOnUrl">http://www.bkjia.com/PHPjc/632826.html</span><span id="genre" itemprop="genre">技術記事</span><span id="description" itemprop="description">以前は、表の重複を防ぐさまざまな方法を見てきましたが、js または jquery 手順を参照してください。しかし、これは単なる 1 つの方法であり、結果として、この面の提交表ではなく、受信データの面に直接アクセスできます...</span> </div> <div class="art_confoot"></div> </table> </table>