6.4 URL映射 - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: null
Table of Contents
6.4 URL映射
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/conf/UrlMappings.groovy.The UrlMappings class contains a single property called mappings that has been assigned a block of code:class UrlMappings {
static mappings = {
}
}6.4.1 映射到控制器和操作
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")
/product to the list action of the ProductController. Omit the action definition to map to the default action of the controller:"/product"(controller: "product")
"/product" { controller = "product" action = "list" }
"/hello"(uri: "/hello.dispatch")
6.4.2 嵌入式变量
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") }
id. For example given the URL /product/MacBook, the following code will render "MacBook" to the response:class ProductController {
def index() { render params.id }
}static mappings = { "/$blog/$year/$month/$day/$id"(controller: "blog", action: "show") }
/graemerocher/2007/01/10/my_funky_blog_entry
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?"() }
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") }
/graemerocher/2007/01/10/my_funky_blog_entry
/graemerocher/2007/01/10
/graemerocher/2007/01
/graemerocher/2007
/graemerocherArbitrary 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 }
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 }
6.4.3 映射到视图
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 }
static mappings = { "/help"(controller: "site", view: "help") // to a view for a controller }
6.4.4 映射到响应代码
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") }
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") }
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.
6.4.5 映射到HTTP方法
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 theProductController:static mappings = { "/product/$id"(controller:"product") { action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"] } }
6.4.6 映射到通配符
Grails' URL mappings mechanism also supports wildcard mappings. For example consider the following mapping:static mappings = { "/images/*.jpg"(controller: "image") }
/image/logo.jpg. Of course you can achieve the same effect with a variable:static mappings = { "/images/$name.jpg"(controller: "image") }
static mappings = { "/images/**.jpg"(controller: "image") }
/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") }
name parameter obtainable from the params object:def name = params.name println name // prints "logo" or "other/logo"
excludes setting inside the UrlMappings.groovy class:class UrlMappings {
static excludes = ["/images/*", "/css/*"]
static mappings = {
…
}
}/images or /css.
6.4.7 自动重写链接
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") }
<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>
<a href="/fred/2007">My Blog</a> <a href="/fred/2007/10">My Blog - October 2007 Posts</a>
6.4.8 应用约束
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") }
/graemerocher/2007/01/10/my_funky_blog_entry
/graemerocher/not_a_year/not_a_month/not_a_day/my_funky_blog_entry
"/$blog/$year?/$month?/$day?/$id?" { controller = "blog" action = "show" constraints { year(matches:/\d{4}/) month(matches:/\d{2}/) day(matches:/\d{2}/) } }
year, month and day parameters match a particular valid pattern thus relieving you of that burden later on.
6.4.9 命名URL映射
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> {
// …
}
}static mappings = { name personList: "/showPeople" { controller = 'person' action = 'list' } name accountDetails: "/details/$acctNumber" { controller = 'product' action = 'accountDetails' } }
<g:link mapping="personList">List People</g:link>
<a href="/showPeople">List People</a>
<g:link mapping="accountDetails" params="[acctNumber:'8675309']"> Show Account </g:link>
<a href="/details/8675309">Show Account</a>
<link:personList>List People</link:personList>
<a href="/showPeople">List People</a>
<link:accountDetails acctNumber="8675309">Show Account</link:accountDetails>
<a href="/details/8675309">Show Account</a>
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>
<a href="/details/8675309" class="fancy">Show Account</a>
6.4.10 自定义URL格式
The default URL Mapping mechanism supports camel case names in the URLs. The default URL for accessing an action namedaddNumbers 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/Config.groovy.// grails-app/conf/Config.groovygrails.web.url.converter = 'hyphenated'
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/groovy/com/myapplication/MyUrlConverterImpl.groovypackage com.myapplicationclass 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.groovybeans = {
"${grails.web.UrlConverter.BEAN_NAME}"(com.myapplication.MyUrlConverterImpl)
}
