8.3 URL Mappings 4.0.10

(Quick Reference) # 8.3 URL Mappings Version: 4.0.10 ## Table of Contents ## 8.3 URL Mappings Throughout the documentation so far the convention used for URLs has been the default of `/controller/action/id`. However, this convention is not hard wired into Grails and is in fact controlled by a URL Mappings class located at `grails-app/controllers/mypackage/UrlMappings.groovy`. The `UrlMappings` class contains a single property called `mappings` that has been assigned a block of code:
    package mypackage

    class UrlMappings {
        static mappings = {
        }
    }
## 8.3.1 Mapping to Controllers and Actions To create a simple mapping simply use a relative URL as the method name and specify named parameters for the controller and action to map to:
    "/product"(controller: "product", action: "list")
In this case we’ve mapped the URL `/product` to the `list` action of the `ProductController`. Omit the action definition to map to the default action of the controller:
    "/product"(controller: "product")
An alternative syntax is to assign the controller and action to use within a block passed to the method:
    "/product" {
        controller = "product"
        action = "list"
    }
Which syntax you use is largely dependent on personal preference. If you have mappings that all fall under a particular path you can group mappings with the `group` method:
    group "/product", {
        "/apple"(controller:"product", id:"apple")
        "/htc"(controller:"product", id:"htc")
    }
You can also create nested `group` url mappings:
    group "/store", {
        group "/product", {
            "/$id"(controller:"product")
        }
    }
To rewrite one URI onto another explicit URI (rather than a controller/action pair) do something like this:
    "/hello"(uri: "/hello.dispatch")
Rewriting specific URIs is often useful when integrating with other frameworks. ## 8.3.2 Mapping to REST resources Since Grails 2.3, it possible to create RESTful URL mappings that map onto controllers by convention. The syntax to do so is as follows:
    "/books"(resources:'book')
You define a base URI and the name of the controller to map to using the `resources` parameter. The above mapping will result in the following URLs:
HTTP Method | URI | Grails Action
--- | --- | ---
GET | /books | index
GET | /books/create | create
POST | /books | save
GET | /books/${id} | show
GET | /books/${id}/edit | edit
PUT | /books/${id} | update
DELETE | /books/${id} | delete
If you are not sure which mapping will be generated for your case just run the command `url-mappings-report` in your grails console. It will give you a really neat report for all the url mappings. If you wish to include or exclude any of the generated URL mappings you can do so with the `includes` or `excludes` parameter, which accepts the name of the Grails action to include or exclude:
    "/books"(resources:'book', excludes:['delete', 'update'])

    or

    "/books"(resources:'book', includes:['index', 'show'])
#### Explicit REST Mappings As of Grails 3.1, if you prefer not to rely on a `resources` mapping to define your mappings then you can prefix any URL mapping with the HTTP method name (in lower case) to indicate the HTTP method it applies to. The following URL mapping:
    "/books"(resources:'book')
Is equivalent to:
    get "/books"(controller:"book", action:"index")
    get "/books/create"(controller:"book", action:"create")
    post "/books"(controller:"book", action:"save")
    get "/books/$id"(controller:"book", action:"show")
    get "/books/$id/edit"(controller:"book", action:"edit")
    put "/books/$id"(controller:"book", action:"update")
    delete "/books/$id"(controller:"book", action:"delete")
Notice how the HTTP method name is prefixed prior to each URL mapping definition. #### Single resources A single resource is a resource for which there is only one (possibly per user) in the system. You can create a single resource using the `single` parameter (as opposed to `resources`):
    "/book"(single:'book')
This results in the following URL mappings:
HTTP Method | URI | Grails Action
--- | --- | ---
GET | /book/create | create
POST | /book | save
GET | /book | show
GET | /book/edit | edit
PUT | /book | update
DELETE | /book | delete
The main difference is that the id is not included in the URL mapping. #### Nested Resources You can nest resource mappings to generate child resources. For example:
    "/books"(resources:'book') {
      "/authors"(resources:"author")
    }
The above will result in the following URL mappings:
HTTP Method | URL | Grails Action
--- | --- | ---
GET | /books/${bookId}/authors | index
GET | /books/${bookId}/authors/create | create
POST | /books/${bookId}/authors | save
GET | /books/${bookId}/authors/${id} | show
GET | /books/${bookId}/authors/edit/${id} | edit
PUT | /books/${bookId}/authors/${id} | update
DELETE | /books/${bookId}/authors/${id} | delete
You can also nest regular URL mappings within a resource mapping:
    "/books"(resources: "book") {
        "/publisher"(controller:"publisher")
    }
