50.4. 将 Exceptions 映射到响应

概述

在有些情况下,引发 WebApplicationException 异常是不现实或不可能的。例如,您可能不希望捕获所有可能的异常,然后为它们创建一个 WebApplicationException。您可能还希望使用自定义例外,以便更轻松地处理应用程序代码。

为了处理这些情况,您可以使用 JAX-RS API 来实施自定义异常提供程序,该提供程序会生成 Response 对象来发送到客户端。自定义异常供应商通过实施 ExceptionMapper<E> 接口来创建。当通过 Apache CXF 运行时注册时,都将在抛出异常时使用自定义提供程序。

如何选择异常映射程序

这两种情况下,使用异常映射程序:

  • 当抛出异常或其中一个子类时,运行时将检查相应的例外映射程序。如果处理特定异常,则会选择异常映射程序。如果抛出了特定异常的异常映射程序,则会选择异常最接近的一块程序。
  • 默认情况下,WebApplicationException 将被默认的 mapper WebApplicationExceptionper 来处理。即使注册了额外的自定义映射程序,这样可能会处理 WebApplicationException 异常(例如,自定义 RuntimeException mapper),也不会 使用自定义映射程序,并将改为使用 WebApplicationExceptionMapper

    不过,可以通过将 Message 对象上的 default.wae.mapper.least.specific 属性设为 true 来更改此行为。启用此选项后,默认的 WebApplicationExceptionMapper 被委派到最低优先级,这样便可与自定义异常映射程序来处理 WebApplicationException 异常。例如,如果启用了这个选项,可以通过注册自定义 RuntimeException mapper 来捕获 WebApplicationException 异常。请参阅 “为 WebApplicationException 注册异常映射程序”一节

如果没有为异常找到异常映射程序,则异常会嵌套在 ServletException 异常中,并传递到容器运行时。然后,容器运行时将决定如何处理异常。

实施异常映射器

exception mappers 通过实施 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);
}

exception mapper 创建的 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 对象。结果是使用者将接收 HTTP 响应,状态为 403

注册异常映射程序

在 JAX-RS 应用可以使用例外映射程序之前,例外映射程序必须使用运行时注册。例外映射程序使用应用配置文件中的 jaxrs:providers 元素在运行时注册。

jaxrs:providers 元素是 jaxrs:server 元素的子项,包含 bean 元素的列表。每个 bean 元素都定义一个例外映射程序。

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