【コラム】

ライトニングJava

66 Servletプログラミング(6) - フォワード、インクルード、リダイレクト

後藤大地  [2006/11/06]

処理を別のURLへ遷移させる方法

Servletでは、サーブレット側から別のURLに処理を遷移させる方法として次の3つを提供している。

フォワードRequestDispatcher#forward
インクルードRequestDispatcher#include
リダイレクトHttpServletResponse#sendRedirect

フォワード/インクルードはサーバ側で処理を遷移させる方法で、リクエスト内容が引き継がれるという特徴がある。つまり、HttpServletRequest経由で設定されるデータがそのまま引き継がれる。 「Servletプログラミング(4) - データスコープ、HTTPリクエスト/レスポンスヘッダ」でデータスコープについて説明したが、つまりHttpServletRequestデータスコープで動作するということである。

リダイレクトは指定したURLに処理を飛ばすだけだ。Webブラウザに対してリダイレクトするように要求を返す。リクエスト内容は引き継がれないし、Webブラウザにいったん要求を返してからWebブラウザが再度アクセスするため、URLも変更される。

フォワード/インクルード/リダイレクトはサーブレットから別のURLへ処理を遷移させる基本的な方法だ。Webアプリケーションフレームワークでも内部ではフォワードやインクルード、リダイレクトを使って処理を遷移させることがある。実際どういった遷移が実施されるのか、基本的な動作は把握しておきたい。

フォワード

処理を指定したURLに完全に渡してしまう方法がフォワードだ。フォワードはRequestDispatcherインスタンスで制御する。RequestDispatcherインスタンスはServletContextからgetRequestDispatcher(String)メソッドを使って取得できる。HttpServletRequest経由でRequestDispatcherをインスタンスを取得することもできる。ServletContextから取得する場合は絶対パスでの指定のみ許可され、HttpServletRequest経由の場合は相対パスによる指定できる。次にフォワードの実装例(リスト1 - 3)と実行例(図1)を示す。

リスト1 ForwardServlet.java - HttpServletRequestにデータを設定してからHelloWorldServletへ処理をフォワードする

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ForwardServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        // HttpServletRequestデータスコープでデータを設定
        request.setAttribute("message", "Hello World!");

        // フォワード
        getServletContext().
            getRequestDispatcher("/hello").forward(request, response);
    }
}

リスト2 HelloWorldServlet.java - ForwardServletによって設定されたデータを出力する

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        // HTML文書を出力
        response.setContentType("text/html; charset=UTF-8");

        PrintWriter out = response.getWriter();
        out.println("Forward Test");
        out.println(request.getAttribute("message"));
        out.println("");
    }
}

リスト3 WEB-INF/web.xml - デプロイメントディスクリプタ

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">

  <!--
  サーブレット名とクラスの対応
  -->
  
    <servlet-name>HelloWorldServlet</servlet-name>
    <servlet-class>HelloWorldServlet</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>ForwardServlet</servlet-name>
    <servlet-class>ForwardServlet</servlet-class>
  </servlet>

  <!--
  パスとサーブレット名の対応
  -->
  <servlet-mapping>
    <servlet-name>HelloWorldServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>ForwardServlet</servlet-name>
    <url-pattern>/forward</url-pattern>
  </servlet-mapping>
</web-app>

図1 デプロイの例

まず図2のように直接HelloWorldServletにアクセスする。当然、値は設定されていないからnullが出力される。次にForwardServletにアクセスする(図3)。ForwardServletで設定したメッセージがHelloWorldServletを経て出力されることがわかる。ここで、フォワードした場合にURLが変更されていないことに注目してほしい(図4)。フォワードはサーバ側で実行されているからだ。

図2 HelloWorldServletに直接アクセスしても値が設定されていないのでnullが表示される

図3 ForwardServletへのアクセス前

図4 ForwardServletへアクセス後 – URLが変更されていない点に注目

インクルード

フォワードと似ているが、処理を渡したあとに処理が戻ってくるものをインクルードという。処理が戻ってくる以外はフォワードとよく似ている。次に実装例(リスト4 - 6)と実行例(図5)を示す。

リスト4 HelloWorldServlet.java - IncludeServletによって設定されたデータを出力する

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                  HttpServletResponse response)
        throws IOException, ServletException
    {
        // HTML文書を出力
        response.setContentType("text/html; charset=UTF-8");

        // インクルード
        getServletContext().
            getRequestDispatcher("/include").include(request, response);

        PrintWriter out = response.getWriter();
        out.println("<html><head><title>Include Test</title></head><body>");
        out.println(request.getAttribute("message"));
        out.println("</body></html>");
    }
}

リスト5 IncludeServlet.java - HttpServletRequestにデータを設定するだけのサーブレット

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class IncludeServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        // HttpServletRequestデータスコープでデータを設定
        request.setAttribute("message", "Hello World!");
    }
}

