3.4 在页面海洋中导航
本书的前两章,我们已经涉及了JSF对从一个页面导航到另一个页面的支持。从概念上说,它非常类似于Struts的ActionForward。但是,它没有像“ActionForward”之类的确切名称,所以只是称它为JSF导航系统。
导航系统的核心是导航处理器,它实际上是决定接下来要载入哪个页面的一段代码。本书中,我们都在描述导航处理器的默认行为(也可以替换和装饰导航处理器;见附录C中的详细信息)。导航处理器的运行负责响应动作事件,而动作事件则通常由动作源执行(捕获用户行为操作的组件,如按钮或者超链接)。动作源可以关联到执行某些应用逻辑后返回一个逻辑结果的的动作方法,或者直接硬编码一个逻辑结果值。
导航处理器运行于一套导航规则之上,后者定义了应用中所有可能的导航路径。导航规则定义了对某个特定的页面或者在一组页面中选择哪个页面。到页面的每一个路径被表达为导航案例(navigation case)。导航案例是根据逻辑结果(从组件自身或者与其相关的动作方法中获得)和/或针对被执行动作方法本身的表达式来选定的。
导航规则在应用配置文件中通过 XML来定义。某些IDE 也可以让你可视化地编辑导航规则。图3-13显示的是Sun Java Studio Creator [Sun,Creator] 的可视化导航编辑器。图中显示了三个页面:PlaceOrder.jsp、ConfirmOrder.jsp和OrderEntry.jsp。
图3-13 在Sun Java Studio Creator [Sun,Creator]中编辑页面导航。相应的XML源代码示于代码清单3-6中
这里从PlaceOrder.jsp出发有个导航规则,有两个导航案例:一是在结果为"success"时导航到ConfirmOrder.jsp;而另一个是在结果为"failure"时导航到 OrderEntry.jsp。对ConfirmOrder.jsp则没有导航规则——它就是终点。对OrderError.jsp有一个导航规则,一个案例:如果结果为"retry"则返回 PlaceOrder.jsp 。
在这个工具产生的漂亮图示后,是常规的Faces配置文件以及其中的<navigation-rule>和<navigation-case> 元素配置。图3-14展示了这些元素的结构。代码清单3-6则是定义示于图3-13中的导航规则的XML源代码。
代码清单3-6 示于图3-13之中的导航规则的XML源代码
图3-14 导航由导航规则组成,并在JSF配置文件中通过<navigation-rule>元素来定义。可选地,导航规则可以指定它适用于哪个页面,即通过 <from-view-id> 元素来指定。每个<navigation-rule>具有一个或多个<navigation-case>元素,它们都必须具有<to-view-id>元素来指定下一个要装入的页面。<navigation-case>也可以通过<from-outcome>元素指定结果,或者以<from-action>元素指定动作。如果<redirect>元素被指定,JSF将发送HTTP重定向而不是通过内部转发到新的视图。<navigation-rule>和<navigation-case>元素都具有可选的<description>、<display-name>和<icon>元素,而它们通常为工具所用
在代码清单中,JSP通过<from-view-id>和<to-view-id>元素来引用。每个视图都有一个标识符,即一个简单的文件名。文件名可以是JSP名称,也可以是其他资源的名称,但是必须相对于Web应用的根目录,并且以前缀斜杠(/)开始。对于同一个<from-view-id>值可以有不只一个导航规则;JSF 将会把它们处理为一个大的完整的导航规则。这在应用有多个具有不同导航规则的不同配置文件时,是很有用的。这种情况在项目中有不同的团队分别工作于应用的不同部分的时候,就会出现。
最后一个导航案例指定了<redirect>元素,这告诉JSF发送HTTP重定向到新的视图,这与内部将用户转发到视图是相对的。在JSP中,这意味着用户会看到她/他当前正在访问的页面的URL,这与前一个访问页面的URL是不同的(记住,JSF页面会为自己回送)。<redirect>元素也强制对新视图的请求由Web容器处理,这意味着它可以被其他应用中可能有的其他应用逻辑来处理(类似于servlet过滤器)。
还应注意到,结果设置并不对应到特定的资源,它们只是逻辑结果。这很关键,因为我们并不需要将结果关联到特定的资源。如果以这种方式使用结果,就非常容易地改变结果所对应的实际资源,而不用改变结果自身的意义(即是说不需要改变Java代码)。表3-8显示了一些通用的结果字符串。
表3-8 常用结果字符串
结 果 | 含 义 |
success | 操作成功完成。转移到下一个逻辑页面 |
failure | 操作没有成功完成。显示一个页面以告诉用户为何失败,以及他们应该为此做些什么 |
error | 发生了某种系统错误。显示一个系统错误页面 |
no results | 没有匹配用户查询的记录。再次显示查询页面 |
login | 用户需要首先登录。显示登录页面 |
logout | 从应用系统注销。显示注销页面 |
定单示例有三个运行结果:"success"、"failure"和"retry"。前两个是动态的,它们从动作方法返回。OrderEntry.jsp有个HtmlCommandButton组件:
用户点击这个按钮,placeOrder方法被执行,而该方法返回字符串"success"或者"failure"。动作方法也可以返回null;如果这样,当前的页面只是重新显示而已(关于如何编写动作方法,见第13章)。
与"success"和"failure"不同,"retry"结果是静态的,所以它根本用不着由动作方法产生。它是硬编码到OrderError.jsp 页面中的:
用户点击这个按钮的结果是不变的——它总是"retry"。
即便这里有两个结果是动态的,一个结果是静态的,导航规则其实并没有区分它们。这意味着你可以在不改变规则的情况下,将动态结果改为静态,反之亦然。
如果想要确保一个结果是由特定的动作方法产生,你可以通过将 <from-action> 元素嵌入 <navigationcase>元素中来完成:
这个案例不会被执行,除非"success"结果是由orderManager.placeOrder动作方法产生。这在两个不同的动作方法产生相同的结果字符串时会有用。如果你想要导航案例只倚赖于特定的动作方法的名字,甚至可以忽略<from-outcome>元素:
这个案例说明只要动作方法orderManager.placeOrder被执行,视图ConfirmOrder.jsp就会被载入。
到此为止,我们所讨论的导航规则都是针对特定页面的。你也可以定义针对所有页面的导航规则(就像Struts的全局ActionForward一样),或者针对一组页面的导航规则。如果<from- view-id>元素的值定义为一个星号,则表示该规则是全局的:
因为例子中 <from-view-id> 元素的值是一个星号,这个规则适用于所有页面。无论何时,只要结果是"login",页面login.jsp 都将被装入;而只要结果是"logout",页面 logout.jsp都会被装入。我们也可以通过一起忽略 <from-view-id> 元素来达到同样的效果。
指定一组页面,需要在<from-view-id> 元素中使用通配符(在除字符串之外其他地方不支持星号通配符)。所以,如果想要匹配所有以"FrogLegs"开头的页面,则元素的值应该是"FrogLegs*"。如果有多个规则都以"FrogLegs"开始,JSF将选择最长的那个。
更常用的做法是将规则对应到整个目录:
这样就指定了,一个适用于上下文相对目录"corporate"下的所有页面的导航规则。这些导航案例都引用位于同一个目录下的页面,但这并不是必需的。
提示 如果应用有许多导航规则,将它们放置到一个单独的应用配置文件而不是在主配置文件中,这样是很合理的。
前面我们所示的所有例子,都略去了几个可以用于导航规则和导航案例的可选元素。它们是 <description>、<display-name>和<icon>元素。下面是添加这些元素后的全局导航规则定义:
这里,我们添加了说明、显示名称以及一个图标到导航规则中。也添加了说明和显示名称到每个导航案例中(导航案例也可以有图标,但是这里并没有指定)。<description>元素可用于与团队中的其他人员交流导航规则的目的(或者晚些时候你再看这些设置时,显示给你自己)。其他元素则通常用于可视化编辑导航规则。
提示 给导航规则一个说明是个好主意,这样其他开发人员也可以理解其目的。
到这里我们便结束了JSF导航的轻快之旅。在第二部分和第三部分我们构建案例时,你可以看到更多例子。