61.2. コンテナー要求フィルター

概要

本セクションでは、コンテナー要求フィルターを実装して登録する方法を説明します。このフィルターは、サーバー (コンテナー) 側の受信要求メッセージをインターセプトするために使用されます。コンテナー要求フィルターは、サーバー側でヘッダーを処理するために使用されることが多く、あらゆる種類の汎用要求処理 (つまり、呼び出された特定のリソースメソッドから独立している処理) に使用できます。

さらに、コンテナーリクエストフィルターは、PreMatchContainerRequest (リソース一致ステップの前) と ContainerRequest (リソース一致ステップの後) の 2 つの異なる拡張ポイントにインストールできるため、特別なケースになります。

ContainerRequestFilter インターフェイス

javax.ws.rs.container.ContainerRequestFilter インターフェイスは以下のように定義されます。

// Java
...
package javax.ws.rs.container;

import java.io.IOException;

public interface ContainerRequestFilter {
    public void filter(ContainerRequestContext requestContext) throws IOException;
}

ContainerRequestFilter インターフェイスを実装することで、サーバー側で以下のエクステンションポイントのいずれかにフィルターを作成できます。

  • PreMatchContainerRequest
  • ContainerRequest

ContainerRequestContext インターフェイス

ContainerRequestFilterfilter メソッドは、javax.ws.rs.container.ContainerRequestContext 型の引数を 1 つ受け取り、これは受信リクエストメッセージとその関連メタデータにアクセスするために使用できます。ContainerRequestContext インターフェイスは以下のように定義されます。

// Java
...
package javax.ws.rs.container;

import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;


public interface ContainerRequestContext {

    public Object getProperty(String name);

    public Collection getPropertyNames();

    public void setProperty(String name, Object object);

    public void removeProperty(String name);

    public UriInfo getUriInfo();

    public void setRequestUri(URI requestUri);

    public void setRequestUri(URI baseUri, URI requestUri);

    public Request getRequest();

    public String getMethod();

    public void setMethod(String method);

    public MultivaluedMap getHeaders();

    public String getHeaderString(String name);

    public Date getDate();

    public Locale getLanguage();

    public int getLength();

    public MediaType getMediaType();

    public List getAcceptableMediaTypes();

    public List getAcceptableLanguages();

    public Map getCookies();

    public boolean hasEntity();

    public InputStream getEntityStream();

    public void setEntityStream(InputStream input);

    public SecurityContext getSecurityContext();

    public void setSecurityContext(SecurityContext context);

    public void abortWith(Response response);
}

PreMatchContainerRequest フィルターの実装例

PreMatchContainerRequest エクステンションポイントのコンテナー要求フィルター (つまり、リソース一致の前にフィルターが実行される場合) を実装するには、ContainerRequestFilter インターフェイスを実装するクラスを定義し、クラスに @PreMatching アノテーションを付けます (PreMatchContainerRequest 拡張ポイントを選択するため)。

たとえば、以下のコードは、PreMatchContainerRequest エクステンションポイントにインストールされる単純なコンテナーリクエストフィルターの例を示しています。ここでは、優先度は 20 です。

// Java
package org.jboss.fuse.example;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;

@PreMatching
@Priority(value = 20)
@Provider
public class SamplePreMatchContainerRequestFilter implements
  ContainerRequestFilter {

  public SamplePreMatchContainerRequestFilter() {
    System.out.println("SamplePreMatchContainerRequestFilter starting up");
  }

  @Override
  public void filter(ContainerRequestContext requestContext) {
    System.out.println("SamplePreMatchContainerRequestFilter.filter() invoked");
  }
}

ContainerRequest フィルターの実装例

ContainerRequest エクステンションポイント (つまり、リソース一致の にフィルターが実行される場合) のコンテナー要求フィルターを実装するには、@PreMatching アノテーション なしContainerRequestFilter インターフェイスを実装するクラスを定義します。

たとえば、以下のコードは、ContainerRequest エクステンションポイントにインストールされる単純なコンテナーリクエストフィルターの例を示しています。ここで、優先度は 30 になります。

// Java
package org.jboss.fuse.example;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;

@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {

  public SampleContainerRequestFilter() {
    System.out.println("SampleContainerRequestFilter starting up");
  }

  @Override
  public void filter(ContainerRequestContext requestContext) {
    System.out.println("SampleContainerRequestFilter.filter() invoked");
  }
}

ResourceInfo の注入

