Grails: предотвратить дальнейшее выполнение кода после перенаправления или пересылки в базовом классе контроллера - PullRequest
3 голосов
/ 13 июля 2011

У меня следующая структура контроллера:

abstract class AbstractController {
  // ...
}

abstract class AbstractDiagramController extends AbstractController {
  // ...
}

class PopulationController extends AbstractDiagramController {
  // ...
}

Большинство действий контроллера вызывают различные методы абстрактных базовых классов. Если теперь одному из этих базовых методов необходимо отправить перенаправление (или переадресацию) клиенту, Grails все равно не помешает приложению обработать оставшийся код действия действия контроллера.

С моей точки зрения, это нежелательное поведение, потому что базовые методы выполняют какую-то проверку (например, проверку параметра, пользователя, сеанса и т. Д.), И контроллер предполагает, что проверка прошла успешно (оттуда возникают последующие ошибки).

Как я могу предотвратить это недостаточное поведение?

С уважением, Christopher

PS: я уже нашел этот вопрос , но ответы не удовлетворили мои потребности, потому что ни один из них не имеет дело с базовым контроллером:

PPS: я использую Grails в версии 1.3.7

EDIT

Это реакция на комментарии Виктора Сергиенко. Здесь я приведу более детальный пример кода моей проблемы.

// The very base controller
abstract class AbstractController { 

    // The interceptor gets called before any action of inheritors get processed
    def beforeInterceptor = [action:this.&initialize]

    // Method validates various stuff
    protected initialize() {
        if( someThingIsWrong() ) {
            // This redirect should stop any other code of inheritors
            redirect( controller: "foo", action: "bar" )
            return false
        }
    } 
}

// The second base controller
abstract class AbstractDiagramController extends AbstractController { 

    // This object must get initialized. If not (eg any errors or exceptions occured) 
    // all inheritors actions are not allowed to do anything
    MyObject myGreatObject = null

    // Overriden, because of additional individual diagram validation
    @Override
    protected initialize() {
        // Do parents stuff first
        super.auth()

        // If parent failed, this code should not get executed anymore.
        // Yes, here I could check if parent returned false and return false as well before
        // continuing the next validation. Anyway I have to do this because grails, comprehendibly,
        // will throw an exception if two redirects were executed in a row.
        // (With this I just want to visualize the behaviour I'd expect)
        if( someThingElseIsWrong() ) { 
            redirect( controller: "hello", action: "world")
            return false
        }

        // After validation I can initialize the object
        myGreatObject = new MyObject()
    }
}


// A controller implementation
class MyDiagramController extends AbstractDiagramController {

    // Overriden because of inidividual validation
    @Override 
    protected initialize() {

        // First do parent stuff
        boolean succeeded = super.auth()

        // Again, annoying double check
        if( !succeeded ) {
            return false
        }
    }

    def myAction = { 
        myGreatObject.SpinAroundAndBeHappy()
    } 
}

Похоже, что было хорошей идеей сократить сценарий использования до минимума строк кода. Похоже, что предложения Виктора (canContinue или hasErrors) могут как-то решить эти неприятные обстоятельства, даже если это своего рода обходной путь.

Но почему-то мне не нравятся эти двойные проверки. Я все еще борюсь с тем фактом, что все слоев над абстрактным базовым контроллером должны реагировать на недействительные проверки, которые уже произошли (и также должны управляться базовыми контроллерами самостоятельно). С моей точки зрения, эти проверки не должны быть делом реализации контроллеров.

PS: Надеюсь, в коде не было серьезных ошибок.

Ответы [ 2 ]

4 голосов
/ 13 июля 2011

В качестве обходного пути вы можете вернуть boolean canContinue из действия предка, либо сгенерировать исключение, либо установить instance.hasErrors() в вашем случае.

EDIT : факт initialize() называется до действие выглядит как контроль доступа или другое полное переопределение семантики действия (до выполнения какой-либо части действия). Пожалуйста, скажите, если мое предположение неверно.

Когда мы сделали настраиваемый безопасный доступ к различным действиям, мы аннотировали действия с собственными тегами, такими как @SecuredDoodah (см. @ Secured ), и добавили Filter, который полностью перекрывает действие (для нам, Filter отвечает 403, но это не обязательно).

Filter может быть лучше, чем beforeInterceptor. Если вам нужно передать какое-либо состояние из Filter, например, myGreatObject в примере, вы можете добавить Сервис в Фильтр и сохранить его в Сервисе.

Я уверен, что есть лучшие способы, чем моя идея, но это должно работать прозрачно для контроллеров.

2 голосов
/ 13 июля 2011

Вы ограничены тем фактом, что это Java / Groovy, и нет способа для вызова метода немедленно инициировать выход из метода (или Closure). Я видел, что другой фреймворк фреймворка и когда вы вызываете render, redirect и т. Д., Он генерирует исключение, которое перехватывает базовый класс фреймворка. Это действует как Гото, которого на самом деле не существует.

Хотя это дорогостоящий подход - заполнение всех этих кадров стека излишне расточительно, поскольку это не исключительный случай, и стек всегда будет игнорироваться.

К сожалению, вам нужен такой подход, как у Виктора, где вы используете логические возвращаемые значения.

...