This is specifically about a grails 1.3.7 application, but hopefully the answers will work for newer versions as well. The code below is a simplified version of what is needed. accountService is being injected. The below snippet does what it's supposed to do but clearly is repeated code. This is in a UserFilter class located in grails-app/conf
How do I extract common logic out of my filters and maintain the capability to redirect and check the session? I've tried extracting out a method into the filter class, passing in the session and flash, but the redirect was still giving me issues.
def filters = {
// ... other filters ...
adminAllCheck(controller: 'administration', action: '*') {
before = {
if(!session.isAdmin) {
if(accountService.isAdmin()) {
session.isAdmin = true
} else {
flash.message = 'Non admin'
redirect(controller: 'home', action: 'index')
return false
}
}
true
}
}
userListCheck(controller: 'user', action: 'list') {
before = {
if(!session.isAdmin) {
if(accountService.isAdmin()) {
session.isAdmin = true
} else {
flash.message = 'Non admin'
redirect(controller: 'home', action: 'index')
return false
}
}
true
}
}
}
One way to create a helper method is to create it outside of the filters
closure, and pass the instance in. It doesn't work to pass in this
because that's not the closure but the UserFilters
instance. Instead pass in the closure's delegate
which is where the render
and redirect
methods are added, where the attributes like params
and controllerName
are:
class UserFilters {
def filters = {
// ... other filters ...
adminAllCheck(controller: 'administration', action: '*') {
before = {
doAdminCheck(delegate)
}
}
userListCheck(controller: 'user', action: 'list') {
before = {
doAdminCheck(delegate)
}
}
}
private boolean doAdminCheck(filters) {
if (!filters.session.isAdmin) {
if (accountService.isAdmin()) {
filters.session.isAdmin = true
}
else {
filters.flash.message = 'Non admin'
filters.redirect(controller: 'home', action: 'index')
return false
}
}
true
}
}
You can also use the |
character in the controller
and action
arguments to do common work across controllers. It wouldn't work here directly since you use *
for the admin controller and only apply to the list
action in the user controller, but you can do an explicit controller/action name check for that:
adminCheck(controller: 'administration|user', action: '*') {
if (controllerName == 'user' && actionName != 'list') {
return true
}
// common logic here
}
You could also move the logic to a service and dependency-inject that back in. It's usually not a good idea to mix tiers like that and have HTTP logic in a service, but that would keep the logic in one place. You could use the same trick with the delegate, or just pass in the session/request/response/etc. as needed.