サーブレットの基礎知識

WebブラウザのBackボタンが押されることで起こる問題にどう対処したらよいですか?

テンアートニ 中越智哉
2001/6/6

  Webブラウザには、それまでに閲覧したページをもう一度見るための、Backボタン(左向き矢印に「戻る」と書かれたボタン)があります。Webブラウザを利用するユーザーにとってはこのボタンを使うことでURLを再入力することなく前のページを閲覧できて便利なのですが、Webアプリケーション側においては、このBackボタンの機能が問題になることがあります。

 たいていのWebブラウザでは、表示したHTMLや画像をローカルのメモリやディスク内にキャッシュする仕組みを持っています。Backボタンによって前のページに戻る場合は、Webサーバに再度接続してロードするのではなく、キャッシュしておいたファイルを表示することで、通常の呼び出しよりも速いレスポンスでページを閲覧できるようにしています。

通常Webブラウザは、一度呼び出したページを、 ローカルのメモリやディスクにキャッシュとして保存している

WebブラウザのBackボタンで戻った場合は、リクエストは Webサーバには送られず、ローカルのキャッシュにあるページが読み出されて表示される

 Webアプリケーションでは、参照系(情報の閲覧)だけでなく、更新系(追加、更新、削除)の処理を行うことが頻繁にあります。そのため、同じ情報を別のユーザーが更新したりすることがありうるため、同じ画面であってもその表示内容が変更されているということもあります。

 こういった場合に、ユーザーがBackボタンで画面遷移を行ってしまうと、本来表示すべき情報は更新されているにもかかわらず、ディスクにキャッシュされた古い情報が表示されてしまうことになります。単なる参照系処理だけならばそれほど問題にはなりませんが、更新系処理においてそういったことが起きてしまうと、すでに削除されているレコードに対して操作を行うことになったり、更新時の情報の整合性に問題が生じることがでてきます。ですから、Webアプリケーションにおいては、ユーザーがBackボタンで画面を戻した場合のことも考慮しなくてはなりません。

データベースから参照した内容をHTMLとして表示し、 それをキャッシュに格納

別のクライアントがデータを更新

別のユーザーがデータを更新した場合、キャッシュの内容と 現在の内容に不整合が生じてしまう

 こういった問題への対策は、いくつか考えられます。

(1)ブラウザにBackボタンを表示しないようにする
(2)ブラウザのキャッシュが無効になるように制御する
(3)Backボタンで画面が戻されても、不整合が起こらないようにアプリケーションを設計する

 (1)は、JavaScriptなどを使ってWebブラウザのメニューやボタンを非表示に設定するという方法です。これによって、ユーザーがBackボタンを押すことがなくなるため、ある程度の効果はありますが、ユーザーがマウスを右クリックしてポップアップメニューを表示して戻る操作を行ったり、ショートカットキーで戻る操作を行う可能性があるため、完全な解決策にはなりません。

 (2)は、サーブレットからHTTPヘッダを操作してWebブラウザがページをキャッシュすることを制御するというものです。以下のリストは、その処理を実装した例です。11行目から13行目の処理で、ブラウザがこの出力をキャッシュしないように設定しています。この設定を行うと、WebブラウザのBackボタンで戻った場合に、自動的にそのURLがリロードされるか、「データがありません」というようにリロードを要求する画面が表示されるため古い情報が表示されることはありません。この方法は、多くの場合において有効に使えますがWebブラウザや設定によっては、この方法が使えない場合もありえますので注意してください。

1:import javax.servlet.*;
2:import javax.servlet.http.*;
3:import java.io.*;
4:import java.util.*;
5:
6:public class CacheServlet extends HttpServlet {
7:  public void doGet(HttpServletRequest request, HttpServletResponse 8:  response) throws ServletException, IOException {
9:
10:    // キャッシュを無効にする
11:    response.setHeader("Pragma","no-cache");
12:    response.setHeader("Cache-Control","no-cache");
13:    response.setDateHeader("Expires",0);
14:
15:    response.setContentType("text/html; charset=Shift_JIS");
16:    PrintWriter out = response.getWriter();
17:    out.println("<html>");
18:    out.println("<head><title>CacheServlet</title></head>");
19:    out.println("<body>");
20:    out.println("このページはキャッシュされません。");
21:    out.println("</body></html>");
22:    out.close();
23:  }
24:}

 (3)は、Backボタンで戻った後で送信処理が行われても、それを判別できるような仕組みを作り込むことで、不整合を解決しようという方法です。例えば、同一セッションで、リクエストごとに毎回シーケンス番号を送信するようにしておきます。送信されたシーケンス番号と、サーバで記録しているリクエスト回数とを比較すれば、Backボタンで戻ったときにはシーケンス番号が古いままになるので、サーバ側でBackボタンで戻った画面からのリクエストであるということが判別できます(これはあくまで一例であり、実際のアプリケーションで適用して検証したわけではありません)。

 また、これは余談ですが、開発時にWeb ブラウザのキャッシュを有効にしていると、クラスを更新したのに表示結果が変わらないということが起きることが多いので、開発時はブラウザ側でキャッシュをできるだけ効かないように設定するとよいでしょう。


「Java Solution FAQ」




Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間