リクエストパラメータを受けとりたくないフィールドの設定方法(アノテーション編)

# まえがき

今回はActionクラスのフィールドと、リクエストパラメータの関係についてです。Conventionプラグインも使いますので導入しておいてください。

なお、Struts2では、全てのリクエストに対するパラメータ名を指定したフィルタリングはParametersInterceptorで行いますが、個別Actionクラスでさらにフィルタリングも設定可能です。それがアノテーションでのフィールド設定です。

## Actionクラスのフィールドはリクエストパラメータを反映する

例えばリクエストパラメータとして、以下のクエリ文字列の場合を想定します。

`name=capybara&phone=09012345678`

これに対し、Actionクラスにはnameとphoneフィールドを用意して、get/setメソッドも設置しておきます。lombok使っている場合は省略できますね。

```java:SampleAction.java
public class SampleAction extends ActionSupport {

	@Action("")
	public String start() throws Exception {
		return SUCCESS;
	}

	@Getter @Setter
	private String name;

	@Getter @Setter
	private String phone;
}
```

このように、リクエストパラメータの名前と同じフィールドがあれば格納されます。

## リクエストパラメータに対するフィールドがActionクラスになかった場合

リクエストパラメータに対するフィールドがActionクラスになかった場合は、Struts2の内部ないしは実装したActionクラスには反映されません。例えば先ほどのSampleActionに対し、

`name=capybara&phone=09012345678&cityname=tokyo`

このクエリ文字列でリクエストしてもcitynameがないため、citynameの内容はActionクラスへは反映されません。

ただしこれはサーブレットリクエストの内容を破棄しているわけではありませんので、HttpServletRequestから復元することは可能です。

## 受けとりたくないフィールドを設定する方法

Struts2.3.16以降ないしは2.5系では次の設定をします。

* struts.xmlにてインターセプタ宣言を追加します。
* リクエストパラメータを受けとりたくないフィールドに`@Blocked`アノテーションを付与します

…そう、なんとデフォルトの設定では、アノテーション用のインターセプタが有効でなくなりました。
だたし実装されたインターセプタが非推奨になったわけではありませんので、これを設定します。

```xml:struts.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
	<package name="sample-default" abstract="true" extends="struts-default">
		<interceptors>
			<interceptor name="annotationParams" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationParameterFilterIntereptor"/>

			<interceptor-stack name="sampleStack">
				<interceptor-ref name="exception" />
				<interceptor-ref name="servletConfig" />
				<interceptor-ref name="i18n" />
				<interceptor-ref name="prepare" />
				<interceptor-ref name="scopedModelDriven" />
				<interceptor-ref name="modelDriven" />
				<interceptor-ref name="fileUpload" />
				<interceptor-ref name="checkbox" />
				<interceptor-ref name="multiselect" />
				<interceptor-ref name="actionMappingParams" />
				<interceptor-ref name="annotationParams" />
				<interceptor-ref name="params" />
				<interceptor-ref name="conversionError" />
				<interceptor-ref name="validation">
					<param name="excludeMethods">input,back,cancel,browse</param>
				</interceptor-ref>
				<interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
			</interceptor-stack>
		</interceptors>
		<default-interceptor-ref name="sampleStack"/>
	</package>
</struts>
```

ポイントは3つです。

1. AnnotationParameterFilterIntereptorを`<interceptor>`で宣言し、別名(ここではannotationParams)をつけます。
2. `<interceptor-stack>`を作成し、annotationParamsを`<interceptor-ref name="params" />`よりも手前に差し込みます。
3. `<default-interceptor-ref>`またはActionクラスの`@InterceptorRef`に、`<interceptor-stack>`で作成したインターセプタスタック（インターセプタの組み合わせ）を設定します。

これでActionクラスのフィールドに対してブロック設定する準備が整いました。

あとはもう簡単で、

```java:SampleAction.java
public class SampleAction extends ActionSupport {

	@Action("")
	public String start() throws Exception {
		return SUCCESS;
	}

	@Getter @Setter
	@Blocked
	private String name;

	@Getter @Setter
	private String phone;
}
```

こうすると、`@blocked`で指定したフィールド name には、リクエストパラメータの内容は反映されません。phoneには特にないので反映されます。

## Action全体設定もできる

Actionクラスでデフォルト拒否/許可の設定を選べます。Apache HTTPDのDeny ALLやAllow ALLと同様の働きです。

### 全体拒否

Actionクラスに`@BlockByDefault`を付けます。これにより、全てのフィールドに対して一切リクエストパラメータの内容が反映されません。
許可するフィールドのみに`@Allowed`をつけます。

### 全体許可（デフォルト）

Actionクラスのフィールドへの格納制限がないのがデフォルトです。拒否したいフィールドに対して`@Blocked`をつけます。

## まとめ

このようにActionクラスごとにもフィールドの受信拒否/許可設定ができます。これによりActionクラスから別の役割と持つフィールドやオブジェクトが同名のリクエストパラメータによって書き換わることは回避されます。

例えば、ActionクラスからSpringのクラスを呼ぶときや、データアクセス担うDAO層のインスタンスを取得していた場合には、この機能を使うことで、リクエストパラメータからの影響は排除されます。