如何在grails REST控制器中捕获异常

时间:2015-01-16 12:02:05

标签: grails exception-handling

如果使用控制器来实现其他API,则需要处理抛出的任何异常并返回通用或特定格式良好的REST响应。

我们无法使用全局错误URL映射方法,因为该应用程序具有许多具有不同响应要求的API和接口,而且我们也不知道哪种类型的Grails'将抛出HTTP错误代码(例如,不知道它是否为400,422,500等)。此外,如果我们使用错误页面映射,我们将无法将相关数据放入JSON响应中。

E.g。这将生成GrailsRuntimeException:

class SomeController {
    def payload = request.JSON
    def someMethod() {
        BigDecimal x = new BigDecimal(payload.notExists)
    }

问题是,似乎无法捕获任何错误。

E.g。这种做法都没有:

def handleRuntimeException(RuntimeException e) {
      render("some JSON error message")
}

也不是这种方法:

try {
    :
}
catch (GrailsRuntimeException e) {
        render("some JSON error message")
}

作品 - 它永远不会发现错误。

尝试了GroovyRuntimeException,Exception,MissingMethodException,Throwable等。

我们能想到的唯一解决方案是不在控制器中做任何工作,在服务中做所有事情,显然我们可以发现错误。

这种方法:

static mappings = {
    "500"(controller: "error")
}

不希望我们需要几个原因:

  1. 我们在不同的控制器中有几种不同的API,需要不同的响应格式。
  2. 我们还有UI控制器,它们需要显示堆栈跟踪等的默认错误系统。
  3. 我们希望处理发生异常的控制器中的错误,因此我们可以清理,或者至少可以记录或返回只有控制器知道的统计信息。
  4. 决定了唯一的方法是将所有代码移动到服务中,除了传递请求之外,不在控制器中执行任何操作,并渲染结果字符串。即,所有参数处理,尤其是数字转换,都在服务中完成。

3 个答案:

答案 0 :(得分:1)

具有讽刺意味的是,您认为解决方案不是最理想的解决方案正是您应该经常做的事情。这不是PHP - 不要将逻辑放在控制器(或GSP)中。

默认情况下,服务是事务性的,因此它们是放置写入数据库的代码的好地方,因为它应该始终在事务中发生。无论是否具有交易性,它们对于业务逻辑都非常出色,您可以使用@Transactional对单个方法进行注释,以将方法划分为在事务中运行的方法和不在事务中的方法。 ;或者将服务分成一些完全交易的服务和一些不是。

如果您将所有与HTTP相关的代码保存在控制器中,从params执行数据绑定,并调用帮助程序类(服务,域类,标记库等),您可以很好地分离关注点,如果服务层对paramsHttpServletRequest,HTTP会话等没有任何了解,那么它可以在其他Grails应用程序中轻松重用,甚至可以在非Grails应用程序中重复使用。它们也更容易测试,因为没有那么多相互关联的代码需要被模拟,否则会使测试更友好。

使用这种方法,控制器基本上变成哑路由器,接受请求,调用帮助程序来完成实际工作,委派页面呈现或响应写入,或重定向或转发。

答案 1 :(得分:0)

我很晚才回答这个问题,但我认为这可能有助于人们绊脚石并寻找解决方案。

以下是我的一篇解释Custom exception handling in grails and error responses for RESTfull services

的博客

希望这可以帮助某人

答案 2 :(得分:0)

我正在使用grails 3.2.4并且没有问题,因为您已在此处解释过。我已将所有业务逻辑移到服务类中,并通过父控制器特征捕获异常。在这里,我在另一个ParentExceptionController特征中处理此异常,该特征由从服务类发生此类异常的类实现。示例:

UserService {
 boolean create(Map params) {
     throw new InvalidParameterException('some message')
  }

UserController implements ParentExceptionController {
 UserService userService
    userService.create(params.userDetails)
  }

trait ParentExceptionController {
 Object handleInvalidParameterException(InvalidParameterExceoption exception) {
  log.error 'log message'
  respond([message: exception.message])
 }

first and second both are working in my case.