Menu Close

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);
    ...