首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 文件上传和 Atom 联合(4)

精通 Grails 文件上传和 Atom 联合(4)

添加 Atom feed至此,您应该明白一种截然不同的模式构成。对于添加到 Grails 应用程序的每个新特性,很可能都要涉及模型、视图和控制器。您还可以额外添加局部模板或 TagLib。
将 Atom feed 添加到 Blogito 同样也遵从该模式。虽然不要求更改模型,但最终要做所有其他的事情。您将:
  • 在 Entry 控制器中添加一个闭包,以处理 Atom 请求。
  • 创建一个新的 GSP 页面,用于以格式良好的 Atom 文档的形式呈现结果。
  • 创建一个新的局部模板和一个新的定制标记,以加快进程。
本来您可以安装一个很好的 Feeds 插件,该插件可以为 Grails 应用程序添加 RSS 和 Atom 功能(请参阅 ),但是我认为您将发现,Atom 格式非常简单,您自己完全可以应付。为证明这一点,您可以查看已有的 Atom feed 的源代码,或者查看关于 Atom 的 Wikipedia 页面最后的例子(请参阅 )。您甚至可以阅读 RFC 4287,这是用于 Atom 格式的 IETF 规范(请参阅 )。或者,您可以继续阅读本文,看看一个特定于 Grails 的解决方案。
首先,在 EntryController.groovy 中添加一个 atom 闭包,如清单 16 所示:
清单 16. 在 EntryController.groovy 中添加一个 atom 闭包
1
2
3
4
5
6
def atom = {
  if(!params.max) params.max = 10
  def list = Entry.list( params )
  def lastUpdated = list[0].lastUpdated
  [ entryInstanceList:list, lastUpdated:lastUpdated ]
}




这个闭包与标准的 list 闭包之间惟一的不同是增加了 lastUpdated 字段。由于该列表已经按 lastUpdated 排序(这要归因于 Entry domain 类的 static mapping 块中的 sort "lastUpdated":"desc" 设置),只需从该列表的第一个 Entry 中获取该字段,就可以得到最近的日期。
接下来,创建 grails-app/views/entry/atom.gsp。添加清单 17 中的代码:
清单 17. atom.gsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<% response.setContentType("application/atom+xml")
%><feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">News from Blogito.org</title>
  <link rel="alternate" type="text/html" href="http://blogito.org/"/>
  <link rel="self" type="application/atom+xml" href="http://blogito.org/entry/atom" />
  <updated><g:atomDate>${lastUpdated}</g:atomDate></updated>
  <author><name>Blogito.org</name></author>
  <id>tag:blogito.org,2009-01-01:entry/atom</id>
  <generator uri="http://blogito.org" version="0.1">Hand-rolled Grails code</generator>

  <g:each in="${entryInstanceList}" status="i" var="entryInstance">
<g:render template="atomEntry" bean="${entryInstance}" var="entryInstance" />
  </g:each>

</feed>




可以看到,以上代码做的第一件事是将 MIME 类型设置为 application/atom+xml。然后,提供关于该 feed 的一些基本的元数据:updated、author 和 generator 等。
如果想避免在整个 feed 中硬编码 blogito.org,那么可以让 atom 闭包获取 request.serverName,将它赋给一个变量,并在响应 hashmap 中返回它,同时返回的还有 entryInstanceList 和 lastUpdated。为了完全动态化,可以使用 request.scheme 返回 http,并使用 request.serverPort 返回 80。(唯一要避免使用 request.serverName 变量的地方是在 id 中,稍后我将讨论到这一点)。
对于 Atom feed 来说,以多种不同的格式提供链接并不少见。从 type 属性可以看出,该 feed 提供两个链接:一个是 HTML 链接,另一个是 Atom 格式的指向它本身的链接。self 链接特别有用;如果有一个不是自己下载的 Atom 文档,那么通过该链接就可以回溯到规范来源。
id 字段是 Atom feed 的惟一标识符,它不同于 URI 或可下载该 Atom feed 的当前位置。(您刚才已经知道,<link> 元素提供 feed 的当前来源)。在这个例子中,我使用 Mark Pilgrim 提供的技术生成一个惟一的、永久的 ID 字符串:将域名、feed 初次进入服务的日期和 URI 剩下的部分组合到一起。(要了解更多信息,请参阅 )。
id 的各个部分远不如整个字符串的惟一性重要。应确保这个 id 以后不会因为无意中传入来自控制器的变量而变化 — 对于 feed id,它应该既是惟一的,又是不变的。即使服务器的 address 发生变化,如果 feed 的内容不变,那么 feed id 也应该保持不变。
更新后的字段应该符合特定的格式 — 2003-12-13T18:30:02Z,或者确切地说是 RFC 3339。(要了解详细信息,请参阅 )。在已有的 grails-app/taglib/DateTagLib.groovy 文件中添加一个 atomDate 闭包,如清单 18 所示:
清单 18. 添加 atomDate 标记
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.text.SimpleDateFormat

class DateTagLib {
  public static final String INCOMING_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss"
  public static final String ATOM_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'-07:00'"

  def atomDate = {attrs, body ->
    def b = attrs.body ?: body()
    def d = new SimpleDateFormat(INCOMING_DATE_FORMAT).parse(b)
    out << new SimpleDateFormat(ATOM_DATE_FORMAT).format(d)
  }

  //SNIP
}




为了完成 Atom feed,创建 grails-app/views/entry/_atomEntry.gsp,并添加清单 19 中的代码:
清单 19. _atomEntry.gsp 局部模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<entry xmlns='http://www.w3.org/2005/Atom'>
  <author>
    <name>${entryInstance.author.name}</name>
  </author>
  <published><g:atomDate>${entryInstance.dateCreated}</g:atomDate></published>
  <updated><g:atomDate>${entryInstance.lastUpdated}</g:atomDate></updated>
  <link href="http://blogito.org/blog/${entryInstance.author.login}/
    ${entryInstance.title.encodeAsUnderscore()}" rel="alternate"
    title="${entryInstance.title}" type="text/html" />
  <id>tag:blogito.org,2009:/blog/${entryInstance.author.login}/
    ${entryInstance.title.encodeAsUnderscore()}</id>
  <title type="text">${entryInstance.title}</title>
  <content type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml">
      ${entryInstance.summary}
    </div>
  </content>
</entry>




最后需要做的是向未经认证的用户开放 Atom feed。调整 EntryController.groovy 中的 beforeInterceptor,如清单 20 所示:
清单 20. 向未经认证的用户开放 Atom feed
1
2
3
4
5
6
class EntryController {

  def beforeInterceptor = [action:this.&auth, except:["index", "list", "show", "atom"]]

  //SNIP
}

返回列表