This will result in the following URL being available:
HTTP Method | URL | Grails Action
--- | --- | ---
GET | /books/${bookId}/publisher | index
To map a URI directly below a resource then use a collection block:
    "/books"(resources: "book") {
        collection {
            "/publisher"(controller:"publisher")
        }
    }
This will result in the following URL being available (without the ID):
HTTP Method | URL | Grails Action
--- | --- | ---
GET | /books/publisher | index
#### Linking to RESTful Mappings You can link to any URL mapping created with the `g:link` tag provided by Grails simply by referencing the controller and action to link to:
    <g:link controller="book" action="index">My Link</g:link>
As a convenience you can also pass a domain instance to the `resource` attribute of the `link` tag:
    <g:link resource="${book}">My Link</g:link>
This will automatically produce the correct link (in this case "/books/1" for an id of "1"). The case of nested resources is a little different as they typically required two identifiers (the id of the resource and the one it is nested within). For example given the nested resources:
    "/books"(resources:'book') {
      "/authors"(resources:"author")
    }
If you wished to link to the `show` action of the `author` controller, you would write:
    // Results in /books/1/authors/2
    <g:link controller="author" action="show" method="GET" params="[bookId:1]" id="2">The Author</g:link>
However, to make this more concise there is a `resource` attribute to the link tag which can be used instead:
    // Results in /books/1/authors/2
    <g:link resource="book/author" action="show" bookId="1" id="2">My Link</g:link>
The resource attribute accepts a path to the resource separated by a slash (in this case "book/author"). The attributes of the tag can be used to specify the necessary `bookId` parameter. ## 8.3.3 Redirects In URL Mappings Since Grails 2.3, it is possible to define URL mappings which specify a redirect. When a URL mapping specifies a redirect, any time that mapping matches an incoming request, a redirect is initiated with information provided by the mapping. When a URL mapping specifies a redirect the mapping must either supply a String representing a URI to redirect to or must provide a Map representing the target of the redirect. That Map is structured just like the Map that may be passed as an argument to the `redirect` method in a controller.
    "/viewBooks"(redirect: [uri: '/books/list'])
    "/viewAuthors"(redirect: [controller: 'author', action: 'list'])
    "/viewPublishers"(redirect: [controller: 'publisher', action: 'list', permanent: true])
Request parameters that were part of the original request will not be included in the redirect by default. To include them it is necessary to add the parameter `keepParamsWhenRedirect: true`.
    "/viewBooks"(redirect: [uri: '/books/list', keepParamsWhenRedirect: true])
    "/viewAuthors"(redirect: [controller: 'author', action: 'list', keepParamsWhenRedirect: true])
    "/viewPublishers"(redirect: [controller: 'publisher', action: 'list', permanent: true, keepParamsWhenRedirect: true])
## 8.3.4 Embedded Variables #### Simple Variables The previous section demonstrated how to map simple URLs with concrete "tokens". In URL mapping speak tokens are the sequence of characters between each slash, '/'. A concrete token is one which is well defined such as as `/product`. However, in many circumstances you don’t know what the value of a particular token will be until runtime. In this case you can use variable placeholders within the URL for example:
    static mappings = {
      "/product/$id"(controller: "product")
    }
In this case by embedding a $id variable as the second token Grails will automatically map the second token into a parameter (available via the params object) called `id`. For example given the URL `/product/MacBook`, the following code will render "MacBook" to the response:
    class ProductController {
         def index() { render params.id }
    }
You can of course construct more complex examples of mappings. For example the traditional blog URL format could be mapped as follows:
    static mappings = {
       "/$blog/$year/$month/$day/$id"(controller: "blog", action: "show")
    }
The above mapping would let you do things like:
    /graemerocher/2007/01/10/my_funky_blog_entry
The individual tokens in the URL would again be mapped into the params object with values available for `year`, `month`, `day`, `id` and so on. #### Dynamic Controller and Action Names Variables can also be used to dynamically construct the controller and action name. In fact the default Grails URL mappings use this technique:
    static mappings = {
        "/$controller/$action?/$id?"()
    }
Here the name of the controller, action and id are implicitly obtained from the variables `controller`, `action` and `id` embedded within the URL. You can also resolve the controller name and action name to execute dynamically using a closure:
    static mappings = {
        "/$controller" {
            action = { params.goHere }
        }
    }
