原文はこちら。
https://community.oracle.com/docs/DOC-1003506
フィルタ(Filter)はJAX-RSフレームワークが提供する重要な機能の一つであり、RESTful Webサービスを開発する際には様々なコンテキストで利用されます。
フィルタを使ってリクエスト・エンティティやレスポンス・エンティティ、ヘッダ、その他のパラメータを変更します。この記事では、様々な種類のフィルタとその使い方を見ていきます。全てのコードサンプルはJerseyフレームワークをベースにしています。
[注意]
この記事のサンプルコードはGitHubからご利用いただけます。
Listing 2の例では、API Keyの検証をリソースメソッドの呼出しで強制しています。期待されるAPI Keyがこのリクエストヘッダにない場合、これ以後の呼出し実行は中止され、このレイヤでレスポンスを生成します。
デフォルトでは、適切なリクエスト・実行メソッドが見つかった後にコンテナ・リクエスト・フィルタ実装を実行します。なお、必要とされるリクエストメソッドを発見したり選択したりすることを制御することはできません。
この挙動を実現するため、コンテナ・リクエスト・フィルタのための
Listing 4に示すクライアント・リクエスト・フィルタの目的は、
既に述べたように、クライアント・フィルタ(リクエスト、レスポンスとも)をListing 5に示すように
図2はフィルタ実行パスのワークフロー(ステップ)全体を示しています。
以下の出力は、上記の全てのフィルタ実装が存在する場合の実行順序を示しています。ソースコードを書くにし、プロジェクトをローカルで実行して以下の出力を確認してください。図2で示した順序に出力が一致するはずです。
https://community.oracle.com/docs/DOC-1003506
フィルタ(Filter)はJAX-RSフレームワークが提供する重要な機能の一つであり、RESTful Webサービスを開発する際には様々なコンテキストで利用されます。
フィルタを使ってリクエスト・エンティティやレスポンス・エンティティ、ヘッダ、その他のパラメータを変更します。この記事では、様々な種類のフィルタとその使い方を見ていきます。全てのコードサンプルはJerseyフレームワークをベースにしています。
[注意]
この記事のサンプルコードはGitHubからご利用いただけます。
RESTskol
https://github.com/cloudskol/restskol
Types of Filters
フィルタはサーバ(コンテナ)フィルタ、もう一つはクライアントフィルタという2種類の主要なカテゴリに分類されます。これらの2個のカテゴリはさらにリクエスト・フィルタやレスポンス・フィルタに分類されます(図1)。Figure 1. Types of filters |
Container Filters
以下ではコンテナ・フィルタと利用可能なアノテーションをご紹介します。Container Response Filters
レスポンス・ヘッダを変更したり、リソースメソッドを実行した後にパラメータを変更したりしたい場合に、コンテナ・レスポンス・フィルタを利用できます。利用するためには、ContainerResponseFilterインターフェースの実装を提供する必要があります(Listing 1)。Listing 1. コンテナ・レスポンス・フィルタの例これをプロバイダとしてリソース構成に登録する必要があります。上記の例では、public class RestSkolResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext,
ContainerResponseContext containerResponseContext) throws IOException {
containerResponseContext.getHeaders().add("X-Powered-By", "RESTSkol");
}
}
X-Powered-B
yと呼ばれるパラメータをレスポンスに注入しました(このサンプルはJersey Webサイトにインスパイアされました)。このように、レスポンスでよく使われるヘッダ値を簡単に注入することができます。Container Request Filters
コンテナ・リクエスト・フィルタはリソースメソッドを実行する前に検証を強制する場合に有用です。そのため、コンテナ・リクエスト・フィルタはリソースメソッドの盾として振る舞います。任意の検証を強制することができ、その検証に失敗した場合、その時点でリソース実行を中止することができます。Listing 2の例では、API Keyの検証をリソースメソッドの呼出しで強制しています。期待されるAPI Keyがこのリクエストヘッダにない場合、これ以後の呼出し実行は中止され、このレイヤでレスポンスを生成します。
Listing 2. Example container request filterpublic class APIKeyCheckRequestFilter implements
ContainerRequestFilter { private static final String API_KEY = "X-API-KEY";
@Override
public void filter(ContainerRequestContext containerRequestContext) throws
IOException { final String apiKey =
containerRequestContext.getHeaders().getFirst(API_KEY); System.out.println("API KEY: " + apiKey);
if (apiKey == null || apiKey.isEmpty()) { containerRequestContext
.abortWith(
Response
.status(Response.Status.UNAUTHORIZED)
.entity("Please provide a valid API Key")
.build());
}
}
}
@PreMatching
Annotation
デフォルトでは、適切なリクエスト・実行メソッドが見つかった後にコンテナ・リクエスト・フィルタ実装を実行します。なお、必要とされるリクエストメソッドを発見したり選択したりすることを制御することはできません。この挙動を実現するため、コンテナ・リクエスト・フィルタのための
@PreMatching
アノテーションがあります。@PreMatching
を使ってコンテナ・リクエスト・フィルタに注釈を付けた場合、リクエストメソッドに影響を及ぼすことができます。Listing 3の実装例では、 /books/v1
へのリクエストを the /books/v2
リソースに委任しています。これはリソース実装を廃止したいときに有用かもしれません。Listing 3. Example of using the@PreMatching
annotation@PreMatching
public class PreMatchingFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext containerRequestContext) throws
IOException { final URI absolutePath =
containerRequestContext.getUriInfo().getAbsolutePath(); String path =
absolutePath.getPath();
System.out.println("Path: " + path);
if (path.contains("/books/v1")) {
path = path.replace("/books/v1", "/books/v2");
}
System.out.println("After replaced Path: " + path);
try {
containerRequestContext.setRequestUri(new URI(path));
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
Client Filters
クライアント・フィルタではクライアント・リクエスト・フィルタとクライアント・レスポンス・フィルタの2種類が用意されています。これらのフィルタはクライアントAPI層にのみ適用します。つまり、Jersey(JAX-RS)クライアントAPIを使ってRESTエンドポイントを呼び出す場合、これらのフィルタをClient
オブジェクトの一部として追加することができます。Client Request Filters
このフィルタはフィルタのワークフロー開始点です。クライアント・リクエスト・フィルタがClient
オブジェクトに登録されている場合、リクエストがサーバに送信される前にワークフローを開始します。そのため、クライアント層自身で何らかの検証を実施したい場合に、このフィルタを使うことができます。Listing 4に示すクライアント・リクエスト・フィルタの目的は、
PATCH
メソッドを呼び出すときに検証する、というものです。検証を実施してサポートされないメソッドを使えないようにすることができます。この呼出しで検証に失敗した場合、リクエストを中止することができ、ワークフローが終了します。検証が成功した場合はサーバーレベルにリクエストを渡します。Listing 4. Example client request filterpublic class RestSkolClientRequestFilter implements ClientRequestFilter {
private static final Logger logger =
LogManager.getLogger(RestSkolClientRequestFilter.class);
@Override
public void filter(ClientRequestContext clientRequestContext) throws IOException {
logger.info("Client request called");
final String methodName = clientRequestContext.getMethod(); if
(methodName.equals("PATCH")) {
clientRequestContext.abortWith(
Response.status(Response.Status.METHOD_NOT_ALLOWED)
.entity("HTTP Patch is NOT supported")
.build());
}
}
}
Client Response Filters
クライアント・レスポンス・フィルタはワークフローの最終ステップです。実行がサーバレベルで完了した後、サーバ層が成功のレスポンスもしくは失敗のレスポンスを生成し、クライアント層にこのレスポンスを渡します。クライアント・レスポンス・フィルタはレスポンスをサーバから受け取った際に実行されるもので、クライアント・レスポンス・フィルタのレベルで検証したり、レスポンスにさらにエンティティデータを注入したりすることができます。その後レスポンスを呼出し元に渡します。How to Use Client Filters
既に述べたように、クライアント・フィルタ(リクエスト、レスポンスとも)をListing 5に示すように
Client
オブジェクトに登録する必要があります。Listing 5. Example of registering client filtersその後、これらのクライアント・フィルタをメソッド呼び出し時に実行します。final Client client = ClientBuilder.newBuilder()
.register(RestSkolClientRequestFilter.class)
.register(RestSkolClientResponseFilter.class)
.build();
Filter Execution Workflow and Output
図2はフィルタ実行パスのワークフロー(ステップ)全体を示しています。
Figure 2. Steps in the filter execution path |
以下の出力は、上記の全てのフィルタ実装が存在する場合の実行順序を示しています。ソースコードを書くにし、プロジェクトをローカルで実行して以下の出力を確認してください。図2で示した順序に出力が一致するはずです。
[INFO ] 2016-04-12 23:29:38.212 [main] RestSkolClientRequestFilter - Client request called
[INFO ] 2016-04-12 23:29:38.509 [http-bio-8080-exec-1] PreMatchingFilter - Pre matching container request filter
[INFO ] 2016-04-12 23:29:38.519 [http-bio-8080-exec-1] APIKeyCheckRequestFilter - Container request filter
[INFO ] 2016-04-12 23:29:38.530 [http-bio-8080-exec-1] RestSkolResponseFilter - Container response filter
[INFO ] 2016-04-12 23:29:38.935 [main] RestSkolClientResponseFilter - Client response filter
Conclusion
サンプルのソースコードは以下にあります。RESTskol詳細を知りたい方はご連絡ください(訳注:当然ながら著者にお願いします)。できる限りお答えしたいと考えています。
https://github.com/cloudskol/restskol
See Also
フィルタに関する詳細情報は、Jerseyのドキュメントをご覧ください。Filters and Interceptors
https://jersey.java.net/documentation/latest/filters-and-interceptors.html
About the Author
ThamizharasuはインドのChennaiにすむJava開発者で、特にプログラミング、Java EEに夢中です。また、ThamizharasuはJSRアクティビティ(JSONB)に参加し、現在Madras Java User Groupのリーダーを務めています。自身のブログやTwitterでプログラミングやフレームワークを啓蒙しています。Cloudskol - Digital School for Learning
http://cloudskol.com/