在本系列文章的第一部分里,我们讨论了异常发生时,该返回给 REST API 调用者的异常表示(格式)的佳实践。
  在本文(第二部分)中,我们将展示如何在使用 Spring MVC 编写的 REST API 中产生那些异常表述信息。
  Spring 异常处理
  Spring MVC 有两个主要方式来处理在调用 MVC 控制器(译注:Controller,下文统一为控制器)时抛出的异常:HandlerExceptionResolver 和 @ExceptionHandler 注解。
  HandlerExceptionResolvers 对于用一种统一的方法来处理异常来说非常的理想,框架组件为你带来性能的提升。@ExceptionHandler 注解在你想手动展示一些基于业务逻辑的特定异常时则是个很好的选择。
  它们的机制都非常强大:我们的普通代码可以以其含有的所有面向对象的益处,通过类型安全的方式抛出预期的异常来明确指示为何失败。然后我们可以让分解器和/或处理器来执行从异常到 HTTP 的必要翻译工作。你当然知道关注点分离(separation of concerns)的好处。
  对于我们的 REST 异常处理而言,我们想要通过佳实践的方式将所有的异常以一致的方式呈现。由于这个一致性(要求),采用HandlerExceptionResolver是非常合适的。 我们将呈献一个 REST 友好的 HandlerExceptionResolver 实现,它可在任何 REST API 中使用。
  RestExceptionHandler
  虽然大多数现有的 HandlerExceptionResolver 实现适用于基于 Spring MVC 的用户界面,但是对于 REST 异常处理它们并不是十分理想。为了实现第一部分中异常表示的格式,我们创建了自己的 HandlerExceptionResolver 实现,称为 RestExceptionHandler
  提示:本文讨论的代码,是一个 Spring MVC REST 应用,可通过 Stormpath 在 Github) 上的 spring-mvc-rest-exhandler 源获取。
  当你的 Spring MVC REST 控制器抛出异常时,RestExceptionHandler 将会:
  将异常转换为一个 RestError 实例。RestError 的实现包含第一部分中讨论过的所有 REST 异常表示属性。
  基于构建的 RestError 在 HTTP 响应中设置合适的 HTTP 响应状态码
  将 RestError 表示提供给 HTTP 响应体。在默认情况下,我们提供一个 JSON 消息体,正如第一部分中的例子所示。
  马上我们将会涉及 RestExceptionHandler 的底层工作细节。现在让我们通过一个示例项目看看 RestExceptionHandler 的实践,这样你清楚它到底是如何运作的。
  示例应用
  你可以检出 Github 上的 spring-mvc-rest-exhandler 项目,它包含了主要的 RestExceptionHandler 实现和一个附加的示例应用。
  在检出项目之后,你可以通过 Maven 来构建:
  (在项目根目录执行):
  $ mvn clean install
  该命令会构建 RestExceptionHandler 库(一个 .jar 文件)以及一个单独的示例 web 应用(一个 .war 文件)。你可以在示例目录中运行该应用:
  $ cd example
  $ mvn jetty:run
  以上命令将在本机启动 Jetty web 服务器,端口为 8080。
  端点
  应用启动后,你可以访问以下两个 REST 端点:
  http://localhost:8080/v1/users/jsmith
  http://localhost:8080/v1/users/djones
  但这些只是运行正常的示例资源。本文我们真正在意的是:一个异常表示该是什么样子?
  /v1/ 路径下的其他任何资源都会展示一个 HTTP 404 Not Found 的异常,并且包含在我们期望的 Rest 异常表示体中。试着访问下面这个 URL:
  http://localhost:8080/v1/users/doesNotExist
  你将会看到我们漂亮的 REST 异常表示!干净漂亮……
  但这是如何运作的?由于所访问资源不存在,异常被抛出,然后某个东西将这个异常转换成了漂亮的 JSON 异常消息。让我们看看这是怎么发生的。
  MVC 控制器
  示例应用中有两个控制器 —— UserController 和 DefaultController。
  UserController
  UserController 的源码表明它是一个很简单的 Spring MVC 控制器。它模拟了一个成功查找两个示例用户的功能并在用户无法被找到时抛出一个自定义(应用指定)的 UnknownResourceException。
  我们期望 UnknownResourceException 通过我们的 RestExceptionHandler 配置(我们很快会涉及到),自动转换 HTTP 404 (Not Found)为一个漂亮的异常表示。
  DefaultController
  DefaultController 源码表明它是一个基础设施组件的作用。通过 @RequestMapping, 我们能看到在 Spring 无法找到一个更明确的控制器时,Spring会调用这个默认的控制器。
  DefaultController 十分简单:它在任何场景下始终抛出一个 UnknownResourceException。这对 REST 应用来说是有益的,因为在没有其他端点可以为一个请求服务时,我们总想展示一个相关的异常消息。
  我们看到了 MVC 控制器如期的抛出漂亮的类型安全的异常。现在我们来看看RestExceptionHandler如何将这些异常转换 HTTP 异常消息体以及你可以如何定制它的行为。
  RestExceptionHandler 的 Spring 配置
  这里有一个基础的 RestExceptionHandler Spring bean 定义:
