How to Reduce Code in Grails Controllers to Minimum

Vladimír Oraný
Stories by Agorapulse
2 min readSep 5, 2019

The controllers in any web framework should reduce their code to the bare minimum which only bridges web input and output into platform-independent business code. The problem arises when you have a code with different outcomes which you would like to represent with different HTTP status codes.

Typical examples are security and parameter checks like the following:

  • Is the endpoint authorized properly?
  • Are some of the parameters missing?
  • Does the referred item exist?

Following CommentController shows some of the problems. Usually, there is even more code between the method's points of return.

class CommentController {

def securityService
def commentService

def
updateComment(String id, String newText) {
if (!securityService.authorized(request)) {
respond status: HttpStatus.FORBIDDEN
return
}

if (!securityService.hasRole(request, 'EDITOR')) {
respond status: HttpStatus.UNAUTHORIZED
return
}

if (!id || !newText) {
respond status: HttpStatus.BAD_REQUEST
return
}

Comment comment = commentService.findById(id)

if (!comment) {
respond status: HttpStatus.NOT_FOUND
return
}

comment = commentService.updateComment(comment, newText)

render(comment as JSON)
}

}

To reduce the code we need to adopt multiple approaches. Typically security and similar cross-cutting concerns are best handled with aspect-oriented programming style which means using interceptors in the Grails environment.

class SecurityInterceptor {

def securityService

SecurityInterceptor() {
// just for demo, usually you would use annotations
// or some name conventions
match(controller: 'comment', action: 'updateComment')
// more actions
}

boolean before() {
if (!securityService.authorized(request)) {
respond status: HttpStatus.FORBIDDEN
return false
}

if (!securityService.hasRole(request, 'EDITOR')) {
respond status: HttpStatus.UNAUTHORIZED
return false
}
return true
}

boolean after() {
return true
}

}

See Interceptors for further reference.

For exceptional states, there are exceptions in Java. You can move the code into the service and throw exceptions at the points where would you normally return from the controller action:

class CommentService {

def commentService

Comment updateComment(String id, String newText) {
// moved to interceptor

if (!id || !newText) {
throw new IllegalArgumentException("Details...")
}

Comment comment = findById(id)

if (!comment) {
throw new NoSuchElementException("Details...")
}

return updateComment(comment, newText)
}
}

The controller will handle the exceptions in an exception handling actions:

class CommentController {

def commentService

def
updateComment(String id, String newText) {
render(commentService.updateComment(id, newText) as JSON)
}

def handleWrongParmeters(IllegalArgumentException e) {
respond status: HttpStatus.BAD_REQUEST
}

def handleMissingComment(NoSuchElementException e) {
respond status: HttpStatus.NOT_FOUND
}

}

See Declarative Controller Exception Handling for further reference.

--

--