This page last changed on Mar 27, 2006 by joncrlsn.

Grails Controllers

What is a controller?

A controller handles requests and creates or prepares the response. They can generate the response or delegate to a view. To create a controller create a class whose name ends with "Controller", thats it nothing to extend simply as that.

The first part of your controller name is mapped to a URI and each action defined within your controller maps to URI within the controller name URI.

Controllers are request-scoped.  A new instance is created for each request. 

Creating Grails Controllers

If you're lazy you can create a controller using the "create-controller" target which will prompt for the name of your controller:

grails create-controller

For example if we typed "book" the following controller would be created:

class BookController { ... }

BookController maps to the <...>/book URI. Note that grails has done no special configuration in the background, all this is doing os creating you a controller from a template.

Creating Actions

A controller can have multiple closure properties. Each of these properties maps to a URI:

class BookController {
    @Property list = {

        // do controller logic
        // create model

        return model
    };
}

This example maps to the <...>/book/list URI. If only one closure property is present the default URI for a controller maps to this property. Alternatively you can define an "index" action which is the action that handles requests when no action is specified in the URI i.e. <...>/book

Setting the default action

There are 2 ways to set the default action (ie the action that is executed if no only the controller name is included in the uri), the simplest way is to merely create an action called "index":

@Property index = {
   redirect(action:list)
}

Alternatively you can set it explicity with the "defaultAction" property:

@Property defaultAction = "list"

Accessing the request parameters, session etc

Every controller has a number of properties inject into them at runtime, these make it possible to access the request, session etc. For a full reference on these please see the Dynamic Methods Reference

class BookController {
    @Property find = {
        def findBy = params["findBy"]
        def appContext = servletContext["appContext"]
        def loggedUser = session["logged_user"]

        // do stuff
        // return model
        return model
    };
}

Using Flash Scope

Flash scope is a concept introduction by Rails and essentially is a temporary store for attributes that should be available for the next request and the request only, after which they are cleared. This is useful for setting a message directly before redirection for example:

@Property delete = {
    def b = Book.get( params['id'] )
    if(!b) {
        flash['message'] = "User not found for id ${params['id']}"
        redirect(action:list)
    }
    ... // remaining code
}

Binding Request Data to the Model

Request parameters get passed into web applications as strings, causing a typical scenario of having to convert each string value into its equivalent object representation. With Grails domain classes this is made simple by the "properties" property:

@Property save = {
  def b = new Book()
  b.properties = params
  b.save()
}

In the above example with one line of code the request parameters are auto-magically converted and set on the instance of Book. If you're using a command object or some other object that you want to bind data to you can use the "bindData" method present in controllers:

def sc = new SaveCommand()
bindData(sc, params)

Returning the Model

A model is essentially a map that the view uses when rendering. There are a couple of ways to return a model, the first way is to explicity return a map instance:

@Property show = {
    def b = Book.get( params['id'] )
    return [ book : b ]
}

If no explicit model is returned the controller's properties will be used as the model thus allowing you to write code like this:

class BookController {
    @Property List books
    @Property List authors
    @Property list = {
           books = Book.list()
           authors = Author.list()
    }
}

In the above example the "books" and "authors" properties will be available in the view. A more advanced approach is to return an instance of the Spring ModelAndView class.

Rendering a Response

Sometimes its easier (typically with Ajax applications) to render snippets of text or code to the response directly from the controller. For this the highly flexible "render" method can be used:

render "Hello World!"

The above code writes the text "Hello World!" to the response, other examples include:

// write some markup
render {
   for(b in books) {
      div(id:b.id, b.title)
   }
}
// render a specific view
render(view:'show')
// render a template for each item in a collection
render(template:'book_template', collection:Book.list())
// render some text with encoding and content type
render(text:"<xml>some xml</xml>",contentType:"text/xml",encoding:"UTF-8")

Action Redirection & Chaining

Actions can be redirected using the "redirect" method present in all controllers:

class OverviewController {
    @Property login = {}

    @Property find = {
        if(!session["logged_user"])
           redirect(action:login)
        .....
    };
}

The redirect method expects either another closure within the same controller class or a string URI for calling another controller:

redirect(action:login)
// or
redirect(action:"/another/action")

Parameters can be optionally passed from one action to the next using the params argument of the method:

redirect(action:"/another/action", params:["myparam":"myvalue"])

These params are made available through the "params" dynamic property that also accesses request parameters. If a parameter is specified with the same name as a request parameter the request parameter is overridden and the controller parameter used.

Actions can also be chained. Chaining allows the model to be retained from one action to the next. For example calling the "firstInChain" action:

class ChainController {
    @Property firstInChain = {
        chain(action:secondInChain,model:["step1":new Object()])
    }
    @Property secondInChain = {
        chain(action:thirdInChain,model:["step2":new Object()])
    };
    @Property thirdInChain= {
        return ["step3":new Object()])
    };
}

Results in the model:

["step1":object1, "step2":object2, "step3":object3]

The model can be accessed in subsequent controller actions in the chain via the "chainModel" map. This dynamic property only exists in actions following the call to "chain":

class ChainController {

    @Property nextInChain = {
        def model = chainModel["myModel"]
        .....
    };
}

Like the "redirect" method you can also pass parameters to the chain method:

chain(action:"/another/action", model:["step1":object1], params:["myparam":"param1"])

Action Interceptors

Introduction

Often it is useful to intercept processing based on either request, session or application state. This can be achieved via action interceptors. There are currently 2 types of interceptors: before and after.

Before Interception

The 'before' interceptor intercepts processing before the action is executed. If it returns 'false' then the following action will not be executed. The interceptor can be defined for all actions as follows:

@Property beforeInterceptor = {
       println 'Tracing action ${actionUri}'
}

The above will be executed before all actions and does not interfere with processing. A common use case is however for authentication:

@Property beforeInterceptor = [action:this.&auth,except:'login']
// defined as a regular method so its private
def auth() {
     if(!session.user) {
            redirect(action:'login')
            return false
     }
}
@Property login = {
     // display login page
}

The above code defines a method called 'auth'. A method is used so that it is not exposed as an action to the outside world (i.e. it is private). The 'beforeInterceptor' then defines an interceptor that is used on all actions 'except' the login action and is told to execute the 'auth' method. The 'auth' method is referenced using Groovy's method pointer syntax, within the method itself it detects whether there is a user in the session otherwise it redirects to the login action and returns false, instruction the intercepted action not to be processed.

After Interception

To define an interceptor that is executed after an action use the 'afterInterceptor' property:

@Property afterInterceptor = { model ->
       println 'Tracing action ${actionUri}'
}

The after interceptor takes the resulting model as an argument and can hence perform post manipulation of the model or response.

Interception Conditions

Rails users will be familiar with the authentication example and how the 'except' condition was used when executing the interceptor (interceptors are called 'filters' in Rails, this terminology conflicts with the servlet filter terminology in Java land):

@Property beforeInterceptor = [action:this.&auth,except:'login']

This executes the interceptor for all actions except the specified action. A list of actions can also be defined as follows:

@Property beforeInterceptor = [action:this.&auth,except:['login','register']]

The other supported condition is 'only', this executes the interceptor for only the specified actions:

@Property beforeInterceptor = [action:this.&auth,only:['secure']]

Injecting services into controllers

Controllers may use services to delegate requests to business logic. To have these resources injected add corresponding properties to the controller.

@Property CountryService countryService
Document generated by Confluence on Mar 29, 2006 08:46