(Quick Reference)

7.6.2 Filter Types - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari

Version: 2.3.8

7.6.2 Filter Types

Within the body of the filter you can then define one or several of the following interceptor types for the filter:
  • before - Executed before the action. Return false to indicate that the response has been handled that that all future filters and the action should not execute
  • after - Executed after an action. Takes a first argument as the view model to allow modification of the model before rendering the view
  • afterView - Executed after view rendering. Takes an Exception as an argument which will be non-null if an exception occurs during processing. Note: this Closure is called before the layout is applied.

For example to fulfill the common simplistic authentication use case you could define a filter as follows:

class SecurityFilters {
   def filters = {
       loginCheck(controller: '*', action: '*') {
           before = {
              if (!session.user && !actionName.equals('login')) {
                  redirect(action: 'login')
                  return false
               }
           }
       }
   }
}

Here the loginCheck filter uses a before interceptor to execute a block of code that checks if a user is in the session and if not redirects to the login action. Note how returning false ensure that the action itself is not executed.

Here's a more involved example that demonstrates all three filter types:

import java.util.concurrent.atomic.AtomicLong

class LoggingFilters {

private static final AtomicLong REQUEST_NUMBER_COUNTER = new AtomicLong() private static final String START_TIME_ATTRIBUTE = 'Controller__START_TIME__' private static final String REQUEST_NUMBER_ATTRIBUTE = 'Controller__REQUEST_NUMBER__'

def filters = {

logFilter(controller: '*', action: '*') {

before = { if (!log.debugEnabled) return true

long start = System.currentTimeMillis() long currentRequestNumber = REQUEST_NUMBER_COUNTER.incrementAndGet()

request[START_TIME_ATTRIBUTE] = start request[REQUEST_NUMBER_ATTRIBUTE] = currentRequestNumber

log.debug "preHandle request #$currentRequestNumber : " + "'$request.servletPath'/'$request.forwardURI', " + "from $request.remoteHost ($request.remoteAddr) " + " at ${new Date()}, Ajax: $request.xhr, controller: $controllerName, " + "action: $actionName, params: ${new TreeMap(params)}"

return true }

after = { Map model ->

if (!log.debugEnabled) return true

long start = request[START_TIME_ATTRIBUTE] long end = System.currentTimeMillis() long requestNumber = request[REQUEST_NUMBER_ATTRIBUTE]

def msg = "postHandle request #$requestNumber: end ${new Date()}, " + "controller total time ${end - start}ms" if (log.traceEnabled) { log.trace msg + "; model: $model" } else { log.debug msg } }

afterView = { Exception e ->

if (!log.debugEnabled) return true

long start = request[START_TIME_ATTRIBUTE] long end = System.currentTimeMillis() long requestNumber = request[REQUEST_NUMBER_ATTRIBUTE]

def msg = "afterCompletion request #$requestNumber: " + "end ${new Date()}, total time ${end - start}ms" if (e) { log.debug "$msg \n\texception: $e.message", e } else { log.debug msg } } } } }

In this logging example we just log various request information, but note that the model map in the after filter is mutable. If you need to add or remove items from the model map you can do that in the after filter.