<bean id="restExceptionResolver" class="com.stormpath.spring.web.servlet.handler.RestExceptionHandler">
<property name="order" value="100"></property>
<property name="errorResolver">
<bean class="com.stormpath.spring.web.servlet.handler.DefaultRestErrorResolver">
<property name="localeResolver" ref="localeResolver"></property>
<property name="defaultMoreInfoUrl" value="mailto:support@mycompany.com"></property>
<property name="exceptionMappingDefinitions">
<map>
<!– 404 –>
<entry key="com.stormpath.blog.spring.mvc.rest.exhandler.UnknownResourceException" value="404, _exmsg"></entry>
<!– 500 (catch all): –>
<entry key="Throwable" value="500, error.internal"></entry>
</map>
</property>
</bean>
</property>
</bean>
  如果你仔细看,你会发现这个关于 RestExceptionHandler 配置的详细示例和它并没有太多直接关系。这里有两个属性:order 和 errorResolver。(我们也可以配置其他属性,例如 HttpMessageConverter 和其他的,但那已超出本文的讨论范围)。
  Order
  order 属性在你想要配置其他的HandlerExceptionResolvers而需链式使用 RestExceptionHandler 的功能时比较有用。
  例如,这使你能配置一个 AnnotationMethodHandlerExceptionResolver bean (例如 order 0),这样你可以在自定义异常处理策略中使用 @ExceptionHandler 注解,然后让 RestExceptionHandler (例如 order 100)作为所有其他异常的默认处理器。本示例应用的 rest-spring.xml 文件阐释了这个技术。
  然而,显而易见的是 errorResolver 属性是这个配置的重点。接下来我们将涉及到。
  RestErrorResolver
  RestExceptionHandler 将运行时异常到 RestError 分解逻辑委托给 RestErrorResolver 实例。RestErrorResolver 知道如何返回一个表示 REST异常表示的RestError 实例。
  在上面的 Spring XML 配置示例中,RestErrorResolver 的实现为 DefaultRestErrorResolver。DefaultRestErrorResolver依赖于一组映射定义来解析异常到对应的RestError实例。
  针对每个映射定义项:
  映射键可以是异常的全限定名中出现的任何字符(或这该异常的父类)。
  映射值是RestError 定义,一个以逗号分隔的字符串。字符串通过启发式解析来决定如何构建一个RestError 实例。
  当 DefaultRestErrorResolver 在运行时遇到一个异常是,它会根据映射定义检测该异常,如果找到匹配的映射,则返回对应的RestError 实例。
  对于 Spring 的特定异常和其他熟知的异常 DefaultRestErrorResolver 已经有一些默认映射,但是这些定义可以在你配置 bean 的时候进行覆盖。