Grails 3: есть ли способ извлечь повторяющийся шаблонный код в этом примере API?

Нам нужно реализовать большое количество вызовов JSON API в веб-приложении Grails 3.3.

К сожалению, сборка каждого метода — это обработка ошибок, и в настоящее время мы дублируем этот код в каждом методе, что создает головную боль при обслуживании (если мы улучшим обработку ошибок, нам придется обновить более 70 методов). Есть ли лучший способ с меньшим количеством повторяющихся кодов ошибок? Какой-то шаблон, который может извлечь код проверки ошибок в одно место?

К вашему сведению, мы используем очень простой и эффективный упорядочиватель объектов для рендеринга json нужным нам образом. К сожалению, паттерн render(){...} игнорирует упорядочивающие объекты, поэтому в данном случае нам нужно использовать JSONBuilder.

Правильный логин выдаст что-то вроде этого:

{"result":"0","user":{"username":"bob","userId":"123"},"accounts":[{"balance":"0.00","currencyIso":"USD","id":1}]}

Ошибка будет выглядеть так:

{"result":"9999","message":"exception in xxx"}

У нас есть длинный список кодов результатов (около 70), поэтому клиент точно знает, в чем была ошибка (мы не используем для этого коды состояния HTTP, поскольку они вообще не отображаются)

class LoginCommand implements grails.validation.Validateable {
    String username
    String password
    static constraints = {
        username    nullable:false, blank: false, size: 3..32
        password    nullable:false, blank: false, size: 3..32
    }

}

class UserApiController {
 def login(LoginCommand cmd) {
    try {
        if (cmd.hasErrors()) {
            log.error("invalid parameters")
            render(status: 400, contentType: 'application/json') {
                    result 10
                    message "errors in parameters"
            return
            }
        } else {
            User user = User.findByUsernameAndPassword(cmd.username, cmd.password)
            def userAccounts = Account.findAllByUser(user)

            if (user == null) {
                render(status: 404, contentType: 'application/json') {
                    result 100
                    message "could not find user with that username and pass"
                }
            } else {

                def builder = new JSONBuilder()
                def json = builder.build {
                    result= "0"
                    user= {
                      username=user.username
                      userId=user.id
                    }
                    accounts=playerAccounts
                }
                render(status: 200, contentType: 'application/json', text: json)                
            } // else all good
        } // else params ok

    } catch (Exception e) {
        log.error("Caught Exception in UserApiController.login()", e)
        render(status: 500, contentType: 'application/json') {
                result 9999
                message e.toString()
        }
    }
}

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


person John Little    schedule 16.07.2017    source источник


Ответы (1)


Пара замечаний, которые могут вам помочь:

  • Если вы хотите, чтобы все функции входа в систему использовались несколькими приложениями, рассмотрите возможность создания подключаемого модуля Grails. Подключаемый модуль — это специфичный для Grails проект JAR, в котором могут размещаться повторно используемые артефакты, такие как контроллеры и службы.

http://docs.grails.org/latest/guide/plugins.html

  • Вы должны настроить глобальный обработчик ошибок, а не делать общий try...catch() для всего вашего метода контроллера. Видеть:

Обработка исключений в контроллерах Grails

  • Если вам нужны представления JSON, вы можете установить их в существующее приложение, даже если приложение не было создано с использованием профиля REST. Представления JSON очень полезны, если вы хотите настроить свой вывод, но вы также хотите разделить задачи между контроллером и представлением.

http://views.grails.org/latest/#_installation

  • Для этого конкретного случая использования вы также можете взглянуть на плагины Spring Security Grails.

https://plugins.grails.org/q/spring-security

  • Чтобы очистить код вашего контроллера, рассмотрите возможность извлечения вашей бизнес-логики в сервисный метод. Вы можете либо создать стандартизированный командный объект для своего вывода, либо использовать настраиваемый подтип RuntimeException (MyLoginFailureException), чтобы сигнализировать о неудачном входе в систему, передавая причину и код результата.
person RMorrisey    schedule 28.07.2017