Fork me on GitHub

LD.

Music, software, life… and stuff.

[ Twitter ] [ GitHub ] [ Linked In ]

Using Grails tags where you can’t use Grails tags

Sometimes in a Grails app, you need to call a tag where you can’t call a tag. This is usually indicative of a bad design in your application so do use the following with caution, but should you absolutely need to… the following is how you can.

In this example, we are going to add the ability to call tags to a filter to workaround GRAILS-6814. We want to call the createLink tag.

Note that GRAILS-6814 has been resolved for Grails 1.4, so this workaround for redirects to a different domain only applies to Grails 1.3 and earlier

import org.codehaus.groovy.grails.web.pages.GroovyPage
import org.springframework.web.context.request.RequestContextHolder

class HttpsOnlyFilter {

    def gspTagLibraryLookup // need this autowired

    def filters = {
        redirectToHttps(controller: "*", action: "*") {
            before = {
                if (!request.secure && isSecurePage(controllerName, actionName)) {
                    def link = invokeTag("createLink", [
                        controller: controllerName, 
                        action: actionName, 
                        params: params, 
                        base: "https://secure.domain.com"
                    ]).toString()
                    response.status = 301
                    response.setHeader("Location", link)
                    false
                }
            }
        }
    }

    def invokeTag(name, attrs = [:], body = null) {
        def namespace
        def tagName

        if (name.contains(":")) {
            def split = name.split(":", 2)
            namespace = split[0]
            tagName = split[1]
        } else {
            namespace = GroovyPage.DEFAULT_NAMESPACE
            tagName = name
        }

        GroovyPage.captureTagOutput(gspTagLibraryLookup, namespace, tagName, attrs, body, RequestContextHolder.currentRequestAttributes())
    }

}

The invokeTag method takes; the name of a tag, a map of attributes, and an option body closure and returns a StreamCharBuffer (so if you want the content as a String you new to toString() the return). Inside the body closure, you can write to the out stream just like in a tag implementation.

If the tag name given to invokeTag() doesn’t include a namespace, the default g namespace is used. To invoke a tag in a different namespace, you can use…

invokeTag("someNamespace:someTag", [param1: 1])

Remember, requiring tags outside of the view layer can be indicative of a design problem so do think long and hard about whether you really need to before using the above. Also, the above will only work in a request environment so if you try to use the above on a background thread were a fake request environment hasn’t been established it will blow up due to RequestContextHolder.currentRequestAttributes() returning null. If you need to setup a fake request environment, you can use GrailsWebUtil.bindMockWebRequest() to do so.

Posted: May 11th, 2011 @ 11:40 am

Tags: #software  #grails  

Comments

Archive · RSS · Theme by Autumn