web-dev-qa-db-ja.com

Grailsコントローラーでの例外処理

GrailsでUrlMappingsとErrorControllerを使用して一般的な例外処理を行う方法を知っているので、例外がコントローラーをエスケープした場合、ユーザーは一般的なエラーページに送信され、例外がログに記録されます。また、try/catchブロックを使用して特定の例外を処理し、それらからの回復を試みる方法も知っています。

しかし、ほとんどのコントローラーでは、例外が発生した場合に、もう少し具体的なエラーメッセージをユーザーに表示したいだけです。したがって、作成アクションでは、アイテムが作成されなかったことをユーザーに伝えたいと思います。または、インポートアクションで、インポートが失敗したことをユーザーに通知します。現在、コントローラーは次のようになっています。

class ThingController {
  def create = {
    try {
      // The real controller code, which quickly hands it off to a service
    } catch (Exception e) {
      handleException(e, "There was an error while attempting to create the Thing")
    }
  }

  def delete = {
    try {
      // The real controller code, which quickly hands it off to a service
    } catch (Exception e) {
      handleException(e, "There was an error while attempting to delete the Thing")
    }
  }

  private void handleException(Exception e, String message) {
    flash.message = message
    String eMessage = ExceptionUtils.getRootCauseMessage(e)
    log.error message(code: "sic.log.error.ExceptionOccurred", args: ["${eMessage}", "${e}"])
    redirect(action:index)
  }
}

Catchブロックは、例外のタイプや内容に基づいて何もしないことに注意してください。コントローラーに基づいて、もう少し説明的なエラーメッセージが表示されるだけです。 「実際の」コントローラーコードは通常6〜10行なので、エラーメッセージを変更するためだけに4行のコードを追加するのは過剰に思われます。さらに、CodeNarcの "CatchException"ルールに不満があるため、これを行うにはより良い方法が必要であるという私の意見が強くなります。他のGrailsアプリケーションにも同様の要件があると思います。例外が発生したアクションに基づいて異なるエラーメッセージを指定するイディオマティック方法は何ですか?

この問題を解決する特定の方法の経験からの回答に興味があります。実際には、ソリューションを実際に確認できるコードベースにリンクしています。

19
Tom Panning

Grailsには、コントローラーの例外を一般的に処理するメカニズムがあります。専用のエラーコントローラ内でこれを行うことができます。通常のコントローラーは、try/catchを使用する必要はありません。

コントローラ:

class ThingController {
    def create() {
        def id = params.id as Long

        if (id == null) {
            throw new MissingPropertyException("thingId")
        }
        // The real controller code, which mostly parses things out and hands it
        // off to a service.
        // Service methods can throws exception

    }
}

UrlMappingsに500エラーの処理を追加します。

class UrlMappings {

    static mappings = {
        // Exception handling in ErrorController
        "500"(controller: "error")
    }
}

ErrorController:

class ErrorController {

    def index() {

        def exception = request.exception.cause
        def message = ExceptionMapper.mapException(exception)
        def status = message.status

        response.status = status
            render(view: "/error", model: [status: status, exception: exception])
    }
}

このアプローチを使用してRESTおよび非REST例外を処理できます。また、 Declarative Exception Handling プラグインがありますが、

更新

エラーコントローラで特定のエラーメッセージを取得できます。コントローラーで新しいRuntimeException(「Thingを削除しようとしたときにエラーが発生しました」)をスローすると、エラーコントローラーでrequest.exception.cause.messageに次のメッセージが表示されます:「Thingを削除しようとしてエラーが発生しました」。

28

参照 どこからエラー500(Grails)がスローされたかを知る方法

コントローラーの注釈に基づいてカスタムエラーページを作成し、いくつかのコントローラーに共通の例外処理手順を提供します。

class ErrorsController {
def index() {
    def initialController = request.exception?.className
    if (initialController) {
        def controller = grailsApplication.getArtefact("Controller", initialController).getReferenceInstance()
        // do some rendering based on the annotations
        render "Controller: ${initialController}, annotations ${controller.getClass().getDeclaredAnnotations()}"
        return
    }
    render 'no initial controller'
}
1
Igor