#### Optional Variables Another characteristic of the default mapping is the ability to append a ? at the end of a variable to make it an optional token. In a further example this technique could be applied to the blog URL mapping to have more flexible linking:
    static mappings = {
        "/$blog/$year?/$month?/$day?/$id?"(controller:"blog", action:"show")
    }
With this mapping all of these URLs would match with only the relevant parameters being populated in the params object:
    /graemerocher/2007/01/10/my_funky_blog_entry
    /graemerocher/2007/01/10
    /graemerocher/2007/01
    /graemerocher/2007
    /graemerocher
#### Optional File Extensions If you wish to capture the extension of a particular path, then a special case mapping exists:
    "/$controller/$action?/$id?(.$format)?"()
By adding the `(.$format)?` mapping you can access the file extension using the `response.format` property in a controller:
    def index() {
        render "extension is ${response.format}"
    }
#### Arbitrary Variables You can also pass arbitrary parameters from the URL mapping into the controller by just setting them in the block passed to the mapping:
    "/holiday/win" {
         id = "Marrakech"
         year = 2007
    }
This variables will be available within the params object passed to the controller. #### Dynamically Resolved Variables The hard coded arbitrary variables are useful, but sometimes you need to calculate the name of the variable based on runtime factors. This is also possible by assigning a block to the variable name:
    "/holiday/win" {
         id = { params.id }
         isEligible = { session.user != null } // must be logged in
    }
In the above case the code within the blocks is resolved when the URL is actually matched and hence can be used in combination with all sorts of logic. ## 8.3.5 Mapping to Views You can resolve a URL to a view without a controller or action involved. For example to map the root URL `/` to a GSP at the location `grails-app/views/index.gsp` you could use:
    static mappings = {
        "/"(view: "/index")  // map the root URL
    }
Alternatively if you need a view that is specific to a given controller you could use:
    static mappings = {
       "/help"(controller: "site", view: "help") // to a view for a controller
    }
## 8.3.6 Mapping to Response Codes Grails also lets you map HTTP response codes to controllers, actions or views. Just use a method name that matches the response code you are interested in:
    static mappings = {
       "403"(controller: "errors", action: "forbidden")
       "404"(controller: "errors", action: "notFound")
       "500"(controller: "errors", action: "serverError")
    }
Or you can specify custom error pages:
    static mappings = {
       "403"(view: "/errors/forbidden")
       "404"(view: "/errors/notFound")
       "500"(view: "/errors/serverError")
    }
#### Declarative Error Handling In addition you can configure handlers for individual exceptions:
    static mappings = {
       "403"(view: "/errors/forbidden")
       "404"(view: "/errors/notFound")
       "500"(controller: "errors", action: "illegalArgument",
             exception: IllegalArgumentException)
       "500"(controller: "errors", action: "nullPointer",
             exception: NullPointerException)
       "500"(controller: "errors", action: "customException",
             exception: MyException)
       "500"(view: "/errors/serverError")
    }
With this configuration, an `IllegalArgumentException` will be handled by the `illegalArgument` action in `ErrorsController`, a `NullPointerException` will be handled by the `nullPointer` action, and a `MyException` will be handled by the `customException` action. Other exceptions will be handled by the catch-all rule and use the `/errors/serverError` view. You can access the exception from your custom error handing view or controller action using the request’s `exception` attribute like so:
    class ErrorController {
        def handleError() {
            def exception = request.exception
            // perform desired processing to handle the exception
        }
    }
| If your error-handling controller action throws an exception as well, you’ll end up with a `StackOverflowException`.
## 8.3.7 Mapping to HTTP methods URL mappings can also be configured to map based on the HTTP method (GET, POST, PUT or DELETE). This is very useful for RESTful APIs and for restricting mappings based on HTTP method. As an example the following mappings provide a RESTful API URL mappings for the `ProductController`:
    static mappings = {
       "/product/$id"(controller:"product", action: "update", method: "PUT")
    }
Note that if you specify a HTTP method other than GET in your URL mapping, you also have to specify it when creating the corresponding link by passing the `method` argument to `g:link` or `g:createLink` to get a link of the desired format. ## 8.3.8 Mapping Wildcards Grails' URL mappings mechanism also supports wildcard mappings. For example consider the following mapping:
    static mappings = {
        "/images/*.jpg"(controller: "image")
    }
This mapping will match all paths to images such as `/image/logo.jpg`. Of course you can achieve the same effect with a variable:
    static mappings = {
        "/images/$name.jpg"(controller: "image")
    }