リスト6 WEB-INF/web.xml - デプロイメントディスクリプタ

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">

  <!--
  サーブレット名とクラスの対応
  -->
  <servlet>
    <servlet-name>HelloWorldServlet</servlet-name>
    <servlet-class>HelloWorldServlet</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>IncludeServlet</servlet-name>
    <servlet-class>IncludeServlet</servlet-class>
  </servlet>

  <!--
  パスとサーブレット名の対応
  -->
  <servlet-mapping>
    <servlet-name>HelloWorldServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>IncludeServlet</servlet-name>
    <url-pattern>/include</url-pattern>
  </servlet-mapping>
</web-app>

図5 デプロイの例

ここではフォワードと逆の動作を実装してある。HelloWorldServletにアクセスがあるといったんIncludeServletをインクルードして値を設定し、再び処理がHelloWorldServletにもどってきて設定された値を出力している(図6 - 7)。こちらもURLが変更されていないことに注目したい(図8)。処理はサーバ側で完結している。

図6 IncludeServletに直接アクセスしても値を設定するだけの処理しかしていないので何も表示されない

図7 HelloWorldServletへのアクセス前

図8 HelloWorldServletへアクセス後 – URLが変更されていない点に注目

リダイレクト

フォワードやインクルードと違いリクエスト要求を引き継がずに、Webブラウザにリダイレクト要求を出して別のURLに飛ぶ方法がある。これがリダイレクトだ。次に実装例(リスト7 - 9)と実行例(図9)を示す。

リスト7 RedirectServlet.java - HttpServletRequestにデータを設定してからHelloWorldServletへリダイレクト

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RedirectServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        // HttpServletRequestデータスコープでデータを設定
        request.setAttribute("message", "Hello World!");

        // リダイレクト
        response.sendRedirect("hello");
    }
}

リスト8 HelloWorldServlet.java - HttpServletRequestからメッセージを取り出して出力

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        // HTML文書を出力
        response.setContentType("text/html; charset=UTF-8");

        PrintWriter out = response.getWriter();
        out.println("<html><head><title>Redirect Test</title></head><body>");
        out.println(request.getAttribute("message"));
        out.println("</body></html>");
    }
}

WEB-INF/web.xml - デプロイメントディスクリプタ

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">
  <!--
  サーブレット名とクラスの対応
  -->
  <servlet>
    <servlet-name>HelloWorldServlet</servlet-name>
    <servlet-class>HelloWorldServlet</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>RedirectServlet</servlet-name>
    <servlet-class>RedirectServlet</servlet-class>
  </servlet>

  <!--
  パスとサーブレット名の対応
  -->
  <servlet-mapping>
    <servlet-name>HelloWorldServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>RedirectServlet</servlet-name>
    <url-pattern>/redirect</url-pattern>
  </servlet-mapping>
</web-app>

図9 デプロイの例

まず、直接HelloWorldServletにアクセスすると図10のようにnullが表示される。何も設定していないので当然だ。次にHttpServletRequestにアクセスする(図11)。するとURLが変更されたあとで、同じくnullが表示されることに注目してほしい(図12)。URLが変更されているのはWebブラウザ側からリダイレクトが実施されたからで、nullが表示されたのはリクエスト要求が引き継がれていないからだ。

図10 直接HelloWorldServletにアクセスしても、値を設定していないのでnullが表示される

図11 RedirectServletのURLを入力してアクセスすると…

図12 HelloWorldServletのアドレスに飛び、さらにリクエスト要求は引き継がれないため、やはりnullが表示される

サーブレットから処理を遷移させる場合の注意

サーブレットから処理を遷移させるということは、つまりプログラミングにページ遷移も含むようになることをいう。こうなってくると、徐々にシステム全体が一枚岩のようになってくる。そろそろページ遷移を完全に外部の設定ファイルにまとめるか、それとも別のスマートな方法を採用するか、考えるべき時期にきたといえる。

ページ遷移が複雑になってきたら、もはやWebアプリケーションフレームワークの採用を検討した方がいい。プログラム側で自在にページ遷移をするようになると、作成時はいいが、あとから仕様変更があった場合とメンテナンスする段階で苦労することになる。

またWebアプリケーションフレームワークによってはフォワードやインクルード、リダイレクトに対する前提知識があるものとしてドキュメントが用意されていることもある。少なくともServletレベルでこのあたりの処理がどうなっているか、基本的なことは把握しておいたほうがいいだろう。

提供:毎日就職ナビ

会員登録はこちら

学生のための就職情報サイト「毎日就職ナビ」。6,000社以上の新卒採用情報が常時掲載され、社内の雰囲気が伝わる情報画面、さまざまな項目での会社検索、エントリーや説明会検索など、機能も充実。無料適職診断、就活Q & A、エントリーシート添削講座など、就職活動に役立つ記事も満載です。研究者、エンジニアを目指す学生の方々も是非エントリーしてください。お待ちしています!

毎日コミュニケーションズ 就職情報事業本部はプライバシーマークを取得しています。

    新着記事

    特設サイトの情報

    人気記事

    一覧

    イチオシ記事

    新着記事

    特別企画

    一覧