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