使用异常发送特定响应或至少特定HTTP状态代码

时间:2014-09-25 10:36:30

标签: exception grails dry http-status-codes

在Django中,有一些异常被设计为被框架截获并转换为特定的HTTP响应代码,例如404 Not Found和403 Forbidden。

这对于请求验证特别有用,因为它允许您将常见验证逻辑分解为实用程序函数并清理控制器操作。

每当实用程序函数决定必须使用特定的HTTP错误代码中止当前请求时,他们都可以通过抛出相关的异常而不在控制器操作中使用任何支持代码来实现返回声明或try / catch的形式。

例如,给定一个嵌套REST资源树:

static mappings = {
    "/authors" (resources: "author") {
        "/sagas" (resources: "saga") {
            "/books" (resources: "book") {
        }
    }
}

然后Book资源的URL模式为/authors/$authorId/sagas/$sagaId/books/$id,这意味着BookController中的任何show(),delete()或update()操作都具有此签名,并且必须包含一些样板验证逻辑:

def actionName(int authorId, int sagaId, Book book) {

    // -- common validation logic ----------
    // fetch parent objects
    def author = Author.get(authorId)
    def saga = Saga.get(sagaId)
    // check that they exists
    if (author == null || saga == null || book == null) {
        return render(status: NOT_FOUND)
    }
    // check consistency
    if (book.author != author || book.saga != saga || saga.author != author) {
        return render(status: BAD_REQUEST)
    }
    // -- end of commond code --------------

    ...
}

Grails将这种方法分解为一种常用方法的方法是什么,而当出现异常情况时,仍然允许它终止请求处理

我认为最好的方法是NotFoundException,ForbiddenException,BadRequestException等,或者可能是接受HTTP状态代码的一般异常。 Grails有什么类似的吗?如果没有,添加它的最佳位置在哪里?过滤器?


编辑:我现在看到标准方法是添加一个具有匹配URL模式的错误控制器,例如:

"500" (controller: "error")

这个问题是Grails仍会记录所有异常的完整堆栈跟踪,包括那些不是编程错误的异常。这会使日志文件发送各种无用的回溯。

有解决方案吗?

1 个答案:

答案 0 :(得分:2)

您在控制器的beforeInterceptor闭包中捕获异常。我通过检查抛出的异常然后相应地采取行动解决了同样的问题。例如:

class BaseController {

/**
 * Define DRA exception handlers. This prevents the default Grails
 * behavior of returning an HTTP 500 error for every exception.
 *
 * Instead the exceptions are intercepted and modified according to
 * the exception that was thrown. These exceptions are not logged
 * whereas application exceptions are.
 */
def beforeInterceptor = {

    request.exceptionHandler = { exception ->

        def cause = exception.cause
        def exceptionBody = [:]

        if(cause.class == BadRequestException) {
            response.setStatus(HttpStatus.BAD_REQUEST.value()) // HTTP 400 BAD REQUEST
            exceptionBody.httpStatus = HttpStatus.BAD_REQUEST.value()
            exceptionBody.error = cause.message
        }

        // render the exception body, the status code is set above.
        render exceptionBody as JSON

        return true

    }

}
}

为了使其工作,您将不得不创建一个ErrorController或处理和呈现所有服务器错误的东西。例如:

class ErrorController {

def serverError() {
    def handler = request.exceptionHandler
    if(handler) {
        request.exceptionHandler = null
        if(handler.call(request.exception)) {
            return
        }
    }
}

我测试过它确实有效。我从正在处理的正在运行的项目中复制了代码。您可以在beforeInterceptor中构建if语句,以捕获您希望的任何类型的Exception。