50.4. 例外の応答へのマッピング

概要

WebApplicationException 例外の出力が現実的でない場合や、不可能な場合があります。たとえば、可能な例外をすべてキャッチし、そのような例外に対して WebApplicationException を作成する必要がない場合があります。また、アプリケーションコードの操作を容易にするカスタムの例外を使用することもできます。

このようなケースを処理するために、JAX-RS API では、クライアントに送信する Response オブジェクトを生成するカスタム例外プロバイダーを実装できます。カスタム例外プロバイダーは、ExceptionMapper<E> インターフェイスを実装して作成されます。Apache CXF ランタイムで登録されると、型 E の例外が出力されるたびにカスタムプロバイダーが使用されます。

例外マッパーの選択方法

例外マッパーは 2 つのケースで使用されます。

  • 例外またはそのサブクラスの 1 つが出力されると、ランタイムは適切な例外マッパーをチェックします。特定の例外出力を処理する場合は、例外マッパーが選択されます。出力された特定の例外の例外マッパーがない場合には、例外の中で最も近いスーパークラスの例外マッパーが選択されます。
  • デフォルトでは、WebApplicationException はデフォルトマッパー WebApplicationExceptionMapper によって処理されます。WebApplicationException 例外を処理する可能性がある追加のカスタムマッパーが登録されていても (カスタム RuntimeException マッパーなど)、カスタムマッパーは 使用されず、代わりに WebApplicationExceptionMapper が使用されます。

    ただし、この動作は Message オブジェクトの default.wae.mapper.least.specific プロパティーを true に設定して変更できます。このオプションを有効にすると、カスタム例外マッパーを使用して WebApplicationException 例外を処理できるようにするため、デフォルトの WebApplicationExceptionMapper の優先順位は最低になります。たとえば、このオプションが有効な場合に、カスタム RuntimeException マッパーを登録することで WebApplicationException 例外をキャッチできます。「WebApplicationException の例外マッパーの登録」を参照してください。

例外に、例外マッパーがない場合には、この例外は ServletException 例外でラップされ、コンテナーランタイムに渡されます。その後、コンテナーランタイムは例外の処理方法を決定します。

例外マッパーの実装

例外マッパーは、javax.ws.rs.ext.ExceptionMapper<E> インターフェイスを実装して作成されます。例50.5「例外マッパーのインターフェイス」で示されているように、インターフェイスには単一のメソッド toResponse() があります。これは元の例外をパラメーターとして取り、Response オブジェクトを返します。

例50.5 例外マッパーのインターフェイス

public interface ExceptionMapper<E extends java.lang.Throwable>
{
  public Response toResponse(E exception);
}

例外マッパーによって作成された Response オブジェクトは、他の Response オブジェクトのようにランタイムによって処理されます。コンシューマーへの結果となる応答には、Response オブジェクトにカプセル化されたステータス、ヘッダー、およびエンティティーボディーが含まれます。

例外マッパー実装はランタイムによってプロバイダーとみなされます。そのため、@Provider アノテーションを付ける必要があります。

例外マッパーによる Response オブジェクトの構築中に例外が発生すると、ランタイムは 500 Server Error のステータスで応答をコンシューマーに送信します。

例50.6「例外の応答へのマッピング」は、Spring AccessDeniedException 例外を遮断し、 403 Forbidden ステータスと空のエンティティーボディーで応答を生成する例外マッパーを示しています。

例50.6 例外の応答へのマッピング

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

import org.springframework.security.AccessDeniedException;

@Provider
public class SecurityExceptionMapper implements ExceptionMapper<AccessDeniedException>
{

  public Response toResponse(AccessDeniedException exception)
  {
    return Response.status(Response.Status.FORBIDDEN).build();
  }

}

ランタイムは AccessDeniedException 例外をキャッチし、空のエンティティーボディーと 403 のステータスで Response オブジェクトを作成します。ランタイムは、通常のレスポンスの場合と同様に Response オブジェクトを処理します。その結果、コンシューマーはステータスが 403 の HTTP 応答を受信します。

例外マッパーの登録

JAX-RS アプリケーションが例外マッパーを使用する前に、例外マッパーをランタイムに登録する必要があります。例外マッパーは、アプリケーションの設定ファイルの jaxrs:providers 要素を使用してランタイムで登録されます。

jaxrs:providers 要素は jaxrs:server 要素の子で、bean 要素のリストが含まれます。各 bean 要素は 1 つの例外マッパーを定義します。

例50.7「ランタイムへの例外マッパーの登録」は、カスタム例外マッパー SecurityExceptionMapper を使用するように設定された JAX-RS サーバーを示しています。

例50.7 ランタイムへの例外マッパーの登録

<beans ...>
  <jaxrs:server id="customerService" address="/">
    ...
    <jaxrs:providers>
      <bean id="securityException" class="com.bar.providers.SecurityExceptionMapper"/>
    </jaxrs:providers>
  </jaxrs:server>
</beans>

WebApplicationException の例外マッパーの登録

WebApplicationException 例外の例外マッパーを登録することは、この例外型はデフォルトの WebApplicationExceptionMapper によって自動的に処理されるため、特殊なケースです。通常、WebApplicationException を処理する予定のカスタムマッパーを登録する場合でも、引き続きデフォルトの WebApplicationExceptionMapper によって処理されます。このデフォルトの動作を変更するには、default.wae.mapper.least.specific プロパティーを true に設定する必要があります。

たとえば、以下の XML コードは JAX-RS エンドポイントで default.wae.mapper.least.specific プロパティーを有効にする方法を示しています。

<beans ...>
  <jaxrs:server id="customerService" address="/">
    ...
    <jaxrs:providers>
      <bean id="securityException" class="com.bar.providers.SecurityExceptionMapper"/>
    </jaxrs:providers>
    <jaxrs:properties>
      <entry key="default.wae.mapper.least.specific" value="true"/>
    </jaxrs:properties>
  </jaxrs:server>
</beans>

以下の例のように、インターセプターで default.wae.mapper.least.specific プロパティーを設定することもできます。

// Java
public void handleMessage(Message message)
{
    m.put("default.wae.mapper.least.specific", true);
    ...