However, you can also use double wildcards to match more than one level below:
    static mappings = {
        "/images/**.jpg"(controller: "image")
    }
In this cases the mapping will match `/image/logo.jpg` as well as `/image/other/logo.jpg`. Even better you can use a double wildcard variable:
    static mappings = {
        // will match /image/logo.jpg and /image/other/logo.jpg
        "/images/$name**.jpg"(controller: "image")
    }
In this case it will store the path matched by the wildcard inside a `name` parameter obtainable from the params object:
    def name = params.name
    println name // prints "logo" or "other/logo"
If you use wildcard URL mappings then you may want to exclude certain URIs from Grails' URL mapping process. To do this you can provide an `excludes` setting inside the `UrlMappings.groovy` class:
    class UrlMappings {
        static excludes = ["/images/*", "/css/*"]
        static mappings = {
            ...
        }
    }
In this case Grails won’t attempt to match any URIs that start with `/images` or `/css`. ## 8.3.9 Automatic Link Re-Writing Another great feature of URL mappings is that they automatically customize the behaviour of the link tag so that changing the mappings don’t require you to go and change all of your links. This is done through a URL re-writing technique that reverse engineers the links from the URL mappings. So given a mapping such as the blog one from an earlier section:
    static mappings = {
       "/$blog/$year?/$month?/$day?/$id?"(controller:"blog", action:"show")
    }
If you use the link tag as follows:
    <g:link controller="blog" action="show"
            params="[blog:'fred', year:2007]">
        My Blog
    </g:link>

    <g:link controller="blog" action="show"
            params="[blog:'fred', year:2007, month:10]">
        My Blog - October 2007 Posts
    </g:link>
Grails will automatically re-write the URL in the correct format:
    <a href="/fred/2007">My Blog</a>
    <a href="/fred/2007/10">My Blog - October 2007 Posts</a>
## 8.3.10 Applying Constraints URL Mappings also support Grails' unified validation constraints mechanism, which lets you further "constrain" how a URL is matched. For example, if we revisit the blog sample code from earlier, the mapping currently looks like this:
    static mappings = {
       "/$blog/$year?/$month?/$day?/$id?"(controller:"blog", action:"show")
    }
This allows URLs such as:
    /graemerocher/2007/01/10/my_funky_blog_entry
However, it would also allow:
    /graemerocher/not_a_year/not_a_month/not_a_day/my_funky_blog_entry
This is problematic as it forces you to do some clever parsing in the controller code. Luckily, URL Mappings can be constrained to further validate the URL tokens:
    "/$blog/$year?/$month?/$day?/$id?" {
         controller = "blog"
         action = "show"
         constraints {
              year(matches:/\\\d{4}/)
              month(matches:/\\\d{2}/)
              day(matches:/\\\d{2}/)
         }
    }
In this case the constraints ensure that the `year`, `month` and `day` parameters match a particular valid pattern thus relieving you of that burden later on. ## 8.3.11 Named URL Mappings URL Mappings also support named mappings, that is mappings which have a name associated with them. The name may be used to refer to a specific mapping when links are generated. The syntax for defining a named mapping is as follows:
    static mappings = {
       name <mapping name>: <url pattern> {
          // ...
       }
    }
For example:
    static mappings = {
        name personList: "/showPeople" {
            controller = 'person'
            action = 'list'
        }
        name accountDetails: "/details/$acctNumber" {
            controller = 'product'
            action = 'accountDetails'
        }
    }
The mapping may be referenced in a link tag in a GSP.
    <g:link mapping="personList">List People</g:link>
That would result in:
    <a href="/showPeople">List People</a>
Parameters may be specified using the params attribute.
    <g:link mapping="accountDetails" params="[acctNumber:'8675309']">
        Show Account
    </g:link>
That would result in:
    <a href="/details/8675309">Show Account</a>
Alternatively you may reference a named mapping using the link namespace.
    <link:personList>List People</link:personList>
That would result in:
    <a href="/showPeople">List People</a>
The link namespace approach allows parameters to be specified as attributes.
    <link:accountDetails acctNumber="8675309">Show Account</link:accountDetails>
That would result in:
    <a href="/details/8675309">Show Account</a>
To specify attributes that should be applied to the generated `href`, specify a `Map` value to the `attrs` attribute. These attributes will be applied directly to the href, not passed through to be used as request parameters.
    <link:accountDetails attrs="[class: 'fancy']" acctNumber="8675309">
        Show Account
    </link:accountDetails>
