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

来源:developerWorks 中国 作者:Scott Davis
  


清单 13. 简化的局部模板
				
<div class="entry">
  <span class="entry-date">
      <g:longDate>${entryInstance.lastUpdated}</g:longDate> : ${entryInstance.author}
  </span>
  <h2><g:link action="show" id="${entryInstance.id}">${entryInstance.title}</g:link></h2>
  <p>${entryInstance.summary}</p>

  <g:displayFile filename="${entryInstance.filename}"
                 user="${entryInstance.author.login}" />

</div>

重新启动 Grails,并再次上传 Grails 徽标。在添加对其他文件类型的支持之前,应该确保 TagLib 重构没有破坏已有的功能。

现在,可以确信仍可以上传图像。接下来就是添加对其他文件类型的支持,这只需在 switch 块中实现适当的 case。清单 14 演示如何处理上传的 HTML 文件,以及为默认的 case 创建一个链接来下载该文件:


清单 14. 完整的 switch/case 块
				
class EntryTagLib {

  def displayFile = {attrs, body->
    def user = attrs["user"]
    def filename = attrs["filename"]

    if(filename){
      def extension = filename.split("\\.")[-1]
      def userDir = "payload/${user}"

      switch(extension.toUpperCase()){
        case ["JPG", "PNG", "GIF"]:
             //SNIP
             break

        case "HTML":
             def webRootDir = servletContext.getRealPath("/")
             out << new File(webRootDir+"/"+userDir, filename).text
             break
        default:
             def html = """
             <p>
               <a href="${createLinkTo(dir:''+userDir,
                                       file:''+filename)}">${filename}</a>
             </p>
             """
             out << html
             break
      }
    }else{
      out << "<!-- no file -->"
    }
  }

}

创建两个新的文本文件,以便测试这个新的行为:一个名为 test.html,另一个名为 noextension。将清单 15 中的内容添加到适当的文件中,上传该文件,确认 TagLib 是否按预期显示每个文件:


清单 15. 用于上传的两个示例文件
				
//test.html
<p>
This is some <b>test</b> HTML.
</p>

<p>
Here is a link to the <a href="http://grails.org">Grails</a> homepage.
</p>

<p>
And here is a link to the 
<img src="http://grails.org/images/grails-logo.png">Grails Logo</img>.
</p>



//noextension
This file doesn't have an extension.

Web 浏览器看上去应该如图 3 所示:


图 3. 显示所有 3 种类型的上传的文件





添加 Atom feed

至此,您应该明白一种截然不同的模式构成。对于添加到 Grails 应用程序的每个新特性,很可能都要涉及模型、视图和控制器。您还可以额外添加局部模板或 TagLib。

将 Atom feed 添加到 Blogito 同样也遵从该模式。虽然不要求更改模型,但最终要做所有其他的事情。您将:

  1. 在 Entry 控制器中添加一个闭包,以处理 Atom 请求。
  2. 创建一个新的 GSP 页面,用于以格式良好的 Atom 文档的形式呈现结果。
  3. 创建一个新的局部模板和一个新的定制标记,以加快进程。

本来您可以安装一个很好的 Feeds 插件,该插件可以为 Grails 应用程序添加 RSS 和 Atom 功能(请参阅 参考资料),但是我认为您将发现,Atom 格式非常简单,您自己完全可以应付。为证明这一点,您可以查看已有的 Atom feed 的源代码,或者查看关于 Atom 的 Wikipedia 页面最后的例子(请参阅 参考资料)。您甚至可以阅读 RFC 4287,这是用于 Atom 格式的 IETF 规范(请参阅 参考资料)。或者,您可以继续阅读本文,看看一个特定于 Grails 的解决方案。

首先,在 EntryController.groovy 中添加一个 atom 闭包,如清单 16 所示:


清单 16. 在 EntryController.groovy 中添加一个 atom 闭包
				
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
				
<% 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 标记
				
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 局部模板
				
<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>

时间:2009-07-06 17:18 来源:developerWorks 中国 作者:Scott Davis 原文链接

好文,顶一下
(3)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量