今回は、クッキーを使用したサンプルサーブレットを作成していきます。
以前に作成したサンプルサーブレットを改良します。
まずは、作成していくサーブレットの内容を下記します。
適当な質問フォームを表示し、ユーザーに入力させます。
送信ボタンが押されたら、入力されたデータをサーブレット「RequestRecv」に渡します。
「RequestRecv」では、質問フォームで入力されたデータを表示させるとともに追加の質問フォームを表示するHTMLをHTTPレスポンスとして返します。その際、クッキーを発行し、入力データもクライアントへ渡します。
データ入力後、送信ボタンを押せば、入力されたデータがサーブレット「SampleGetCookie」に渡されるようにします。さらに、送信ボタンを押した際のHTTPリクエストの中に、クッキーにより、前回の入力データを格納し、「SampleGetCookie」に渡します。
「SampleGetCookie」は、質問フォームで入力されたデータを表示させるとともに、クッキーからデータを取得し、そのデータを表示するHTMLをHTTPレスポンスとして返します。
クッキー操作メソッド一覧
クッキーを操作するには、主に下記メソッドを使用します。
- void addCookie(Cookie cookie)
- クライアントにクッキーを保存する場合はHttpServletResponseインターフェースで定義されているaddCookieメソッドを使用します。なお、1つのドメインに対して保存できるCookieの数は20個まで、1つのCookieは名前と値を合わせて4096バイトまで、クライアントに保存できるCookieの数は全体で300個までに制限されます。
addCookieメソッドの引数にはjavax.servlet.http.Cookieオブジェクトを設定します。Cookieクラスの引数はString型の「名前」と「値」の2つです。コンストラクタの定義は以下になります。
Cookie(String name, String value) - void setMaxAge(int 有効期限)
- クッキーの有効期限を秒単位で指定します。指定した有効期限を超えた場合、クライアントのWebブラウザの保存場所から破棄されます。負の値を指定するとWebブラウザが終了したときに削除されます。また0を指定すると直ちに削除されます。
- Cookie[ ] getCookies()
- クライアントに保存されているクッキーのテキストを取得するにはHttpServletRequestインターフェースに定義されているgetCookiesメソッドを使用します。戻り値にはCookieオブジェクトの配列が返されます。
クッキーの取得は保存したWebサーバーから読み出すことはできますが、他のWebサーバーが保存したクッキーを取得することはできません。
クライアントに保存したCookieオブジェクトを削除するには、コンストラクタで値を空にしたインスタンスを生成します。もしくは上記したsetMaxAge()メソッドの引数(有効期限)で0を設定します。
Cookie cookMemberName = new Cookie("MemberName", ""); cookMemberName.setMaxAge(0);
サンプルサーブレットのソースコード
以降に各サーブレットのソースコードを記載します。(冗長な部分は省略し、主要な部分のみ記載しています。)
まずは、フォームを作成します。フォーム(HTMLファイル)の詳細な作成方法はこちらを参照してください。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>サーブレットへのデータ送信</title> </head> <body> <h1>サーブレットへのデータ送信</h1> <form action="RequestRecv" method="GET"> <p>好きなゲームは?</p> <input type="TEXT" name="Game" size=40> <input type="submit" value="送信"> </form> </body> </html>
テキストボックスに入力後、送信ボタンをクリックした場合のアクションとして、RequestRecvサーブレットを呼び出してします。その際のリクエストの種類には「GET」を指定しています。
RequestRecvサーブレットのdoGet()メソッドには下記のように記述します。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String gameName = request.getParameter("Game"); Cookie cookGameTitle = new Cookie("GameTitle", gameName); cookGameTitle.setMaxAge(60*60*24); response.addCookie(cookGameTitle); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>あなたの回答</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>あなたの回答</h1>"); out.println("好きなゲームは" + gameName + "です。<br /><br />"); out.println("では、そのゲームの好きなナンバリングは?<br />"); out.println("<form action=\"SampleGetCookie\" method=\"POST\">"); out.println("<input type=\"TEXT\" name=\"GameNumbering\" size=40>"); out.println("<input type=submit value=\"次のサーブレットを起動\">"); out.println("</form>"); out.println("</body>"); out.println("</html>"); }
getParameter()メソッドで取得したデータを、addCookie()メソッドでクッキーに保存します。その前の、setMaxAge()メソッドでは、有効期限を1日としています。
「次のサーブレットを起動」ボタンをクリックした場合のアクションとして、SampleGetCookieサーブレットを呼び出してします。その際のリクエストの種類には「POST」を指定しています。また、注意点として、println()メソッドでダブルクォーテーショ(“)を出力したい場合は、「\」(バックスラッシュもしくは環境によっては円マーク)でエスケープしてやる必要があります。
SampleGetCサーブレットのdoPost()メソッドには下記のように記述します。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); String gameTitle = ""; String gameNumbering = request.getParameter("GameNumbering"); if (cookies != null) { for (Cookie cook : cookies) { if (cook.getName().equals("GameTitle")) { gameTitle = cook.getValue(); } } } PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>クッキーサンプル</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>クッキーサンプル</h1>"); out.println("好きなゲームは" + gameTitle + "です。<br />"); out.println("ナンバリングは" + gameNumbering + "です。<br />"); out.println("</body>"); out.println("</html>"); }
getCookies()メソッドで、クッキーを取得し、さらに、getParameter()メソッドでデータを取得しています。そして、それらを表示するHTMLを生成しています。
不完全なサーブレット
上記サンプルは一見、動いたように見えますが、致命的な問題が存在しています。例えば、フォーム画面でテキストボックスに日本語を入力して「送信」ボタンをクリックしたら、HTTPステータス500が返り、サーバー内部エラーとなります。
サーバー内部では「java.lang.IllegalArgumentException: Control character in cookie value or attribute.」という例外が発生しています。
このエラーの発生箇所は下記になります。
Cookie cookGameTitle = new Cookie("GameTitle", gameName); cookGameTitle.setMaxAge(60*60*24); response.addCookie(cookGameTitle);
実は、Cookieの値として使用できる文字には制限があり、日本語をそのままクッキーに保存しようとしているため、エラーが発生しています。このエラーは回避するにはURLエンコードと呼ばれる処理を行う必要があります。
URLエンコードとデコードはそれぞれ、URLEncoder.encode()メソッド、URLDecoder.decode()メソッドで行えます。
URLEncoder.encodeは、テキストをURLエンコードしたものを返し、URLDecoder.decodeはエンコードされたテキストを元のテキストに戻したものを返します。
これらを利用し、テキストをエンコードしてクッキーに保存し、クッキーから取り出したら再びデコードして利用することで、日本語でも問題なくクッキーに保管しておけるようになります。
但し、今ではこの方法はあまり使われません。クッキーをそのまま使うにはかなり致命的な問題があり、推奨されないからです。では、どのように「ステートフル」を実現すれば良いのでしょう。次回の記事でクッキーの問題点と代替の方法について見ていきます。