That would result in:
    <a href="/details/8675309" class="fancy">Show Account</a>
## 8.3.12 Customizing URL Formats The default URL Mapping mechanism supports camel case names in the URLs. The default URL for accessing an action named `addNumbers` in a controller named `MathHelperController` would be something like `/mathHelper/addNumbers`. Grails allows for the customization of this pattern and provides an implementation which replaces the camel case convention with a hyphenated convention that would support URLs like `/math-helper/add-numbers`. To enable hyphenated URLs assign a value of "hyphenated" to the `grails.web.url.converter` property in `grails-app/conf/application.groovy`. grails-app/conf/application.groovy
    grails.web.url.converter = 'hyphenated'
Arbitrary strategies may be plugged in by providing a class which implements the UrlConverter interface and adding an instance of that class to the Spring application context with the bean name of `grails.web.UrlConverter.BEAN_NAME`. If Grails finds a bean in the context with that name, it will be used as the default converter and there is no need to assign a value to the `grails.web.url.converter` config property. src/main/groovy/com/myapplication/MyUrlConverterImpl.groovy
    package com.myapplication

    class MyUrlConverterImpl implements grails.web.UrlConverter {

        String toUrlElement(String propertyOrClassName) {
            // return some representation of a property or class name that should be used in URLs...
        }
    }
grails-app/conf/spring/resources.groovy
    beans = {
        "${grails.web.UrlConverter.BEAN_NAME}"(com.myapplication.MyUrlConverterImpl)
    }
## 8.3.13 Namespaced Controllers If an application defines multiple controllers with the same name in different packages, the controllers must be defined in a namespace. The way to define a namespace for a controller is to define a static property named `namespace` in the controller and assign a String to the property that represents the namespace. grails-app/controllers/com/app/reporting/AdminController.groovy
    package com.app.reporting

    class AdminController {

        static namespace = 'reports'

        // ...
    }
grails-app/controllers/com/app/security/AdminController.groovy
    package com.app.security

    class AdminController {

        static namespace = 'users'

        // ...
    }
When defining url mappings which should be associated with a namespaced controller, the `namespace` variable needs to be part of the URL mapping. grails-app/controllers/UrlMappings.groovy
    class UrlMappings {

        static mappings = {
            '/userAdmin' {
                controller = 'admin'
                namespace = 'users'
            }

            '/reportAdmin' {
                controller = 'admin'
                namespace = 'reports'
            }

            "/$namespace/$controller/$action?"()
        }
    }
Reverse URL mappings also require that the `namespace` be specified.
    <g:link controller="admin" namespace="reports">Click For Report Admin</g:link>
    <g:link controller="admin" namespace="users">Click For User Admin</g:link>
When resolving a URL mapping (forward or reverse) to a namespaced controller, a mapping will only match if the `namespace` has been provided. If the application provides several controllers with the same name in different packages, at most 1 of them may be defined without a `namespace` property. If there are multiple controllers with the same name that do not define a `namespace` property, the framework will not know how to distinguish between them for forward or reverse mapping resolutions. It is allowed for an application to use a plugin which provides a controller with the same name as a controller provided by the application and for neither of the controllers to define a `namespace` property as long as the controllers are in separate packages. For example, an application may include a controller named `com.accounting.ReportingController` and the application may use a plugin which provides a controller named `com.humanresources.ReportingController`. The only issue with that is the URL mapping for the controller provided by the plugin needs to be explicit in specifying that the mapping applies to the `ReportingController` which is provided by the plugin. See the following example.
    static mappings = {
        "/accountingReports" {
            controller = "reporting"
        }
        "/humanResourceReports" {
            controller = "reporting"
            plugin = "humanResources"
        }
    }
With that mapping in place, a request to `/accountingReports` will be handled by the `ReportingController` which is defined in the application. A request to `/humanResourceReports` will be handled by the `ReportingController` which is provided by the `humanResources` plugin. There could be any number of `ReportingController` controllers provided by any number of plugins but no plugin may provide more than one `ReportingController` even if they are defined in separate packages. Assigning a value to the `plugin` variable in the mapping is only required if there are multiple controllers with the same name available at runtime provided by the application and/or plugins. If the `humanResources` plugin provides a `ReportingController` and there is no other `ReportingController` available at runtime, the following mapping would work.
    static mappings = {
        "/humanResourceReports" {
            controller = "reporting"
        }
    }
It is best practice to be explicit about the fact that the controller is being provided by a plugin. | Quick Reference (hide)