☕
[Servlet]リダイレクト&ブラウザのリロード対策
◆はじめに
こういうホップアップが、
ページのリロード時に出てきたりしますよね。
今回は、こちらのホップアップが出ないようにしたいと思います。
リロード対策をしておくと、
フォームの再送信でおこる重複など防ぐことができるので、
実装しておくといいでしょう。
本記事では、「リダイレクト」と「リロード対策」の2つをご紹介します。
リロード対策を行うには、
まずは、リダイレクトについて知っておく必要があるためです。
◆リダイレクトとは
リダイレクトとは、
ページの切り替え方法の一つです。
ウェブブラウザに、移動先のURLをレスポンスで送ります。
すると、ウェブブラウザが自動的にURLの移動リクエストを返すので、
ページを移動します。
なので、例えば、GoogleのHPなど
別のサーバー上のページを指定することもできます。
【リダイレクト】
response.sendRedirect("移動先のURL");
リダイレクト以外にも、ページの切り替え方法があります。
それが、フォワードです。
こちらは、サーバー側で作成したページを返却します。
なので、同じサーバー上のページしか指定はできません。
【フォワード】
RequestDispatcher dispatcher = request.getRequestDispatcher("転送先のページ");
dispatcher.forward(req, resp);
◆リロード対策とは
リロード対策をしていない場合は、
ページを更新した時や戻るボタンを押した際にも、
下記のホップアップが表示されます。
これは、画面移動するたびに、Postが投げられていることで起こります。
そこで、「PRGパターン」を使用します。
PRGパターンとは、Post Redirect Getの頭文字を繋いだ言葉です。
処理の流れとしては、
①POSTメソッドでリクエストを受け取けとる
②のメソッドの処理の終わりにRedirectする
③処理後の結果はGetメソッドで呼び出せる画面に返す
また、このPRGパターンで実装することで、
例えば、オンライン注文のばあいなどで、
再度Postされて、2重注文が起こることを防ぐことができます。
◆前提/環境など
本記事は、以下の記事の「InputFunction」プロジェクトをベースに作成します。
Echo機能のServletの処理時間を計測するFilterを実装しています。
また、Sessionで結果を保持するようになっています。
以下の記事の内容を踏まえた前提で、記事は書いていますのでご了承ください。
仕様は以下です。
<Echo機能> ・文字列を入力し、送信ボタンを押すと、送信した文字列が画面に表示される。
<Validation機能> ・送信された文字列が空欄の場合は,「この項目は必須入力です。」と文字列が表示される。
<TimeFiletr機能(Listener)> ・Servletの処理時間を計測し、コンソールに表示
<Session機能> ・処理結果をSessionがリセットされるまで保持する
※本記事では、 eclipseは以下のバージョンでの画像を使用しています。
日本語化はしていません。
他のversionでの動作は確認しておりません。
◆手順
それでは、実際に実装してみたいと思います。
リダイレクト先を、Postを受けたページのURLにします。
そして、処理の内容はSessionで保持しておけば、
元のページに戻った時に処理結果が表示されています。
①ソースコード(「EchoServlet.java」の変更点)
コードの変更点としては、
フォワード処理をリダイレクト処理に書き換えているだけです。
コードの全体は以下です。
EchoServlet.java
package input;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
//URLを定義するアノテーション
@WebServlet(urlPatterns = {"/echo"})
//Validation機能を持たせたEchoサーブレットクラス
public class EchoServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
//画面からPostされた場合に起動するメインとなるメソッド
@Override
protected void doPost(HttpServletRequest request , HttpServletResponse response)throws ServletException,
IOException {
//取得文字のエンコード設定(文字化け防止)
request.setCharacterEncoding("UTF-8");
//セッションの取得
HttpSession session = request.getSession();
//Validation機能
//テキストボックスのtextを保持したFormの作成
TextForm SetForm = toSetForm(request);
//toValidationFormをバリデーションに通す。
//引っかかった場合はrequestにvalidationMessageをセットする
setValidationAttribute(session,SetForm);
//Echo機能
//Echoメッセージのセット
if(session.getAttribute("validationMessage") == null) {
//バリデーションメッセージがrequestに付与されていない場合、
//Formに保持していたtextをrequestにEcho用に付与する
String EchoMessage = SetForm.getTextMessage();
//取得したセッションのmessageに対してセットする
session.setAttribute("message",EchoMessage);
}
//Echo保持されている時の、バリデーションが働いた時,EchoMessageは初期化
if(session.getAttribute("validationMessage") != null) {
//取得したセッションのmessageに対してセットする
session.setAttribute("message",null);
}
//セッションIDの取得
String session_id = session.getId();
//コンソールに出力
System.out.println("セッションID:" + session_id );
System.out.println("validationMessage<session>):"+session.getAttribute("validationMessage"));
System.out.println("message<session>):"+session.getAttribute("message"));
//リダイレクト
//コンテキストルート(InputFunction)の取得
String contextPath = getServletContext().getContextPath();
//リダイレクトpathを設定(表示させるページ→echo.jsp)
String path = contextPath + "/echo.jsp";
//実際にリダイレクトを行っている関数
response.sendRedirect(path);
// //RequestDispatcher
// RequestDispatcher dispatcher = request.getRequestDispatcher("echo.jsp");
// dispatcher.forward(request, response);
}
//「セッションのリセット」のリクエストパラメータが渡された場合が押された場合、セッションを無効にする
@Override
protected void doGet(HttpServletRequest request , HttpServletResponse response) throws ServletException,IOException {
Map<String , String[]> parameterMap = request.getParameterMap();
if(parameterMap.containsKey("reset") == true) {
//セッションの取得
HttpSession session = request.getSession();
//セッションを無効にする
session.invalidate();
}
//リダイレクト
//コンテキストルート(InputFunction)の取得
String contextPath = getServletContext().getContextPath();
//リダイレクトpathを設定(表示させるページ→echo.jsp)
String path = contextPath + "/echo.jsp";
//実際にリダイレクトを行っている関数
response.sendRedirect(path);
//PRGパターンの場合,RequestDispatcherは不要
// RequestDispatcher dispatcher = request.getRequestDispatcher("echo.jsp");
// dispatcher.forward(request,response);
}
//テキストを保持したFormを作成するメソッド
private TextForm toSetForm(HttpServletRequest request) {
//バリデーションに通すためのフォームのインスタンス作成
TextForm SetForm = new TextForm();
//テキストボックスの文字列を取得
String text = request.getParameter("m");
//テキストボックスが空欄ではない場合
if(text.isEmpty() == false) {
//テキストを保持したFormを返す(今回は実装してないがNull以外のバリデーション機能もあるため)
SetForm.setTextMessage(text);
}
return SetForm;
}
//バリデーション用のメソッド
private void setValidationAttribute(HttpSession session, TextForm toValidationForm) {
//toValidationFormに対してバリデーションを実行
Map<String,List<String>> validationMessage = validate(session,toValidationForm);
//バリデーションメッセージがセットされている場合、requestにバリデーションメッセージを付与
if(validationMessage.isEmpty() == false) {
session.setAttribute("validationMessage", validationMessage);
return;
}
//Sessionで保持されていたバリデーションメッセージの初期化
if(validationMessage.isEmpty() == true) {
session.setAttribute("validationMessage", null);
return;
}
}
//validate
private Map<String , List<String>> validate(HttpSession session,TextForm toSetForm){
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<TextForm>> validationResult = validator.validate(toSetForm);
Map<String , List<String>> ret = new HashMap<String , List<String>>();
for(ConstraintViolation<TextForm> violation : validationResult) {
String propertyPath = violation.getPropertyPath().toString();
List<String> messages = ret.get(propertyPath);
if(messages == null) {
messages = new ArrayList<String>();
ret.put(propertyPath, messages);
}
messages.add(violation.getMessage());
}
return ret;
}
}
②ソースコード(「echo.jsp」の変更点 )
<%-- Echoメッセージの出力--%>
<c:if test="${ not empty message }">
入力された文字は、「${ message }」です。
</c:if>
以上の箇所で、「RequestScope.message」としてましたが、
Postではなく、リダイレクトにするとGetになるためか使用できなかったため、
「message」にしました。
コードの全体は以下です。
echo.jsp
<%@page language="java" contentType = "text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@page isELIgnored= "false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<title>Echo機能+Validation機能+TimeFilter機能(Listener)+Session機能+リロード対策</title>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h3>Echo機能+Validation機能+TimeFilter機能(Listener)+Session機能+リロード対策</h3>
<%-- リセットのリクエストパラメータを渡す--%>
<a href="echo?reset">セッションのリセット</a>
<%-- 入力フォーム --%>
<form action="echo" method="post">
<br />入力してください :
<%-- テキストボックス(m) --%>
<input type="text" name="m" />
<%-- Validationメッセージの出力 --%>
<%-- var="vm" → c:out で valueにセットする際に使う名称 --%>
<%-- [' ']には、 MessageForm.javaのテキストボックスのメッセージ保持用のパラメータ名を入れる --%>
<c:forEach items="${validationMessage['textMessage']}" var="vm">
<c:out value="${vm}"/>
</c:forEach>
<br />
<%-- 送信ボタン --%>
<input type="submit" /> <br />
</form>
<br />
<%-- Echoメッセージの出力--%>
<c:if test="${ not empty message }">
入力された文字は、「${ message }」です。
</c:if>
</body>
③動作チェック
・Tomcatなどアプリケーションサーバーを起動
・実装後に以下のURLにアクセス
画面が表示されますので、
・テキストを打つ
・送信ボタンを押す
・更新ボタンを押す
すると、PRGパターンでリダイレクトされ、
再度、同じURLでページが開き、Sessionで保持された文字が表示されています。
先ほど更新ボタンを押した際に、
下記のホップアップが表示されていなkれば、
リロード対策が成功しています。
◆さいごに
以上です。いかがだったでしょうか。
Sessionの実装はできたでしょうか。
今回のポイントです。
①リロード対策は、PRGパターンを使用する
②フォワードではなく、リダイレクトを使用する
③値の保持が必要な場合は、Sessionを使用する
コードに関しての詳しい説明に関しては、
あまり書いていません。少し、コード内にコメントはつけてます。
あとは、他の参考に載せている書籍やサイトなど調べてみてください。
最後まで、ご覧いただきありがとうございました。
◆参考
本記事は、以下の書籍を大いに参考にしています。
より詳しく知りたい方は、どうぞ。
・関連記事、その他
Discussion