-------- アクション -------- agata baba -------- 2008-09-03 -------- {目次} *{{{#アクション}アクション}} *{{{#アクションの定義}アクションの定義}} *{{{#アクションクラス}アクションクラス}} *{{{#アクションメソッド}アクションメソッド}} *{{{#リクエストパラメータのバインド}リクエストパラメータのバインド}} *{{{#コンバータ}コンバータ}} *{{{#ActionResult}ActionResult}} *{{{#アノテーション}アノテーション}} *{{{#事前処理事後処理}事前処理・事後処理}} *{{{#リクエストからアクション実行までのフロー}リクエストからアクション実行までのフロー}} *{{{#アクションのテスト}アクションのテスト}} {アクション} アクションとは特定の URI へのリクエストに対応する処理で、Cubby のユーザーが実装する部分です。\ URI に対応するメソッドをアクションメソッド、アクションメソッドが定義されたクラスをアクションクラスといいます。 {アクションの定義} *{アクションクラス} {{{cubby/apidocs/org/seasar/cubby/action/Action.html}Action}} を継承し、〜Action という名前のクラスをアクションクラスとして認識します。 *{アクションメソッド} アクションクラスに定義されたメソッドのうち、アクセス修飾子が public で戻り値が {{{cubby/apidocs/org/seasar/cubby/action/ActionResult.html}ActionResult}} のメソッドをアクションメソッドとして認識します。\ このメソッドは AOP によってエンハンスされ、メソッド実行時に以下の処理が実行されるようになります。 *アクションの初期化やリクエストパラメータからフォームオブジェクトへのバインド ({{{cubby/apidocs/org/seasar/cubby/interceptor/InitializeInterceptor.html}InitializeInterceptor}}) *入力検証 ({{{cubby/apidocs/org/seasar/cubby/interceptor/ValidationInterceptor.html}ValidationInterceptor}}) [] これらは AOP によってメソッド自体がエンハンスされるため、アクションメソッドの中から他のアクションメソッドを呼び出した場合も上記の処理が実行されます。 *{リクエストパラメータのバインド} クライアントから送信されたリクエストのパラメータはフォームオブジェクトへバインドされます。 フォームオブジェクトはアクションメソッドごとに指定することが可能で、 アクション自身かアクションのプロパティから取得するオブジェクトのいずれかを使用できます。 デフォルトではアクション自身をフォームオブジェクトとして使用しますが、 {{{cubby/apidocs/org/seasar/cubby/action/Form.html}@Form}} でアクションのプロパティ名を指定することによって、 そのプロパティから取得できるオブジェクトをフォームオブジェクトとして使用します。 クライアントから送信されたリクエストパラメータは、まず Map\ に変換します。\ サーブレット API 上ではリクエストパラメータの値は String だけですが、 ファイルアップロード等に対応するため、この Map には String 以外の型も格納されます。\ ファイルアップロードで使用する multipart のリクエストを受けた場合には、アップロードされたファイルを表す {{{http://commons.apache.org/fileupload/apidocs/org/apache/commons/fileupload/FileItem.html}FileItem}} のインスタンスが格納されます。 そして、上記の Map をフォームオブジェクトへバインドします。\ デフォルトではアクションのプロパティのうち、 {{{cubby/apidocs/org/seasar/cubby/action/RequestParameter}@RequestParameter}} がついたプロパティに同名のリクエストパラメータをバインドします。\ {{{cubby/apidocs/org/seasar/cubby/action/Form.html#bindingType()}@Form#bindingType}} でバインディングの方法を指定することで、以下の挙動から選択することができます。 * 全プロパティをバインド対象とする ({{{cubby/apidocs/org/seasar/cubby/action/RequestParameterBindingType#ALL_PROPERTIES}ALL_PROPERTIES)}}) * {{{cubby/apidocs/org/seasar/cubby/action/RequestParameter}@RequestParameter}} がついたプロパティをバインド対象とする ({{{cubby/apidocs/org/seasar/cubby/action/RequestParameterBindingType#ONLY_SPECIFIED_PROPERTIES}ONLY_SPECIFIED_PROPERTIES)}}) * バインドしない ({{{cubby/apidocs/org/seasar/cubby/action/RequestParameterBindingType#NONE}NONE)}}) * {{{cubby/apidocs/org/seasar/cubby/action/Form.html}@Form}} で修飾して bindingType を指定しない場合は ALL_PROPERTIES と同様に全プロパティをバインド対象とする [] また、バインドされるプロパティの型によってはコンバータによる型変換を行います。 *{コンバータ} コンバータとは、リクエストパラメータをフォームオブジェクトへバインドする際に型変換を行う {{{cubby/apidocs/org/seasar/cubby/converter/Converter.html}Converter}} を実装したクラスです。 標準でサポートしているコンバータは{{{cubby/apidocs/index.html?org/seasar/cubby/converter/impl/package-summary.html}APIドキュメント}}をご覧下さい。 また、独自のコンバータを実装することによって、標準ではサポートしていない型へリクエストパラメータをバインドすることができます。 以下の BookConverter.java は ISBN を表す文字列からデータベースを参照して Book に変換するコンバータです。 変換結果は +------------------------------------------------------+ : @RequestParameter public Book book; : +------------------------------------------------------+ というプロパティにバインドされます。 <> +------------------------------------------------------+ package org.seasar.cubby.examples.other.web.converter; import org.seasar.cubby.converter.ConversionHelper; import org.seasar.cubby.converter.impl.AbstractConverter; /** * ISDN を表す文字列と Book を相互変換するコンバータです。 */ public class BookConverter extends AbstractConverter { public BookDao bookDao; /** * Book.class を返すことで、このコンバータが任意の値を Book へ変換できることを示します。 */ public Class getObjectType() { return Book.class; } /** * リクエストパラメータ(ここではISBN)を Book へ変換します。 */ public Object convertToObject(Object value, Class objectType, ConversionHelper helper) { if (value == null) { return null; } String isbn13 = value.toString(); Book book = bookDao.findByIsbn13(isbn13); return book; } /** * Book から表示用の文字列(ISBN)へ変換します。 */ public String convertToString(Object value, ConversionHelper helper) { Book book = Book.class.cast(value); return book.getIsbn13(); } } +------------------------------------------------------+ *{ActionResult} 以下のクラスは {{{cubby/apidocs/org/seasar/cubby/action/ActionResult.html}ActionResult}} を実装するクラスで、アクションメソッドの戻り値として使用できます。 *---------------------------------------------------------------------+--------------------------------------------------------------------+ || クラス名 || 説明 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Forward.html}Forward}} | 指定されたパスにフォワードします。 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Redirect.html}Redirect}} | 指定されたパスにリダイレクトします。 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Direct.html}Direct}} | 何も処理を行いません。アクションメソッド中でレスポンスに直接書き込みます。ファイルや画像をブラウザに返す時に使用します。 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Json.html}Json}} | 指定されたオブジェクトを Json 形式に変換してレスポンスを返します。 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/SendError.html}SendError}} | 指定されたエラー系のステータスコードを返します。 | *---------------------------------------------------------------------+--------------------------------------------------------------------+ [] *{アノテーション} アクションクラスやアクションメソッドに以下のアノテーションを指定することで、アクションのメタ情報を設定することができます。 *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ || アノテーション名 || 説明 || 省略した場合 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Path.html}@Path}} | アクションメソッドに対応する URI を指定します。 | XxxAction#yyyy() の場合は /xxx/yyyy になります。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/OnSubmit.html}@OnSubmit}} | アクションメソッドへ振り分けるためのリクエストパラメータ名を指定します。 | リクエストパラメータによる振り分けを行いません。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Accept.html}@Accept}} | アクションメソッドが受け付ける HTTP メソッドを指定します。 | GET と POST に対応するメソッドになります。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Form.html}@Form}} | リクエストパラメータをバインドするオブジェクトを指定します。 | アクションクラスのインスタンスにリクエストパラメータをバインドします。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/RequestParameter}@RequestParameter}} | リクエストパラメータのバインド対象とするプロパティを指定します。 | リクエストパラメータのバインド対象としません。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/InitializeMethod.html}@InitializeMethod}} | アクションメソッド実行前に実行する初期化メソッドを指定します。 | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#initialize()}Action#initialize}} を実行します。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/InitializeMethod.html}@PreRenderMethod}} | フォワード直前に実行するメソッドを指定します。 | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#prerender()}Action#prerender}} を実行します。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/InitializeMethod.html}@PostRenderMethod}} | フォワード直後に実行するメソッドを指定します。 | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#postrender()}Action#postrender}} を実行します。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Validation.html}@Validation}} | 入力検証を定義したプロパティを指定します。 | 入力検証を行いません。 | *------------------------------------------------------------------------------------+--------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------+ [] *{事前処理・事後処理} アクションクラスにある以下のメソッドをオーバーライドすることで、アクションメソッド実行の前後にフック処理を記述することができます。\ これらはアクションクラスごとの処理となるので、アクションメソッドごとに異なる処理をしたい場合は{{{#アノテーション}アノテーション}}(@InitializeMethod、@PreRenderMethod、@PostRenderMethod)でメソッドを指定してください。 *---------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+ || メソッド名 || 説明 | *---------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#initialize()}Action#initialize}} | アクションメソッドの実行前に呼ばれます。パラメータのバインディング前に呼ばれるので、パラメータを使用したい場合はリクエストから直接取得する必要があります。 | *---------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#prerender()}Action#prerender}} | フォーワードの直前で呼ばれます。対象のActionクラスのフォワード先で必ず使用する共通のデータなどを取得する目的で使用します。 | *---------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+ | {{{cubby/apidocs/org/seasar/cubby/action/Action.html#postrender()}Action#postrender}} | フォワードの直後で呼ばれます。通常はあまり使用することはないでしょう。 | *---------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+ また、アクションメソッド実行の前後に処理を組み込みたい場合は、{{{http://s2container.seasar.org/2.4/ja/aop.html}Seasar の AOP}} を使用して処理をフックすることもできます。 [] {リクエストからアクション実行までのフロー} ブラウザのリクエストからアクション実行までのフローは以下のようになります。 <<リクエストからアクション実行までのフロー>> [images/action_sequence.png]リクエストからアクション実行までのフロー {アクションのテスト} アクションの単体テストは {{{cubby/apidocs/org/seasar/cubby/unit/CubbyTestCase.html}CubbyTestCase}} を使用します。 Seasar の S2Unit の機能を全て使用できるので {{{http://s2container.seasar.org/2.4/ja/S2Unit.html}SeasarのS2Unitのドキュメント}}も合わせてご覧ください。 [[1]]CubbyTestCase を継承して、XXXXTest を作成します。 [[2]]テスト対象のアクションをフィールドに用意します。これはアクション実行後に結果を取得するためです。 [[3]]setUpメソッドで app.dicon を読み込み、 Seasar2 で管理されるコンポーネント(テスト対象のアクションを含む)を初期化します。 [[4]]アクションのメソッドごとにテストメソッドを書きます。メソッド名は test から始めます。 [[5]]リクエストパラメータのセットなど、テストに必要な初期化処理は各テストメソッド用のセットアップメソッドで行います。メソッド名は setup から始めます。 [[6]]{{{cubby/apidocs/org/seasar/cubby/unit/CubbyTestCase.html#processAction(java.lang.String)}CubbyTestCase#processAction}} にリクエストのパスを指定して、アクションメソッドを実行します。CoolURI(パラメータ付きのURI)の場合、パスにパラメータを含んで実行します(TodoActionTest#)。 [[7]]戻り値を{{{cubby/apidocs/org/seasar/cubby/unit/CubbyTestCase.html#assertPathEquals(java.lang.Class,java.lang.String,org.seasar.cubby.action.ActionResult)}CubbyTestCase#processAction}}で結果の ActionResult の型とパスをチェックします。パスのチェックは Forward と Redirect の場合のみ行われます。 [[8]]必要に応じてアクションクラスのフィールドやリクエストやセッションの値をチェックします。 <> +------------------------------------------------------+ package org.seasar.cubby.examples.other.web.hello; import org.seasar.cubby.action.ActionResult; import org.seasar.cubby.action.Forward; import org.seasar.cubby.unit.CubbyTestCase; public class HelloActionTest extends CubbyTestCase { // 対象のアクション private HelloAction action; // 初期化処理 protected void setUp() throws Exception { // diconファイルの読み込み include("app.dicon"); } public void testIndex() throws Exception { // アクションの実行 ActionResult result = processAction("/hello/"); // 結果のチェック assertPathEquals(Forward.class, "input.jsp", result); } public void setupMessage() { // リクエストパラメータのセット getRequest().addParameter("name", "name1"); } public void testMessage() throws Exception { // アクションの実行 ActionResult result = processAction("/hello/message"); // 結果のチェック assertPathEquals(Forward.class, "result.jsp", result); // 実行後のアクションの状態を確認 assertEquals("name1", action.name); } } +------------------------------------------------------+ <> +------------------------------------------------------+ package org.seasar.cubby.examples.todo.action; import org.seasar.cubby.action.ActionResult; import org.seasar.cubby.action.Forward; import org.seasar.cubby.action.Redirect; import org.seasar.cubby.examples.RunDdlServletRequestListener; import org.seasar.cubby.unit.CubbyTestCase; public class TodoActionTest extends CubbyTestCase { private TodoAction action; protected void setUp() throws Exception { include("app.dicon"); RunDdlServletRequestListener listener = new RunDdlServletRequestListener(); listener.requestInitialized(null); } @Override protected void setUpAfterBindFields() throws Throwable { super.setUpAfterBindFields(); getRequest().addParameter("userId", "test"); getRequest().addParameter("password", "test"); // 後続のテストを実行するためにログインアクションを実行 assertPathEquals(Redirect.class, "/todo/", processAction("/todo/login/process")); } public void testShow() throws Exception { this.readXlsAllReplaceDb("TodoActionTest_PREPARE.xls"); // CoolURIの場合のテスト ActionResult result = processAction("/todo/1"); assertPathEquals(Forward.class, "show.jsp", result); assertEquals(new Integer(1), action.id); assertEquals("todo1", action.text); assertEquals("todo1 memo", action.memo); assertEquals(new Integer(1), action.todoType.getId()); assertEquals("type1", action.todoType.getName()); assertEquals("2008-01-01", action.limitDate); } } +------------------------------------------------------+