在服务中引发异常时在何处加载grails消息

在服务中引发异常时在何处加载grails消息,grails,Grails,如果grails服务抛出异常,UI消息元素的分离应该在哪里?消息应该由服务加载并通过异常传递给控制器,还是控制器应该根据抛出的异常类型加载消息?这假设消息将有一些需要填写的参数值 这里有一个例外: class CustomException extends RuntimeException { String message } class MyService { void doSomething() { ... if (somethingBad)

如果grails服务抛出异常,UI消息元素的分离应该在哪里?消息应该由服务加载并通过异常传递给控制器,还是控制器应该根据抛出的异常类型加载消息?这假设消息将有一些需要填写的参数值

这里有一个例外:

class CustomException extends RuntimeException {
    String message
}
class MyService {
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(value)
        }
        ...
    }
 }
 class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
            render view:'some_gsp'
        }
        ...
    }
 }
class MyService {
    def messageSource
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
        }
        ...
    }
}
class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = e.message
            render view:'some_gsp'
        }
        ...
    }
}
捕获异常后从控制器加载消息源:

class CustomException extends RuntimeException {
    String message
}
class MyService {
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(value)
        }
        ...
    }
 }
 class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
            render view:'some_gsp'
        }
        ...
    }
 }
class MyService {
    def messageSource
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
        }
        ...
    }
}
class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = e.message
            render view:'some_gsp'
        }
        ...
    }
}
从控制器从异常中提取消息字符串的服务中的消息源加载错误:

class CustomException extends RuntimeException {
    String message
}
class MyService {
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(value)
        }
        ...
    }
 }
 class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
            render view:'some_gsp'
        }
        ...
    }
 }
class MyService {
    def messageSource
    void doSomething() {
        ...
        if (somethingBad) {
            String value = 'Mary Smith'
            throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
        }
        ...
    }
}
class MyController {
    def myService

    void processRequest() {
        try {
            myService.doSomething()
        }
        catch (CustomException e) {
            flash.error = e.message
            render view:'some_gsp'
        }
        ...
    }
}

坦率地说,这两个地方都不需要翻译

关注点分离
控制器应该只关心HTTP方法及其委托。
服务应该负责事务和底层业务逻辑

声明性错误处理
对于
2.0.*
及以上版本,Grails为您提供了处理错误的最佳选择。你猜怎么着

所有与异常相关的代码都会转移到一个单独的控制器(内部),在那里它们会得到正确的处理,从而保持您的业务控制器和服务干净,并从锅炉板代码中抽象出来

对于Grails
2.3.*
,控制器本身中有一个,但大多数锅炉板(try-catch)都是从控制器实现中抽象出来的

结论
如果您使用的是
v2.0.*
及更高版本,那么您的控制器看起来像:

class MyController {
    def myService

    def processRequest() {
        myService.doSomething()
        ...
    }
 }

//URL Mapping
static mappings = {
   "500"(controller: "errors", action: "customException",
         exception: CustomException)
}

//Error Controller
class ErrorsController {
    def customException() {
        def exception = request.exception
        // perform desired processing to handle the exception
    }
}
class MyController {
    def myService

    def processRequest() {
        myService.doSomething()
        ...
    }

    def handleCustomException(CustomException e) {
        //Move this translation to src/groovy/utility if feasible
        flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
        render view:'some_gsp'
    }
 }
如果需要,您可以将错误处理逻辑移动到单独的插件,以处理各种错误/异常和不愉快路径。把这件事分开是很优雅的

如果您使用的是
v2.3.*
,则控制器的外观如下:

class MyController {
    def myService

    def processRequest() {
        myService.doSomething()
        ...
    }
 }

//URL Mapping
static mappings = {
   "500"(controller: "errors", action: "customException",
         exception: CustomException)
}

//Error Controller
class ErrorsController {
    def customException() {
        def exception = request.exception
        // perform desired processing to handle the exception
    }
}
class MyController {
    def myService

    def processRequest() {
        myService.doSomething()
        ...
    }

    def handleCustomException(CustomException e) {
        //Move this translation to src/groovy/utility if feasible
        flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
        render view:'some_gsp'
    }
 }
在这种情况下,也不需要从服务中进行处理,您只需要经历该异常


我想如果您环顾四周并对使用此模式感兴趣,您也会从各种来源获得更多的输入

视情况而定。这在控制器中更常见,因为这是基于UI的职责。此外,使用服务中的异常来控制事务提交/回滚会对性能造成影响(您真的需要它在每次抛出该异常或任何其他异常时填充整个堆栈吗?记住Grails每次都要填充一个巨大的堆栈…)。例外情况应为例外情况,而非预期情况。