6.2 Groovy服务器页面(GSP) - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: null
Table of Contents
6.2 Groovy服务器页面(GSP)
Groovy Servers Pages (or GSP for short) is Grails' view technology. It is designed to be familiar for users of technologies such as ASP and JSP, but to be far more flexible and intuitive.GSPs live in the A GSP is typically a mix of mark-up and GSP tags which aid in view rendering.This action will look up a
Groovy服务器页面(或者简称为GSP)是Grails的视图技术。它是专为熟悉ASP和JSP技术的用户而设计,不过更加灵活和直观。GSPs位于grails-app/views directory and are typically rendered automatically (by convention) or with the render method such as:render(view: "index")Although it is possible to have Groovy logic embedded in your GSP and doing this will be covered in this document, the practice is strongly discouraged. Mixing mark-up and code is a bad thing and most GSP pages contain no code and needn't do so.A GSP typically has a "model" which is a set of variables that are used for view rendering. The model is passed to the GSP view from a controller. For example consider the following controller action:
def show() {
[book: Book.get(params.id)]
}Book instance and create a model that contains a key called book. This key can then be referenced within the GSP view using the name book:${book.title}grails-app/views目录下边,通常情况下是自动渲染的(基于规约)或者通过render方法,比如:render(view: "index")虽然在你的GSP中可以使用内嵌的Groovy逻辑(本文档将会涉及),但是作为最佳实践,是非常不鼓励这么做的。混合使用标记和代码是一件很 不好 的事情,而且大多数的GSP页面无需也不必包含代码。GSP通常都会有一个用以视图渲染所需的变量集合"模型(model)",此模型是从控制器传递到GSP视图的。以如下的控制器操作为例:
def show() {
[book: Book.get(params.id)]
}Book实例,并且创建一个包含键book的模型。 此键在GSP视图中可以通过名字book来引用:${book.title}6.2.1 GSP基础
In the next view sections we'll go through the basics of GSP and what is available to you. First off let's cover some basic syntax that users of JSP and ASP should be familiar with.GSP supports the usage of You can also use the GSP also supports JSP-style server-side comments (which are not rendered in the HTML response) as the following example demonstrates:
在下一个视图章节中,我们将涉及GSP的基础部分以及那些是你所需的。本节首先简单介绍一些基础的语法,对于JSP和ASP用户来说,这些应该都很熟悉。GSP支持内嵌Groovy代码的用法(再次强调,不提倡这样用)是通过<% %> scriptlet blocks to embed Groovy code (again this is discouraged):<html> <body> <% out << "Hello GSP!" %> </body> </html>
<%= %> syntax to output values:<html> <body> <%="Hello GSP!" %> </body> </html>
<html> <body> <%-- This is my comment --%> <%="Hello GSP!" %> </body> </html>
<% %>的脚本代码块的来实现的,比如:<html> <body> <% out << "Hello GSP!" %> </body> </html>
<%= %>的语法来输出:<html> <body> <%="Hello GSP!" %> </body> </html>
<html> <body> <%-- This is my comment --%> <%="Hello GSP!" %> </body> </html>
6.2.1.1 变量和作用域
Within the and then access those variables later in the page:Within the scope of a GSP there are a number of pre-defined variables, including:
在<% %> brackets you can declare variables:<% now = new Date() %><%=now%>application- The javax.servlet.ServletContext instanceapplicationContextThe Spring ApplicationContext instanceflash- The flash objectgrailsApplication- The GrailsApplication instanceout- The response writer for writing to the output streamparams- The params object for retrieving request parametersrequest- The HttpServletRequest instanceresponse- The HttpServletResponse instancesession- The HttpSession instancewebRequest- The GrailsWebRequest instance
<% %>内,你可以声明变量:<% now = new Date() %><%=now%>application- javax.servlet.ServletContext实例applicationContextSpring的ApplicationContext实例flash- flash对象grailsApplication- GrailsApplication实例out- 响应输出流输出器params- params对象,用于接收请求参数request- HttpServletRequest实例response- HttpServletResponse实例session- HttpSession实例webRequest- GrailsWebRequest实例
6.2.1.2 逻辑和迭代
Using the As well as logical branching:
使用<% %> syntax you can embed loops and so on using this syntax:<html> <body> <% [1,2,3,4].each { num -> %> <p><%="Hello ${num}!" %></p> <%}%> </body> </html>
<html> <body> <% if (params.hello == 'true')%> <%="Hello!"%> <% else %> <%="Goodbye!"%> </body> </html>
<% %>语法你可以内嵌循环之类的用法,其语法如下:<html> <body> <% [1,2,3,4].each { num -> %> <p><%="Hello ${num}!" %></p> <%}%> </body> </html>
<html> <body> <% if (params.hello == 'true')%> <%="Hello!"%> <% else %> <%="Goodbye!"%> </body> </html>
6.2.1.3 页面指令
GSP also supports a few JSP-style page directives.The import directive lets you import classes into the page. However, it is rarely needed due to Groovy's default imports and GSP Tags:GSP also supports the contentType directive:The contentType directive allows using GSP to render other formats.
GSP也支持一些JSP风格的页面指令。import指令可以让你将Java类导入到页面中。但是它应该很少使用,因为已经有Groovy缺省导入和GSP标签:<%@ page import="java.awt.*" %><%@ page contentType="text/json" %><%@ page import="java.awt.*" %><%@ page contentType="text/json" %>6.2.1.4 表达式
In GSP the However, unlike JSP EL you can have any Groovy expression within the Other possible values are 'none' (for no default encoding) and 'base64'.
在GSP中,一开始所介绍的<%= %> syntax introduced earlier is rarely used due to the support for GSP expressions. A GSP expression is similar to a JSP EL expression or a Groovy GString and takes the form ${expr}:<html> <body> Hello ${params.name} </body> </html>
${..} block. Variables within the ${..} block are not escaped by default, so any HTML in the variable's string is rendered directly to the page. To reduce the risk of Cross-site-scripting (XSS) attacks, you can enable automatic HTML escaping with the grails.views.default.codec setting in grails-app/conf/Config.groovy:grails.views.default.codec='html'<%= %>语法是很少被应用于GSP表达式的。一个GSP表达式类似于JSP EL表达式或者Groovy GString,使用的是${expr}形式:<html> <body> Hello ${params.name} </body> </html>
${..}代码块中使用任意Groovy表达式。${..}块中的变量缺省是 不 被转义的,因此变量中字符串将会直接被渲染到页面HTML。要减少这种Cross-site-scripting (XSS)攻击风险,你可以使用自动HTML转义来避免,只需要在grails-app/conf/Config.groovy中配置grails.views.default.codec即可:grails.views.default.codec='html'6.2.2 GSP标签
Now that the less attractive JSP heritage has been set aside, the following sections cover GSP's built-in tags, which are the preferred way to define GSP pages.GSP tags can also have a body such as:Expressions can be passed into GSP tag attributes, if an expression is not used it will be assumed to be a String value:Maps can also be passed into GSP tag attributes, which are often used for a named parameter style syntax:Note that within the values of attributes you must use single quotes for Strings:With the basic syntax out the way, the next sections look at the tags that are built into Grails by default.
现在没有吸引力的JSP遗留部分已经被废除了,那么接下来的章节,我们将讨论GSP的内置标签,它们是定义GSP页面非常有力的方法。The section on Tag Libraries covers how to add your own custom tag libraries.All built-in GSP tags start with the prefix
g:. Unlike JSP, you don't specify any tag library imports. If a tag starts with g: it is automatically assumed to be a GSP tag. An example GSP tag would look like:<g:example /><g:example> Hello world </g:example>
<g:example attr="${new Date()}"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:'one', two:'two']"> Hello world </g:example>
标签库章节讨论的是如何添加你自己定制的标签库所有内置的GSP标签都是以前缀
g:开始的。跟JSP不同的是,你不需要指定任何的标签库导入。如果一个标签以g:开头,那么将会自动地被当作GSP标签看待。一个GSP的标签的样子如下所示:<g:example /><g:example> Hello world </g:example>
<g:example attr="${new Date()}"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]"> Hello world </g:example>
<g:example attr="${new Date()}" attr2="[one:'one', two:'two']"> Hello world </g:example>
6.2.2.1 变量和作用域
Variables can be defined within a GSP using the set tag:Here we assign a variable called Variables can also be placed in one of the following scopes:
在GSP中,可以通过set标签来定义变量:<g:set var="now" value="${new Date()}" />now to the result of a GSP expression (which simply constructs a new java.util.Date instance). You can also use the body of the <g:set> tag to define a variable:<g:set var="myHTML"> Some re-usable code on: ${new Date()} </g:set>
page- Scoped to the current page (default)request- Scoped to the current requestflash- Placed within flash scope and hence available for the next requestsession- Scoped for the user sessionapplication- Application-wide scope.
scope attribute:<g:set var="now" value="${new Date()}" scope="request" /><g:set var="now" value="${new Date()}" />java.util.Date实例)的结果赋值给now变量。你也可以使用<g:set>标签的主体来定义一个变量:<g:set var="myHTML"> Some re-usable code on: ${new Date()} </g:set>
page- 作用于当前页面(缺省)request- 作用于当前请求flash- 置于flash作用域内,因此在下一个请求中是有效的session- 作用于用户会话application- 应用级别的作用域
scope属性:<g:set var="now" value="${new Date()}" scope="request" />6.2.2.2 逻辑和迭代
GSP also supports logical and iterative tags out of the box. For logic there are if, else and elseif tags for use with branching:Use the each and while tags for iteration:
GSP也支持逻辑和迭代地标签。if、else和elseif标签用于逻辑,用以处理分支:<g:if test="${session.role == 'admin'}"> <%-- show administrative functions --%> </g:if> <g:else> <%-- show basic functions --%> </g:else>
<g:each in="${[1,2,3]}" var="num"> <p>Number ${num}</p> </g:each><g:set var="num" value="${1}" /> <g:while test="${num < 5 }"> <p>Number ${num++}</p> </g:while>
<g:if test="${session.role == 'admin'}"> <%-- show administrative functions --%> </g:if> <g:else> <%-- show basic functions --%> </g:else>
<g:each in="${[1,2,3]}" var="num"> <p>Number ${num}</p> </g:each><g:set var="num" value="${1}" /> <g:while test="${num < 5 }"> <p>Number ${num++}</p> </g:while>
6.2.2.3 搜索和过滤
If you have collections of objects you often need to sort and filter them. Use the findAll and grep tags for these tasks:The Or using a regular expression:The above example is also interesting due to its usage of GPath. GPath is an XPath-like language in Groovy. The
如果你的对象是集合,那么你经常需要排序和过滤。使用findAll和grep标签可以完成这些任务:Stephen King's Books: <g:findAll in="${books}" expr="it.author == 'Stephen King'"> <p>Title: ${it.title}</p> </g:findAll>
expr attribute contains a Groovy expression that can be used as a filter. The grep tag does a similar job, for example filtering by class:<g:grep in="${books}" filter="NonFictionBooks.class"> <p>Title: ${it.title}</p> </g:grep>
<g:grep in="${books.title}" filter="~/.*?Groovy.*?/"> <p>Title: ${it}</p> </g:grep>
books variable is a collection of Book instances. Since each Book has a title, you can obtain a list of Book titles using the expression books.title. Groovy will auto-magically iterate the collection, obtain each title, and return a new list!
Stephen King's Books: <g:findAll in="${books}" expr="it.author == 'Stephen King'"> <p>Title: ${it.title}</p> </g:findAll>
expr属性使用一个Groovy表达式来作为过滤器。grep标签完成类似的任务,比如要过滤对象类:<g:grep in="${books}" filter="NonFictionBooks.class"> <p>Title: ${it.title}</p> </g:grep>
<g:grep in="${books.title}" filter="~/.*?Groovy.*?/"> <p>Title: ${it}</p> </g:grep>
books变量是一个Book实例的集合。因为每一个Book都有title,你可以使用表达式books.title来获取Book标题的列表。Groovy将会自动地对集合迭代,获取每一个标题,最终返回一个新的列表。
6.2.2.4 链接和资源
GSP also features tags to help you manage linking to controllers and actions. The link tag lets you specify controller and action name pairing and it will automatically work out the link based on the URL Mappings, even if you change them! For example:
GSP标签也能帮助你来管理控制器和操作的超链接。link标签让你来指定控制器和操作名称对,并且标签会自动生成基于URL映射的链接,即使映射改变了也没有问题,比如:<g:link action="show" id="1">Book 1</g:link><g:link action="show" id="${currentBook.id}">${currentBook.name}</g:link><g:link controller="book">Book Home</g:link><g:link controller="book" action="list">Book List</g:link><g:link url="[action: 'list', controller: 'book']">Book List</g:link><g:link params="[sort: 'title', order: 'asc', author: currentBook.author]" action="list">Book List</g:link>
<g:link action="show" id="1">Book 1</g:link><g:link action="show" id="${currentBook.id}">${currentBook.name}</g:link><g:link controller="book">Book Home</g:link><g:link controller="book" action="list">Book List</g:link><g:link url="[action: 'list', controller: 'book']">Book List</g:link><g:link params="[sort: 'title', order: 'asc', author: currentBook.author]" action="list">Book List</g:link>
6.2.2.5 表单和字段
Form Basics
GSP supports many different tags for working with HTML forms and fields, the most basic of which is the form tag. This is a controller/action aware version of the regular HTML form tag. Theurl attribute lets you specify which controller and action to map to:<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>
myForm that submits to the BookController's list action. Beyond that all of the usual HTML attributes apply.
表单基础
GSP有很多不同的标签来支持HTML表单和字段,不过最基础的还是form标签。常规的HTML表单标签支持controller/action属性,而url属性让你以映射(map)的方式来指定controller和action:<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>
myForm表单,它将会提交到BookController控制器的list操作。此外HTML的所有通用属性都可以使用。Form Fields
In addition to easy construction of forms, GSP supports custom tags for dealing with different types of fields, including:- textField - For input fields of type 'text'
- passwordField - For input fields of type 'password'
- checkBox - For input fields of type 'checkbox'
- radio - For input fields of type 'radio'
- hiddenField - For input fields of type 'hidden'
- select - For dealing with HTML select boxes
<g:textField name="myField" value="${myValue}" />表单字段
除了轻松地构造表单之外,GSP自定义的标签支持不同的字段类型,包括:- textField - 针对类型是'text'的输入字段
- passwordField - 针对类型是'password'的输入字段
- checkBox - 针对类型是'checkbox'的输入字段
- radio - 针对类型是'radio'的输入字段
- hiddenField - 针对类型是'hidden'的输入字段
- select - 针对HTML的下拉框(select boxes)
<g:textField name="myField" value="${myValue}" />Multiple Submit Buttons
The age old problem of dealing with multiple submit buttons is also handled elegantly with Grails using the actionSubmit tag. It is just like a regular submit, but lets you specify an alternative action to submit to:<g:actionSubmit value="Some update label" action="update" />多个提交按钮
处理多个提交按钮这一个古老的问题,也得到优雅的解决,那就是使用Grails的actionSubmit标签。跟常规的提交类似,只不过你可以指定另外一个操作来提交:<g:actionSubmit value="Some update label" action="update" />6.2.2.6 标签的方法调用
One major different between GSP tags and other tagging technologies is that GSP tags can be called as either regular tags or as method calls from controllers, tag libraries or GSP views.This is particularly useful for using a tag within an attribute:In view technologies that don't support this feature you have to nest tags within tags, which becomes messy quickly and often has an adverse effect of WYSWIG tools such as Dreamweaver that attempt to render the mark-up as it is not well-formed:
One major different between GSP tags and other tagging technologies is that GSP tags can be called as either regular tags or as method calls from controllers, tag libraries or GSP views.Tags as method calls from GSPs
Tags return their results as a String-like object (aStreamCharBuffer which has all of the same methods as String) instead of writing directly to the response when called as methods. For example:Static Resource: ${createLinkTo(dir: "images", file: "logo.jpg")}<img src="${createLinkTo(dir: 'images', file: 'logo.jpg')}" /><img src="<g:createLinkTo dir="images" file="logo.jpg" />" />在GSP中以方法调用标签
当标签以方法的方式调用时,其返回一个类似String(一个StreamCharBuffer,有着跟String完全相同的方法)的对象,而不是直接写回到响应器。比如:Static Resource: ${createLinkTo(dir: "images", file: "logo.jpg")}<img src="${createLinkTo(dir: 'images', file: 'logo.jpg')}" /><img src="<g:createLinkTo dir="images" file="logo.jpg" />" />Tags as method calls from Controllers and Tag Libraries
You can also invoke tags from controllers and tag libraries. Tags within the defaultg: namespace can be invoked without the prefix and a StreamCharBuffer result is returned:def imageLocation = createLinkTo(dir:"images", file:"logo.jpg").toString()
def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg").toString()
def editor = fckeditor.editor(name: "text", width: "100%", height: "400")
在控制器和标签库中的以方法调用标签
你可以可以在控制器和标签库中调用标签。命名空间是g:的标签调用可以忽略其前缀,并且一个StreamCharBuffer类型的结果被返回:def imageLocation = createLinkTo(dir:"images", file:"logo.jpg").toString()
def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg").toString()
def editor = fckeditor.editor(name: "text", width: "100%", height: "400")
6.2.3 视图和模板
Grails also has the concept of templates. These are useful for partitioning your views into maintainable chunks, and combined with Layouts provide a highly re-usable mechanism for structured views.Use the render tag to render this template from one of the views in Notice how we pass into a model to use using the
Grails也有模板的概念。这对于将你的视图分割成可维护的模块也是颇有裨益的,并且结合布局还可为结构化视图提供一个高复用机制。Template Basics
Grails uses the convention of placing an underscore before the name of a view to identify it as a template. For example, you might have a template that renders Books located atgrails-app/views/book/_bookTemplate.gsp:<div class="book" id="${book?.id}"> <div>Title: ${book?.title}</div> <div>Author: ${book?.author?.name}</div> </div>
grails-app/views/book:<g:render template="bookTemplate" model="[book: myBook]" />model attribute of the render tag. If you have multiple Book instances you can also render the template for each Book using the render tag with a collection attribute:<g:render template="bookTemplate" var="book" collection="${bookList}" />模板基础
Grails使用在其视图名称前放置一个下划线的方式来标识一个模板。比如,你可能有一个渲染Books的模板,位于grails-app/views/book/_bookTemplate.gsp:<div class="book" id="${book?.id}"> <div>Title: ${book?.title}</div> <div>Author: ${book?.author?.name}</div> </div>
grails-app/views/book中的一个视图中,使用render标签来渲染此模板:<g:render template="bookTemplate" model="[book: myBook]" />render标签的model属性来传递模型的。如果你有多个Book实例,你还可以通过render标签的collection属性来为每一个Book渲染模板:<g:render template="bookTemplate" var="book" collection="${bookList}" />Shared Templates
In the previous example we had a template that was specific to theBookController and its views at grails-app/views/book. However, you may want to share templates across your application.In this case you can place them in the root views directory at grails-app/views or any subdirectory below that location, and then with the template attribute use an absolute location starting with / instead of a relative location. For example if you had a template called grails-app/views/shared/_mySharedTemplate.gsp, you would reference it as:<g:render template="/shared/mySharedTemplate" /><g:render template="/book/bookTemplate" model="[book: myBook]" />共享的模板
在上一个示例中,我们有了一个跟BookController相关的模板,其视图都位于grails-app/views/book中。然而,有时候,你可能想将你的模板在整个应用中共享。在这种情况下,你可以将模板放在grails-app/views这个视图根目录下,或者跟目录下的任意子目录中,然后在template属性中使用以/开头的绝对位置而非相对位置。比如,你有一个grails-app/views/shared/_mySharedTemplate.gsp模板,你就可以这样引用:<g:render template="/shared/mySharedTemplate" /><g:render template="/book/bookTemplate" model="[book: myBook]" />The Template Namespace
Since templates are used so frequently there is template namespace, calledtmpl, available that makes using templates easier. Consider for example the following usage pattern:<g:render template="bookTemplate" model="[book:myBook]" />tmpl namespace as follows:<tmpl:bookTemplate book="${myBook}" />模板的命名空间
因为模板是如此频繁地被使用,因此tmpl这个模板命名空间就产生了,这样模板的使用也更简易。比如下例所示地用法:<g:render template="bookTemplate" model="[book:myBook]" />tmpl命名空间的表达如下所示:<tmpl:bookTemplate book="${myBook}" />Templates in Controllers and Tag Libraries
You can also render templates from controllers using the render controller method. This is useful for Ajax applications where you generate small HTML or data responses to partially update the current page instead of performing new request:def bookData() {
def b = Book.get(params.id)
render(template:"bookTemplate", model:[book:b])
}def bookData() {
def b = Book.get(params.id)
String content = g.render(template:"bookTemplate", model:[book:b])
render content
}g namespace which tells Grails we want to use the tag as method call instead of the render method.
控制器和标签库的模板
你还可以在控制器中使用render方法来渲染模板。这在Ajax的应用中是非常有用的,你可以通过生成小的HTML或者数据响应来部分的更新当前页面,而不是发起一个新的请求:def bookData() {
def b = Book.get(params.id)
render(template:"bookTemplate", model:[book:b])
}def bookData() {
def b = Book.get(params.id)
String content = g.render(template:"bookTemplate", model:[book:b])
render content
}g命名空间的用法,它会让Grails知道我们想用标签的方法调用,而不是render方法。
6.2.4 使用Sitemesh布局
Creating Layouts
Grails leverages Sitemesh, a decorator engine, to support view layouts. Layouts are located in thegrails-app/views/layouts directory. A typical layout can be seen below:<html> <head> <title><g:layoutTitle default="An example decorator" /></title> <g:layoutHead /> </head> <body onload="${pageProperty(name:'body.onload')}"> <div class="menu"><!--my common menu goes here--></menu> <div class="body"> <g:layoutBody /> </div> </div> </body> </html>
layoutTitle- outputs the target page's titlelayoutHead- outputs the target page's head tag contentslayoutBody- outputs the target page's body tag contents
创建布局
Grails使用Sitemesh(一个装饰引擎)来支持视图布局。布局位于grails-app/views/layouts目录下边。一个典型的布局可以如下所示:<html> <head> <title><g:layoutTitle default="An example decorator" /></title> <g:layoutHead /> </head> <body onload="${pageProperty(name:'body.onload')}"> <div class="menu"><!--my common menu goes here--></menu> <div class="body"> <g:layoutBody /> </div> </div> </body> </html>
layoutTitle- 输出目标页面的标题(title)layoutHead- 输出目标页面的head标签的内容layoutBody- 输出目标页面的body标签的内容
Triggering Layouts
There are a few ways to trigger a layout. The simplest is to add a meta tag to the view:<html> <head> <title>An Example Page</title> <meta name="layout" content="main" /> </head> <body>This is my content!</body> </html>
grails-app/views/layouts/main.gsp will be used to layout the page. If we were to use the layout from the previous section the output would resemble this:<html> <head> <title>An Example Page</title> </head> <body onload=""> <div class="menu"><!--my common menu goes here--></div> <div class="body"> This is my content! </div> </body> </html>
触发布局
有几种方法来触发一个布局。最简单的一种就是在视图中增加一个meta标签:<html> <head> <title>An Example Page</title> <meta name="layout" content="main" /> </head> <body>This is my content!</body> </html>
grails-app/views/layouts/main.gsp的布局将被用于安排页面。如果我们使用上一小节的布局,那么其输出类似下面所示:<html> <head> <title>An Example Page</title> </head> <body onload=""> <div class="menu"><!--my common menu goes here--></div> <div class="body"> This is my content! </div> </body> </html>
Specifying A Layout In A Controller
Another way to specify a layout is to specify the name of the layout by assigning a value to the "layout" property in a controller. For example, if you have a controller such as:class BookController {
static layout = 'customer' def list() { … }
}grails-app/views/layouts/customer.gsp which will be applied to all views that the BookController delegates to. The value of the "layout" property may contain a directory structure relative to the grails-app/views/layouts/ directory. For example:class BookController {
static layout = 'custom/customer' def list() { … }
}grails-app/views/layouts/custom/customer.gsp template.
在控制器中指定一个布局
另外一种指定布局的方法是在控制器中为"layout"属性赋值一个布局的名称。举一个例子,假设你有如下一个控制器:class BookController {
static layout = 'customer' def list() { … }
}grails-app/views/layouts/customer.gsp布局,这样BookController所有的所有视图将使用此布局。"layout"属性的值还可以包含一个目录结构,不过要相对于grails-app/views/layouts/目录。比如:class BookController {
static layout = 'custom/customer' def list() { … }
}grails-app/views/layouts/custom/customer.gsp模板来装饰。Layout by Convention
Another way to associate layouts is to use "layout by convention". For example, if you have this controller:class BookController {
def list() { … }
}grails-app/views/layouts/book.gsp, which will be applied to all views that the BookController delegates to.Alternatively, you can create a layout called grails-app/views/layouts/book/list.gsp which will only be applied to the list action within the BookController.If you have both the above mentioned layouts in place the layout specific to the action will take precedence when the list action is executed.If a layout may not be located using any of those conventions, the convention of last resort is to look for the application default layout which
is grails-app/views/layouts/application.gsp. The name of the application default layout may be changed by defining a property
in grails-app/conf/Config.groovy as follows:grails.sitemesh.default.layout = 'myLayoutName'grails-app/views/layouts/myLayoutName.gsp.
布局规约
另外一种关联布局的方式是使用"布局规约"。比如,你的控制器如下所示:class BookController {
def list() { … }
}grails-app/views/layouts/book.gsp布局,此布局将会应用于一个BookController的所有视图。此外,你也可以创建一个grails-app/views/layouts/book/list.gsp布局,其用于BookController的list操作。如果你有如上所述的两个布局,那么当list操作被执行时,跟操作相关的布局将优先使用。如果一个布局在这些约定中没有找到,那么此规约的最后顺序是查找应用的缺省布局grails-app/views/layouts/application.gsp。应用的缺省布局名称可以通过修改grails-app/conf/Config.groovy中的属性来改变,比如:grails.sitemesh.default.layout = 'myLayoutName'grails-app/views/layouts/myLayoutName.gsp。Inline Layouts
Grails' also supports Sitemesh's concept of inline layouts with the applyLayout tag. This can be used to apply a layout to a template, URL or arbitrary section of content. This lets you even further modularize your view structure by "decorating" your template includes.Some examples of usage can be seen below:<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" /><g:applyLayout name="myLayout" url="http://www.google.com" /><g:applyLayout name="myLayout"> The content to apply a layout to </g:applyLayout>
内联布局
Grails同样支持Sitemesh的内联布局概念,可以使用applyLayout标签来实现。此标签可以将一个布局应用于一个模板,URL或者任意部分的内容。更甚者,通过“装饰”你的模板你可以将你的视图模块化。这些用法的一些示例如下:<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" /><g:applyLayout name="myLayout" url="http://www.google.com" /><g:applyLayout name="myLayout"> The content to apply a layout to </g:applyLayout>
Server-Side Includes
While the applyLayout tag is useful for applying layouts to external content, if you simply want to include external content in the current page you use the include tag:<g:include controller="book" action="list" /><g:applyLayout name="myLayout"> <g:include controller="book" action="list" /> </g:applyLayout>
def content = include(controller:"book", action:"list")
服务器端的包含
applyLayout标签可以将布局应用到外部内容,而如果你只想简单地将外部内容包含到当前页面,你可以使用include标签:<g:include controller="book" action="list" /><g:applyLayout name="myLayout"> <g:include controller="book" action="list" /> </g:applyLayout>
def content = include(controller:"book", action:"list")
6.2.5 静态资源
Grails 2.0 integrates with the Resources plugin to provide sophisticated static resource management. This plugin is installed by default in new Grails applications.The basic way to include a link to a static resource in your application is to use the resource tag. This simple approach creates a URI pointing to the file.However modern applications with dependencies on multiple JavaScript and CSS libraries and frameworks (as well as dependencies on multiple Grails plugins) require something more powerful.The issues that the Resources framework tackles are:
Grails 2.0集成了资源插件以提供更复杂的静态资源管理。此插件在新建Grails应用中是缺省安装的。在你的应用中要引用一个静态资源链接的基本方法,就是使用resource标签。此种方式会创建一个指向文件的URI。但是,现在的应用往往会依赖于多个JavaScript、CSS库和框架(即依赖于多个Grails插件),这就要求一些更强大的功能来支撑。本资源(Resources)框架要解决的主要问题如下:
- Web application performance tuning is difficult
- Correct ordering of resources, and deferred inclusion of JavaScript
- Resources that depend on others that must be loaded first
- The need for a standard way to expose static resources in plugins and applications
- The need for an extensible processing chain to optimize resources
- Preventing multiple inclusion of the same resource
- Web应用的性能调优是非常困难的
- 正确地对资源排序,推迟引用JavaScript
- 依赖于其他的资源必须要优先加载
- 在插件和应用中需要采用一种标准的方式来暴露静态资源
- 需要扩展性更好的处理链来优化资源
- 阻止同样的资源被多次引用
6.2.5.1 通过资源标签引用资源
Pulling in resources with r:require
To use resources, your GSP page must indicate which resource modules it requires. For example with the jQuery plugin, which exposes a "jquery" resource module, to use jQuery in any page on your site you simply add:<html> <head> <r:require module="jquery"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
r:require multiple times in a GSP page, and you use the "modules" attribute to provide a list of modules:<html> <head> <r:require modules="jquery, main, blueprint, charting"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
使用r:require获取资源
要使用资源,你的GSP页面必须要知道那些资源模块是所需要的。以jQuery插件为例,其导出了一个"jquery"的资源模块,要在你站点的任何页面使用jQuery,你需要简单地增加如下代码:<html> <head> <r:require module="jquery"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
r:require,也可以使用"modules"属性提供一个模块列表:<html> <head> <r:require modules="jquery, main, blueprint, charting"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
Rendering the links to resources with r:layoutResources
When you have declared the resource modules that your GSP page requires, the framework needs to render the links to those resources at the correct time.To achieve this correctly, you must include the r:layoutResources tag twice in your page, or more commonly, in your GSP layout:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
使用r:layoutResources渲染资源链接
当在你的GSP页面中声明所需要的资源模块时,插件框架需要在正确的时间渲染那些资源的链接。要正确地处理,你必须在你的页面中引用两次r:layoutResources标签,或者更通用的方式是在你的GSP布局中处理:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
Adding page-specific JavaScript code with r:script
Grails has the javascript tag which is adapted to defer to Resources plugin if installed, but it is recommended that you callr:script directly when you need to include fragments of JavaScript code.This lets you write some "inline" JavaScript which is actually not rendered inline, but either in the <head> or at the end of the body, based on the disposition.Given a Sitemesh layout like this:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
<html> <head> <title>Testing r:script magic!</title> </head> <body> <r:script disposition="head"> window.alert('This is at the end of <head>'); </r:script> <r:script disposition="defer"> window.alert('This is at the end of the body, and the page has loaded.'); </r:script> </body> </html>
使用r:script增加特定页面的JavaScript代码
在资源插件安装以后,Grails的javascript标签将被适配到优先使用资源插件,即便如此,如果你需要直接使用JavaScript代码片段,还是推荐你直接调用r:script。这可以让你写一些“内联(inline)”的JavaScript,但实际 不在 内联时渲染,而是根据其安排,决定是在<head>或者body的结尾。假设一个Sitemesh布局如下所示:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
<html> <head> <title>Testing r:script magic!</title> </head> <body> <r:script disposition="head"> window.alert('This is at the end of <head>'); </r:script> <r:script disposition="defer"> window.alert('This is at the end of the body, and the page has loaded.'); </r:script> </body> </html>
Linking to images with r:img
This tag is used to render<img> markup, using the Resources framework to process the resource on the fly (if configured to do so - e.g. make it eternally cacheable).This includes any extra attributes on the <img> tag if the resource has been previously declared in a module.With this mechanism you can specify the width, height and any other attributes in the resource declaration in the module, and they will be pulled in as necessary.Example:<html> <head> <title>Testing r:img</title> </head> <body> <r:img uri="/images/logo.png"/> </body> </html>
g:img tag as a shortcut for rendering <img> tags that refer to a static resource. The Grails img tag is Resources-aware and will delegate to r:img if found. However it is recommended that you use r:img directly if using the Resources plugin.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.
使用r:img链接图片
此标签被用以渲染HTML的<img>标签,并且通过资源框架来处理那些频繁访问的资源(如果配置了的话,比如使其永久的缓存)。如果资源在以前已经被声明为一个模块的话,那么r:img会包含<img>标签的任何额外属性。基于此机制,你可以在声明资源模块的时候,来指定width、height以及其他任何属性,然后在需要的时候获取一下即可。比如:<html> <head> <title>Testing r:img</title> </head> <body> <r:img uri="/images/logo.png"/> </body> </html>
g:img标签只是渲染静态资源<img>的一个快捷方式而已。Grails的img的标签如果感知到资源插件,那么将会将其代理给r:img。即便如此,如果使用了资源插件的话,还是推荐直接你使用r:img。跟常规的Grails的resource标签属性一样,为了增加简洁性,r:img也支撑"uri"属性。更多完整的详细信息请参考r:resource文档。
6.2.5.2 其他资源标签
r:resource
This is equivalent to the Grails resource tag, returning a link to the processed static resource. Grails' owng:resource tag delegates to this implementation if found, but if your code requires the Resources plugin, you should use r:resource directly.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.
r:resource
这跟Grails的resource标签相当,都是返回一个处理过的静态资源链接。如果发现资源插件已经安装,Grails自带的g:resource标签将代理给r:resource,但是如果你的代码依赖资源插件,最好还是直接使用r:resource的好。跟常规的Grails的resource标签属性一样,为了增加简洁性,r:resource也支撑"uri"属性。更多完整的详细信息请参考r:resource文档。r:external
This is a resource-aware version of Grails external tag which renders the HTML markup necessary to include an external file resource such as CSS, JS or a favicon.See r:resource documentation for full details.r:external
这是一个资源感知(resource-aware)版本的Grails的external标签,用以渲染那些必要的HTML标签所需要的外部资源文件,比如CSS、JS或者favicon。更多完整的详细信息请参考r:resource文档。6.2.5.3 声明资源
A DSL is provided for declaring resources and modules. This can go either in your This defines three resource modules; 'core', 'utils' and 'forms'. The resources in these modules will be automatically bundled out of the box according to the module name, resulting in fewer files. You can override this with
系统提供了一个DSL专门用于声明资源和模块。其可以位于Config.groovy in the case of application-specific resources, or more commonly in a resources artefact in grails-app/conf.Note that you do not need to declare all your static resources, especially images. However you must to establish dependencies or other resources-specific attributes. Any resource that is not declared is called "ad-hoc" and will still be processed using defaults for that resource type.Consider this example resource configuration file, grails-app/conf/MyAppResources.groovy:modules = {
core {
dependsOn 'jquery, utils' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js'
resource url: '/css/main.css',
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css'
resource url: '/js/forms.js'
}
}bundle:'someOtherName' on each resource, or call defaultBundle on the module (see resources plugin documentation).It declares dependencies between them using dependsOn, which controls the load order of the resources.When you include an <r:require module="forms"/> in your GSP, it will pull in all the resources from 'core' and 'utils' as well as 'jquery', all in the correct order.You'll also notice the disposition:'head' on the core.js file. This tells Resources that while it can defer all the other JS files to the end of the body, this one must go into the <head>.The CSS file for print styling adds custom attributes using the attrs map option, and these are passed through to the r:external tag when the engine renders the link to the resource, so you can customize the HTML attributes of the generated link.There is no limit to the number of modules or xxxResources.groovy artefacts you can provide, and plugins can supply them to expose modules to applications, which is exactly how the jQuery plugin works.To define modules like this in your application's Config.groovy, you simply assign the DSL closure to the grails.resources.modules Config variable.For full details of the resource DSL please see the resources plugin documentation.
Config.groovy中特定应用的资源容器,或者更常用的是grails-app/conf下的一个资源工件中。注意!你并不需要声明所有的静态资源,尤其是图片。但是你必须要建立所需依赖或者其他资源相关的属性。没有被声明的资源称之为"ad-hoc",并且根据其资源类型被缺省处理。假设如下所示的grails-app/conf/MyAppResources.groovy资源配置文件:modules = {
core {
dependsOn 'jquery, utils' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js'
resource url: '/css/main.css',
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css'
resource url: '/js/forms.js'
}
}bundle:'someOtherName' 来覆盖之,或者调用模块的defaultBundle(更多请参考资源插件文档)。通过dependsOn来声明的依赖关系,可以控制资源的加载顺序。当你在的GSP中引用<r:require module="forms"/>的时候,它将从'core'和'utils'还有'jquery'中获取所有的资源,并以正确的顺序加载。你将会注意到core.js文件中的disposition:'head' 。它将告诉资源插件当所有的其他JS文件推迟到body末尾加载的时候,此文件(core.js)必须要在<head>加载。用于打印风格的CSS文件通过attrs映射选项来添加自定义属性,并且在渲染到资源链接的时候,它们将被传递给r:external标签,因此你可以自定义HTML的属性来生成链接。模块或者你定义的xxxResources.groovy工件的数量是没有限制的,插件也可以将资源模块暴露给应用,正如jQuery插件所做的那样。要在你应用中的Config.groovy定义模块,你可以简单地将DSL闭包赋给grails.resources.modules配置变量。完整的资源DSL信息请参考资源插件文档。
6.2.5.4 覆盖插件资源
Because a resource module can define the bundle groupings and other attributes of resources, you may find that the settings provided are not correct for your application.For example, you may wish to bundle jQuery and some other libraries all together in one file. There is a load-time and caching trade-off here, but often it is the case that you'd like to override some of these settings.To do this, the DSL supports an "overrides" clause, within which you can change the This will put all code into a single bundle named 'monolith'. Note that this can still result in multiple files, as separate bundles are required for head and defer dispositions, and JavaScript and CSS files are bundled separately.Note that overriding individual resources requires the original declaration to have included a unique id for the resource.For full details of the resource DSL please see the resources plugin documentation.
因为一个资源模块定义了捆绑(bundle)组和资源的其他属性,因此你可能会发现设置所提供的并不适合你的应用。比如,你可能希望将jQuery和其他的库捆绑到一个文件中。此处就要根据加载时间和缓存做一个权衡,但是在此种情况下,你经常会想重载这些配置的一部分。这时候,DSL提供了"overrides"子句来完成此功能,子句内你可以修改一个模块的defaultBundle setting for a module, or attributes of individual resources that have been declared with a unique id:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'monolith' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js'
resource url: '/css/main.css',
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery'
defaultBundle 'monolith' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils'
defaultBundle 'monolith' resource url: '/css/forms.css'
resource url: '/js/forms.js'
} overrides {
jquery {
defaultBundle 'monolith'
}
}
}defaultBundle,或者每个单独资源的属性,不过每个资源必须要声明一个唯一的id:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'monolith' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js'
resource url: '/css/main.css',
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery'
defaultBundle 'monolith' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils'
defaultBundle 'monolith' resource url: '/css/forms.css'
resource url: '/js/forms.js'
} overrides {
jquery {
defaultBundle 'monolith'
}
}
}6.2.5.5 优化资源
The Resources framework uses "mappers" to mutate the resources into the final format served to the user.The resource mappers are applied to each static resource once, in a specific order. You can create your own resource mappers, and several plugins provide some already for zipping, caching and minifying.Out of the box, the Resources plugin provides bundling of resources into fewer files, which is achieved with a few mappers that also perform CSS re-writing to handle when your CSS files are moved into a bundle.
资源框架使用"映射器(mappers)"来将资源转变为最终用户所需的格式。资源映射器以一个特定的顺序将每一个静态资源处理一次。你可以创建你自己的资源映射器,有一些插件已经提供了比如压缩(zipping)、缓存(caching)和缩少(minifying)等映射。除此之外,资源插件还提供了捆绑多个资源到较少的文件功能,在将你的CSS文件移动到一个捆绑束的时候,其使用一些映射器执行重写CSS处理。Bundling multiple resources into fewer files
The 'bundle' mapper operates by default on any resource with a "bundle" defined - or inherited from adefaultBundle clause on the module. Modules have an implicit default bundle name the same as the name of the module.Files of the same kind will be aggregated into this bundle file. Bundles operate across module boundaries:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'common' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js', bundle: 'ui'
resource url: '/css/main.css', bundle: 'theme'
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js', bundle: 'common'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css', bundle: 'ui'
resource url: '/js/forms.js', bundle: 'ui'
}
}捆绑多个资源到较少的文件
缺省情况下,'bundle'映射器会操作使用"bundle"定义的任何资源-或者继承自模块的defaultBundle子句。模块有一个隐含的跟模块名称相同的缺省捆绑束名。同样类型的文件将会被汇集到当前的捆绑束文件中。捆绑束是通过模块的边界来操作的:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'common' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js', bundle: 'ui'
resource url: '/css/main.css', bundle: 'theme'
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js', bundle: 'common'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css', bundle: 'ui'
resource url: '/js/forms.js', bundle: 'ui'
}
}Making resources cache "eternally" in the client browser
Caching resources "eternally" in the client is only viable if the resource has a unique name that changes whenever the contents change, and requires caching headers to be set on the response.The cached-resources plugin provides a mapper that achieves this by hashing your files and renaming them based on this hash. It also sets the caching headers on every response for those resources. To use, simply install the cached-resources plugin.Note that the caching headers can only be set if your resources are being served by your application. If you have another server serving the static content from your app (e.g. Apache HTTPD), configure it to send caching headers. Alternatively you can configure it to request and proxy the resources from your container.让资源“永久”地缓存在客户浏览器
在客户端“永久”地缓存资源只有在资源有一个唯一的名字的情况下,才切实可行,并且当资源的内容变化时,其名字也要做相应的变化,还要求在响应中设置缓存标头(caching headers)。cached-resources插件提供了一个映射器来完成此功能,它是通过对你的文件做哈稀校验并根据校验值来重命名来实现的。此插件也会在每一次的响应中根据这些资源的情况来设置缓存标头。要使用它,只需要简单的安装cached-resources插件即可。注意!只有在你的应用管辖范围内的资源,才会有可能设置缓存标头.如果你有另外一个服务器专门管理你应用的静态资源(比如Apache的HTTPD),那么需要配置此服务器来发送缓存标头。或者你也可以配置它来请求和代理你容器内的资源。Zipping resources
Returning gzipped resources is another way to reduce page load times and reduce bandwidth.The zipped-resources plugin provides a mapper that automatically compresses your content, excluding by default already compressed formats such as gif, jpeg and png.Simply install the zipped-resources plugin and it works.压缩资源
返回用gzip压缩过的资源是另外减少页面加载时间和带宽的方法。zipped-resources插件提供了一个映射器来自动地压缩你的资源内容。当然那些已经压缩过地除外,比如gif、jpeg和png。简单地安装zipped-resources插件后,即可工作。Minifying
There are a number of CSS and JavaScript minifiers available to obfuscate and reduce the size of your code. At the time of writing none are publicly released but releases are imminent.缩少资源
已经有很多的CSS和JavaScript缩少器可以用来混淆和减少你代码的大小。这可以解决发布迫在眉睫,而现在编码时没有什么可公开发布的情况。6.2.5.6 调试
When your resources are being moved around, renamed and otherwise mutated, it can be hard to debug client-side issues. Modern browsers, especially Safari, Chrome and Firefox have excellent tools that let you view all the resources requested by a page, including the headers and other information about them.There are several debugging features built in to the Resources framework.
当你的资源正在移动、重命名以及其他变动的时候,要调试客户端的问题是非常困难的。现代的浏览器,尤其是Safari、Chrome和Firefox,都有非常优秀的工具来查看一个请求页面的所有资源,包括其请求头和其他的信息。除此之外,Resources框架还提供了几个内置的调试特性。X-Grails-Resources-Original-Src Header
Every resource served in development mode will have the X-Grails-Resources-Original-Src: header added, indicating the original source file(s) that make up the response.X-Grails-Resources-Original-Src信息头
在开发模式下,每一个用到的资源都会添加X-Grails-Resources-Original-Src的头信息,用以表示此响应对应的原始代码文件。Adding the debug flag
If you add a query parameter _debugResources=y to your URL and request the page, Resources will bypass any processing so that you can see your original source files.This also adds a unique timestamp to all your resource URLs, to defeat any caching that browsers may use. This means that you should always see your very latest code when you reload the page.添加调试标记
如果在你的URL和请求页面的参数中增加 _debugResources=y 的话,Resources将会不管任何处理,而直接显示和使用原始的代码文件。此外,你资源的URLs还会添加一个唯一的时间戳,用以处理浏览器导致的缓存问题。这意味着在你重现加载页面的时候,你总是得到最新的代码。Turning on debug all the time
You can turn on the aforementioned debug mechanism without requiring a query parameter, but turning it on in Config.groovy:grails.resources.debug = true打开调试
你可以在不需要额外请求参数的情况下打开如上所述的调试机制,要如此,只要在Config.groovy中配置一下即可:grails.resources.debug = true6.2.5.7 阻止资源处理
Sometimes you do not want a resource to be processed in a particular way, or even at all. Occasionally you may also want to disable all resource mapping.
有时候,你并不想以一种特别的方式处理资源,甚至根本就不想。偶尔,你还想禁止所有的资源映射。Preventing the application of a specific mapper to an individual resource
All resource declarations support a convention of noXXXX:true where XXXX is a mapper name.So for example to prevent the "hashandcache" mapper from being applied to a resource (which renames and moves it, potentially breaking relative links written in JavaScript code), you would do this:modules = {
forms {
resource url: '/css/forms.css', nohashandcache: true
resource url: '/js/forms.js', nohashandcache: true
}
}阻止到一个单独资源的特定映射
所有的资源声明都支持noXXXX:true的用法,此处的XXXX是一个映射器的名称。因此在下例中,要阻止"hashandcache"映射器应用到一个资源(重命名,移动甚至断开JavaScript代码中的相关链接)你可以这样做:modules = {
forms {
resource url: '/css/forms.css', nohashandcache: true
resource url: '/js/forms.js', nohashandcache: true
}
}Excluding/including paths and file types from specific mappers
Mappers have includes/excludes Ant patterns to control whether they apply to a given resource. Mappers set sensible defaults for these based on their activity, for example the zipped-resources plugin's "zip" mapper is set to exclude images by default.You can configure this in yourConfig.groovy using the mapper name e.g:// We wouldn't link to .exe files using Resources but for the sake of example: grails.resources.zip.excludes = ['**/*.zip', '**/*.exe']// Perhaps for some reason we want to prevent bundling on "less" CSS files: grails.resources.bundle.excludes = ['**/*.less']
从特定映射器中 排除/包含 路径和文件类型
映射器的排除/包含使用Ant语法来控制是否要应用到给定的资源上。映射器会根据其活动情况来设置缺省的感知类型,以资源压缩(zipped-resources)插件为例,其"zip"映射器会缺省地排除那些镜像文件。你可以通过你的Config.groovy文件地映射器名称来配置相关信息,比如:// We wouldn't link to .exe files using Resources but for the sake of example: grails.resources.zip.excludes = ['**/*.zip', '**/*.exe']// Perhaps for some reason we want to prevent bundling on "less" CSS files: grails.resources.bundle.excludes = ['**/*.less']
Controlling what is treated as an "ad-hoc" (legacy) resource
Ad-hoc resources are those undeclared, but linked to directly in your application without using the Grails or Resources linking tags (resource, img or external).These may occur with some legacy plugins or code with hardcoded paths in.There is a Config.groovy setting grails.resources.adhoc.patterns which defines a list of Servlet API compliant filter URI mappings, which the Resources filter will use to detect such "ad-hoc resource" requests.By default this is set to:grails.resources.adhoc.patterns = ['images/*', '*.js', '*.css']
控制"ad-hoc"(遗留)资源
Ad-hoc资源是那些未声明的,并且 不 使用Grails或者Resources的链接标签(resource, img or external),而是在你的应用中直接链接的资源。这可能会在那些遗留插件或者硬编码路径的时候会碰到。Config.groovy中的 grails.resources.adhoc.patterns 配置就是用来定义一系列Servlet API兼容的URI映射的过滤器,其资源过滤器通常用来检测那些"ad-hoc resource"请求。其缺省值如下:grails.resources.adhoc.patterns = ['images/*', '*.js', '*.css']
6.2.5.8 其他资源感知的插件
At the time of writing, the following plugins include support for the Resources framework:
截至到书写为止,资源框架已经被下列插件所支撑:
6.2.6 Sitemesh的内容块
Although it is useful to decorate an entire page sometimes you may find the need to decorate independent sections of your site. To do this you can use content blocks. To get started, partition the page to be decorated using the Then within the layout you can reference these components and apply individual layouts to each:
虽然装饰整个页面是有用的,但有时候你可能只需要装饰站点单独的部分。这时,你可以使用内容块来完成。首先,对于要装饰的页面部分使用<content> tag:<content tag="navbar"> … draw the navbar here… </content><content tag="header"> … draw the header here… </content><content tag="footer"> … draw the footer here… </content><content tag="body"> … draw the body here… </content>
<html> <body> <div id="header"> <g:applyLayout name="headerLayout"> <g:pageProperty name="page.header" /> </g:applyLayout> </div> <div id="nav"> <g:applyLayout name="navLayout"> <g:pageProperty name="page.navbar" /> </g:applyLayout> </div> <div id="body"> <g:applyLayout name="bodyLayout"> <g:pageProperty name="page.body" /> </g:applyLayout> </div> <div id="footer"> <g:applyLayout name="footerLayout"> <g:pageProperty name="page.footer" /> </g:applyLayout> </div> </body> </html>
<content>标签来处理:<content tag="navbar"> … draw the navbar here… </content><content tag="header"> … draw the header here… </content><content tag="footer"> … draw the footer here… </content><content tag="body"> … draw the body here… </content>
<html> <body> <div id="header"> <g:applyLayout name="headerLayout"> <g:pageProperty name="page.header" /> </g:applyLayout> </div> <div id="nav"> <g:applyLayout name="navLayout"> <g:pageProperty name="page.navbar" /> </g:applyLayout> </div> <div id="body"> <g:applyLayout name="bodyLayout"> <g:pageProperty name="page.body" /> </g:applyLayout> </div> <div id="footer"> <g:applyLayout name="footerLayout"> <g:pageProperty name="page.footer" /> </g:applyLayout> </div> </body> </html>
6.2.7 修改已经部署的应用
One of the main issues with deploying a Grails application (or typically any servlet-based one) is that any change to the views requires that you redeploy your whole application. If all you want to do is fix a typo on a page, or change an image link, it can seem like a lot of unnecessary work. For such simple requirements, Grails does have a solution: the
The first line tells Grails that modified GSP files should be reloaded at runtime. If you don't have this setting, you can make as many changes as you like but they won't be reflected in the running application until you restart. The second line tells Grails where to load the views and layouts from.
The key point here is that you must retain the view directory structure, including the
GSP reloading is supported for precompiled GSPs since Grails 1.3.5 .
部署一个Grails应用(或者任意基于servlet的应用)的一个主要问题是视图的任何修改都需要重新再部署你的整个应用。假如你只是想修复一个页面的打字错误或者修改一个图像链接,那么这种再部署像是比较多余的工作。对于这种比较简单的需求,Grails提供了一个解决方案:配置grails.gsp.view.dir configuration setting.How does this work? The first step is to decide where the GSP files should go. Let's say we want to keep them unpacked in a /var/www/grails/my-app directory. We add these two lines to grails-app/conf/Config.groovy :
grails.gsp.enable.reload = true grails.gsp.view.dir = "/var/www/grails/my-app/"
The trailing slash on the grails.gsp.view.dir value is important! Without it, Grails will look for views in the parent directory.
Setting "grails.gsp.view.dir" is optional. If it's not specified, you can update files directly to the application server's deployed war directory. Depending on the application server, these files might get overwritten when the server is restarted. Most application servers support "exploded war deployment" which is recommended in this case.With those settings in place, all you need to do is copy the views from your web application to the external directory. On a Unix-like system, this would look something like this:
mkdir -p /var/www/grails/my-app/grails-app/views cp -R grails-app/views/* /var/www/grails/my-app/grails-app/views
grails-app/views bit. So you end up with the path /var/www/grails/my-app/grails-app/views/... .One thing to bear in mind with this technique is that every time you modify a GSP, it uses up permgen space. So at some point you will eventually hit "out of permgen space" errors unless you restart the server. So this technique is not recommended for frequent or large changes to the views.There are also some System properties to control GSP reloading:
| Name | Description | Default |
|---|---|---|
| grails.gsp.enable.reload | altervative system property for enabling the GSP reload mode without changing Config.groovy | |
| grails.gsp.reload.interval | interval between checking the lastmodified time of the gsp source file, unit is milliseconds | 5000 |
| grails.gsp.reload.granularity | the number of milliseconds leeway to give before deciding a file is out of date. this is needed because different roundings usually cause a 1000ms difference in lastmodified times | 1000 |
grails.gsp.view.dir属性。那么它是如何工作的呢?第一步就是要确定GSP文件位于什么地方。假设我们想让这些文件解压缩到/var/www/grails/my-app目录,那么我们需要在grails-app/conf/Config.groovy增加如下两行:
grails.gsp.enable.reload = true grails.gsp.view.dir = "/var/www/grails/my-app/"
grails.gsp.view.dir值的最后一个反斜杠是很重要的!没有它,Grails将会在其上一级目录寻找视图。
"grails.gsp.view.dir"的值是可选的。如果没有设置,你可以直接更新部署在应用服务器的war目录下的文件。这些文件可能会在应用服务器重新启动的时候被覆盖,不过这是跟服务器相关的。在这个时候,大部分的应用服务器所支持的“war额外加载部署(exploded war deployment)”模式是值得推荐的。所有这些设置完毕以后,你所需要做的就是从你的web应用中拷贝视图文件到外部的目录中。在一个Unix类的系统中,这可能看起来如下所示:
mkdir -p /var/www/grails/my-app/grails-app/views cp -R grails-app/views/* /var/www/grails/my-app/grails-app/views
grails-app/views本身。因此你的路径是/var/www/grails/my-app/grails-app/views/...的形式。使用此技术,要牢记的一件事情是在你每一次修改GSP文件的时候,会增加permgen的内存空间。因此最终你将会碰到"permgen内存空间益"的错误,当然你可以通过重新启动服务器来解决。所以,此技术不推荐应用于视图被频繁或者大量修改的情况。此外还有一些系统级的属性配置来控制GSP的重新加载:
| 名称 | 描述 | 缺省值 |
|---|---|---|
| grails.gsp.enable.reload | 在不修改Config.groovy的情况下,通过系统设置变量来启动GSP重栽模式 | |
| grails.gsp.reload.interval | 轮询gsp源文件最后修改时间的时间间隔,单位是毫秒 | 5000 |
| grails.gsp.reload.granularity | 在一个文件超时以前预留的毫秒数,此项是需要的,因为最后修改时间精度会导致1000毫秒的误差 | 1000 |
6.2.8 GSP调试
Viewing the generated source code
- Adding "?showSource=true" or "&showSource=true" to the url shows the generated Groovy source code for the view instead of rendering it. It won't show the source code of included templates. This only works in development mode
- The saving of all generated source code can be activated by setting the property "grails.views.gsp.keepgenerateddir" (in Config.groovy) . It must point to a directory that exists and is writable.
- During "grails war" gsp pre-compilation, the generated source code is stored in grails.project.work.dir/gspcompile (usually in ~/.grails/(grails_version)/projects/(project name)/gspcompile).
Debugging GSP code with a debugger
Viewing information about templates used to render a single url
GSP templates are reused in large web applications by using theg:render taglib. Several small templates can be used to render a single page.
It might be hard to find out what GSP template actually renders the html seen in the result.
The debug templates -feature adds html comments to the output. The comments contain debug information about gsp templates used to render the page.Usage is simple: append "?debugTemplates" or "&debugTemplates" to the url and view the source of the result in your browser.
"debugTemplates" is restricted to development mode. It won't work in production.Here is an example of comments added by debugTemplates :
<!-- GSP #2 START template: /home/.../views/_carousel.gsp
precompiled: false lastmodified: … -->
.
.
.
<!-- GSP #2 END template: /home/.../views/_carousel.gsp
rendering time: 115 ms -->查看生成的源代码
- 在url中增加"?showSource=true"或者"&showSource=true"来显示生成的用于查看的Groovy源代码。它将不会显示包含模板的源代码,并且只工作于开发模式。
- 要保存所有生成的源代码,可以通过配置"grails.views.gsp.keepgenerateddir"(在Config.groovy中)来完成。指向的目录必须存在而且可写。
- 在"grails war"的gsp预编译阶段,其生成的源代码被保存在grails.project.work.dir/gspcompile中(通常位于~/.grails/(grails_version)/projects/(project name)/gspcompile中)。
在调试器中调试GSP代码
- 详细请参考 在STS中调试GSP
查看渲染成一个url的模板信息
在大型的WEB应用中,GSP的模板可以通过使用g:render标签而得以复用。几个小模板可以被渲染到一个单独的页面中。
在最后渲染的html中,很难区分出那些是那个GSP模板被实际渲染到那里。
调试模板功能将会在输出中添加html注释。这些注释包含着关于gsp模板渲染的调试信息。用法也很简单:添加"?debugTemplates"或者"&debugTemplates"到url中,然后查看你浏览器中的源代码。
"debugTemplates"仅限于开发模式,在生产环境中将无效。下面是增加了debugTemplates后的一个带有注释的示例:
<!-- GSP #2 START template: /home/.../views/_carousel.gsp
precompiled: false lastmodified: … -->
.
.
.
<!-- GSP #2 END template: /home/.../views/_carousel.gsp
rendering time: 115 ms -->