ContainerRequest エクステンションポイント (つまりリソース一致発生 ) では、ResourceInfo クラスを注入することで、一致したリソースクラスとリソースメソッドにアクセスできます。たとえば、以下のコードは、ResourceInfo クラスを ContainerRequestFilter クラスのフィールドとしてインジェクトする方法を示しています。

// Java
package org.jboss.fuse.example;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.ext.Provider;
import javax.annotation.Priority;
import javax.ws.rs.core.Context;

@Provider
@Priority(value = 30)
public class SampleContainerRequestFilter implements ContainerRequestFilter {

  @Context
  private ResourceInfo resinfo;

  public SampleContainerRequestFilter() {
    ...
  }

  @Override
  public void filter(ContainerRequestContext requestContext) {
    String resourceClass = resinfo.getResourceClass().getName();
    String methodName    = resinfo.getResourceMethod().getName();
    System.out.println("REST invocation bound to resource class: " + resourceClass);
    System.out.println("REST invocation bound to resource method: " + methodName);
  }
}

呼び出しの中止

コンテナー要求フィルターに適した実装を作成して、サーバー側の呼び出しを中止できます。通常、認証機能や承認機能を実装する場合など、これはサーバー側でセキュリティー機能を実装するのに役立ちます。受信要求が認証に失敗した場合には、コンテナー要求フィルター内から呼び出しを中止できます。

たとえば、以下の事前照合機能は URI のクエリーパラメーターからユーザー名とパスワードを抽出し、認証メソッドを呼び出してユーザー名とパスワードの認証情報を確認します。認証に失敗すると、ContainerRequestContext オブジェクトで abortWith を呼び出すことで呼び出しが中断され、クライアントに返されるエラー応答を渡します。

// Java
package org.jboss.fuse.example;

import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;

@PreMatching
@Priority(value = 20)
@Provider
public class SampleAuthenticationRequestFilter implements
  ContainerRequestFilter {

  public SampleAuthenticationRequestFilter() {
    System.out.println("SampleAuthenticationRequestFilter starting up");
  }

  @Override
  public void filter(ContainerRequestContext requestContext) {
    ResponseBuilder responseBuilder = null;
    Response response = null;

    String userName = requestContext.getUriInfo().getQueryParameters().getFirst("UserName");
    String password = requestContext.getUriInfo().getQueryParameters().getFirst("Password");
    if (authenticate(userName, password) == false) {
      responseBuilder = Response.serverError();
      response = responseBuilder.status(Status.BAD_REQUEST).build();
      requestContext.abortWith(response);
    }
  }

  public boolean authenticate(String userName, String password) {
    // Perform authentication of 'user'
    ...
  }
}

サーバー要求フィルターのバインド

サーバー要求フィルターを バインド する (Apache CXF ランタイムにインストールする) には、以下の手順を実行します。

  1. 以下のコードフラグメントで示されるように、@Provider アノテーションをコンテナーリクエストフィルタークラスに追加します。

    // Java
    package org.jboss.fuse.example;
    
    import javax.ws.rs.container.ContainerRequestContext;
    import javax.ws.rs.container.ContainerRequestFilter;
    import javax.ws.rs.ext.Provider;
    import javax.annotation.Priority;
    
    @Provider
    @Priority(value = 30)
    public class SampleContainerRequestFilter implements ContainerRequestFilter {
      ...
    }

    コンテナーリクエストフィルター実装が Apache CXF ランタイムにロードされると、REST 実装はロードされたクラスを自動的にスキャンし、@Provider アノテーション (スキャンフェーズ) の付いたクラスを検索します。

  2. XML で JAX-RS サーバーエンドポイントを定義する場合 (例: 「JAX-RS サーバーエンドポイントの設定」)、jaxrs:providers 要素のプロバイダーリストにサーバー要求フィルターを追加します。

    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
        xmlns:cxf="http://cxf.apache.org/blueprint/core"
        ...
    >
        ...
        <jaxrs:server id="customerService" address="/customers">
          ...
          <jaxrs:providers>
            <ref bean="filterProvider" />
          </jaxrs:providers>
          <bean id="filterProvider" class="org.jboss.fuse.example.SampleContainerRequestFilter"/>
    
        </jaxrs:server>
    
    </blueprint>
    注記

    この手順は、Apache CXF の非標準要件です。厳密に言うと、JAX-RS 標準によれば、フィルターをバインドするために必要なのは @Provider アノテーションのみです。しかし実際には、標準的なアプローチはやや柔軟性がなく、大規模なプロジェクトに多くのライブラリーが含まれている場合は、プロバイダーの衝突につながる可能性があります。