WebObjects/Web 应用程序/开发/生成静态页面
上周 WWDC 上,一位观众在 WebObjects 会议上问到如何使用 WebObjects 从动态网站创建静态页面。据我回忆,Bill Bumgarner 回答了这个问题。这是在提出任何类型的动态网站时反复出现的一个问题。曾经,在线 WebObjects 文档的编程主题中包含了如何处理此问题的简要说明,由为 BBC 设置初始网站的苹果员工编写;该说明没有在网站的多次清除中幸存下来 - 这很好,因为它非常简短和肤浅。
从动态引擎创建静态网站有很多原因;BBC 选择这样做是为了担心 WebObjects 的性能。更合理的理由是使用 html 作为便携的文档格式;例如,创建在 CD 上分发的公司形象手册。我参与过几个这样做的项目,包括上面提到的两个。
我将完全跳过如何在 WebObjects 中创建动态页面,而专注于如何在页面级组件中创建静态页面。动态内容是一个很容易解决的问题,而且它被多次解决。创建静态页面的问题可以清楚地分为两部分:明显的部分是如何将页面写入 html 文件,这通常被称为“爬取”;不那么明显的部分是,所有页面链接都必须以某种方式从 WebObjects 动态操作转换为静态 URL。
首先,您需要一个机制来访问网站上的每个页面。我通常为此目的在管理应用程序中创建一个操作。我将有一个数据库结构,其中包含要写入的每个页面的记录(企业对象)。这将包含页面子级的关系以及父级的关系。顶层页面可以通过它没有父级来识别;如果需要,此结构也可以扩展为表示单个数据库中的多个网站或顶层页面。我的页面或节点 EO 将有一个方法来将自身写入;要写入的文件系统位置将根据节点路径到当前节点计算,使用 EO 中的文件系统名称来标记路径。
我在组件中使用以下方法来写入页面,该方法从爬取代码中调用;节点是表示页面或节点的 EO,htmlPath 是一个明显的方法。
public void writeToHtml() { WOResponse r = null; String c = null; try { File folder = new File(NSPathUtilities.stringByDeletingLastPathComponent(node().htmlPath())); folder.mkdirs(); BufferedWriter out = new BufferedWriter(new FileWriter(new File(node().htmlPath()))); r = generateResponse(); if (r == null) NSLog.debug.appendln("writeHtml: no response"); c = r.contentString(); if (c == null) NSLog.debug.appendln("writeHtml: no content"); out.write(c); out.flush(); } catch (IOException e) { System.err.println(e.getMessage()); } catch (Throwable e) { System.err.println("writeToHtml(" + node().htmlPath() + "): " + e.getMessage()); } }
htmlPath() 方法的示例实现如下
public String htmlPath() { return NSPathUtilities.stringByAppendingPathComponent(PLUtilities.defaultForKey(null, "docroot", "/Library/WebServer/Documents").toString(), urlPath()); } public String urlPath() { String fileName = name() + primaryKeyString(this) + ".html"; return "/" + NSPathUtilities.stringByAppendingPathComponent(site().name(), fileName); } static public String primaryKeyString(EOEnterpriseObject object) { NSDictionary dict = EOUtilities.primaryKeyForObject(object.editingContext(), object); String result = ""; java.util.Enumeration enumerator = dict.objectEnumerator(); while (enumerator.hasMoreElements()) { Object anObject = enumerator.nextElement(); result += anObject.toString(); } return result; }
可能需要更智能的实现,根据节点层次结构构建路径字符串,或者从 EO 属性获取文件名等。
此类网站组件中可以使用唯一的操作是 WOHyperlinks;这些可以转换为静态页面引用,而表单操作等则不能。基本假设是所有页面都将从文件系统而不是 Web 服务器提供服务,因此不支持 cgi。理论上,您可以使用 Javascript 来满足页面的任何动态需求。
由于允许网站以动态网站和静态版本运行非常有用,因此我用以下结构替换了所有 WOHyperlinks(来自 wod 文件)
Generic1: WOGenericContainer { elementName = "a"; href = nodeHref; invokeAction = nodeLink; }
这两种方法必须对上下文敏感,并知道何时生成动态的 WOHyperlink 风格链接,何时生成静态链接。在我的管理应用程序中,用于创建和编辑网站,我包含一个根据我们是否正在查看(动态)或生成(静态)网站而设置的标志。
public String nodeHref() { if (sessionViewMode()) return context().componentActionURL(); return aNode.urlPath(); } public CustomTemplate nodeLink() { if (aNode != null) return pageWithName(aNode.name()); }
对 pageWithName() 的调用从节点 EO 中获取组件名称;同样,这可以用更智能的调用来替换,该调用在组件中设置值等。
通用容器使用 <A> 标记构建一个 href,该 href 将包含适当的静态 url,或者根据 sessionViewMode() 返回的值完全充当 WOHyperlink。