wash

#

Kid Language Specification

Kid is a simple XML based template language that uses embedded Python to do cool stuff. The syntax was inspired by a number of existing template languages, namely XSLT, TAL, and PHP.

This document describes the template language and will be most useful as reference to those developing Kid templates. For information about using templates from Python, the command line, or in web environments, see the User's Guide.

1   Synopsis

<?python
title = "A Kid Test Document"
fruits = ["apple", "orange", "kiwi", "M&M"]
from platform import system
?>
<html xmlns:py="http://purl.org/kid/ns#">
  <head>
    <title py:content="title">This is replaced.</title>
  </head>
  <body>
    <p>These are some of my favorite fruits:</p>
    <ul>
      <li py:for="fruit in fruits">
        I like ${fruit}s
      </li>
    </ul>
    <p py:if="system() == 'Linux'">
      Good for you!
    </p>
  </body>
</html>

Yields something like this:

<?xml version="1.0" encoding="utf-8"?>
<html>
  <head>
    <title>A Kid Test Document</title>
  </head>
  <body>
    <p>These are some of my favorite fruits:</p>
    <ul>
      <li>I like apples</li>
      <li>I like oranges</li>
      <li>I like kiwis</li>
      <li>I like M&amp;Ms</li>
    </ul>
    <p>
      Good for you!
    </p>
  </body>
</html>

2   The Kid Namespace

All attributes described in this document must belong to the following namespace:

http://purl.org/kid/ns#

The namespace prefix py is used throughout this document to indicate that an item belongs to the Kid/Python namespace.

3   Embedding Code Blocks (<?python?>)

The <?python?> processing instruction (PI) contains Python code and MAY occur anywhere that is legal for processing instructions to occur in an XML document.

The rules for executing code found in a <?python?> PI is as follows:

  1. <?python?> PIs located outside of the document element (e.g. root element) contain Document Level code. This code SHOULD be executed in a global, shared scope for the document. The code SHOULD be executed once when the template is loaded and shared between multiple invocations of the template.
  2. <?python?> PIs located within the document element contain Local Level code. This code is executed each time the document is processed with a local scope specific to the invocation and the shared document level global scope.

Document Level and Local Level code work exactly like Module Level and Function Level code in normal Python modules. For example, the following Kid template:

<?python
x = 0
y = 0
?>
<html xmlns:py="http://purl.org/kid/ns#">
  <?python
  x = 1
  if x == 1:
    x = 10
  ?>
  <p py:content="x"/>
  <?python
  global y
  y = 30
  ?>
  <p py:content="y"/>
</html>

May be considered equivalent to the following Python module:

x = 0
y = 0
def expand(handler):
  handler.startDocument()
  handler.startElement('html')
  x = 1
  if x == 1:
    x = 10
  handler.element('p', content=x) # output p element with x as value
  global y
  y = 30
  handler.element('p', content=y) # output p element with value of y
  handler.endElement('html')
  handler.endDocument()

<?python?> PIs may contain any legal Python language construct including functions, classes, lamda forms, etc.

<?python
class Adder:
  def __init__(self, x, y):
    self.x = x
    self.y = y
  def doit(self):
    return self.x + self.y

foo = Adder(x=400, y=20)
x = foo.doit()
?>

Single line <?python?> PIs are okay too:

<?python x = 10 ?>

4   Content Producing Constructs

There are multiple methods of generating content output from a template: py:content, py:replace, py:attrs, and ${} substitution. Each of these syntaxes have the same rules for what types of objects may result from the Python expression they contain.

str, unicode
The string is inserted as XML CDATA. That is, it is non-parsable character data that does not contain markup. The following characters are encoded as XML entities when serialized: '<', '&'. Attribute values containing content also encode the quote character: '"'.
ElementTree.Element

When an ElementTree.Element is referenced from a content producing construct, the item is inserted into the document literally, i.e. it is not encoded as text, but becomes part of the output structure.

The XML() and document() functions can be used to turn a string into structured content and to retrieve an XML document from a URL, respectively.

Note that attribute values MUST NOT reference structured content. This applies to py:attrs and using ${} substitution in attribute values.

sequence
If a sequence type (list, tuple, or other iterable) is referenced, the rules are applied to each of the items in the sequence. For example, you could reference a list containing an Element and a string.
Other
If the result of evaluating the expression is any other type, an attempt is made to coerce the value to unicode as if by calling unicode(expr) and processing continues as if the object were a string or unicode object initially.

5   Python Expression Substitution (${expr})

Attributes not belonging to the Kid namespace and text content MAY embed Python expressions by enclosing the expression within a dollar sign followed by curly braces: ${expr}. The result of evaluating the expression(s) is substituted with the rest of the attribute value or text content following rules defined for Content Producing Constructs.

<?python
verb = 'ran'
noun = 'store'
?>
<a title="I ${verb} to the ${noun}">...

... would result in:

<a title="I ran to the store">...

If an attribute value consists purely of substitution expressions and all expressions evaluate to None, the attribute is removed. This can be avoided by using expr or '' to force a zero length string to be returned instead of None. For example:

<?python
# set something to None
x = None
?>
<a title="${x}">...

... would result in:

<a>...

However, this:

<?python x = None?>
<a title="${x or ''}">...

... results in:

<a title="">...

5.1   Identifier Shortcut ($name)

For simple expressions consisting entirely variable names and object access operators (.), the curly braces may be omitted:

<a href="http://example.com/$page" title="$title">
   Dots are allowed too: $object.another.attribute
</a>

However, it is good practice to use the curly brace form as it sets the substitution off from the other text a bit more providing a stronger visual clue as to what's going on.

5.2   Escaping ($$)

$$ is an escape. $${bla} will output ${bla}.

6   Default Imports

All templates have a few default imports for convenience.

6.1   XML() function

Expression substitution, py:content, and py:replace encode strings as text. That is, text is encoded according to the rules of the XML specification, which includes, among other things, replacing the literal characters < and & with their encoded counterparts (&lt; &amp;). If you have XML stored as a string and want it to be output as XML and not encoded text, you need to pass the string to the XML function.

For example, let's say there is a function, hello, that returns XML data that should be embedded in template output (let's say it returns <hello>world</hello>). Consider the following:

<p>${hello()}</p>

The result would be:

<p>&lt;hello>world&lt;hello></p>

Calling the XML function would have given us the result we intended:

<p>${XML(hello())}</p>
<p><hello>world</hello></p>

6.2   document() function

The document function loads an XML document from a file or URL allowing it to be embedded in template output:

<div py:content="document('header.html')"></div>

The document function resolves paths relative to the current template file (if the template location is available).

7   Attribute Language

7.1   Repetition/Iteration (py:for)

<element py:for="target_list in expression_list" />

Works exactly like the Python for statement.

The py:for attribute may appear on any element to signify that the element should be processed multiple times, once for each value in the sequence specified:

<?python
bottles = range(1, 101)
bottles.reverse()
?>
<p py:for="num in bottles">
   <span py:content="num">X</span> bottles of beer on the wall,
   <span py:content="num">X</span> bottles of beer on the wall,
   take one down, pass it around, <span py:content="num - 1">X - 1</span>
   bottles of beer on the wall.
</p>

The py:for attribute is the first attribute to be processed if present. All other py: attributes are processed for each iteration of the loop.

7.2   Conditionals (py:if)

<element py:if="expr" />

The py:if attribute may appear on any element to signify that the element and its decendant items should be output only if the boolean expression specified evaluates to true in Python:

<p py:if="5 * 5 == 25">
  Python seems to be handling multiplication okay.
</p>

The py:if attribute is processed after the py:for attribute and is evaluated for each iteration. If the result of evaluating expr as a boolean expression is false in Python, no further py: attributes are processed for the current iteration or, if not in a py:for, at all.

Note

Evaluated as a boolean expression in Python, None, False, [], (), {}, 0, and '' are all considered to be false.

7.3   Dynamic Content (py:content)

<element py:content="expr" />

This attribute MAY appear on any element to signify that the decendant items of the element are to be replaced with the result of evaluating expr.

<p py:content="time.strftime('%C %c')">The Time</p>

Results in:

<p>Tues, Jun 26, 2004 02:03:53 AM</p>

py:content is a Content Producing Construct and can output both character and structured data.

7.4   Replacing Content (py:replace)

<element py:replace='expr' />

py:replace is shorthand for specifying a py:content and a py:strip="True" on the same element:

<?python
x = 10
?>
<p><span py:replace="x">...</span></p>

... results in:

<p>10</p>

... and is equivelant to specifying:

<?python #
x = 10
?>
<p><span py:strip="" py:content="x">...</span></p>

The py:replace attribute is processed after the py:for and py:if attributes. py:strip and py:content attributes are not processed and are discarded.

py:replace is a Content Producing Construct and can output both character and structured data.

7.5   Stripping Tags (py:strip)

<element py:strip="expr" />

The py:strip attribute may apppear on any element to signify that the containing element should not be output. If the attribute value is blank (no expr at all) or if the result expr is a boolean expression that evaluates to true, the element is not output, but all descendant elements are processed normally. If expr is not blank and the result of evaluating expr as a boolean expression is false, processing continues as if the attribute did not exist.

The py:strip attribute MAY appear on an element with any other kid attribute. However, if both a py:replace and a py:strip exist on the same element, the py:strip attribute is ignored and discarded.

The py:strip attribute is processed after the py:for and py:if attributes. If omission is eminent, the py:content attribute is processed normally but attribute interpolation does not occur.

7.6   Dynamic Attributes (py:attrs)

<element py:attrs="expr" />

The py:attrs attribute may appear on any element to specify a set of attributes that should be set on the element when it is processed. The expression specified MUST evaluate to one of the following types of values:

dict
A dictionary with keys specifying attribute names and values specifying attribute values. These are added to the attributes of the current element by calling element.attrib.update(mapping), where element is an ElementTree Element object and mapping is the dictionary returned from the expression. Outer curly braces are not necessary to write down.
list
A list of tuples of the form (name, value) is also acceptable. Each item of the list is added to the current set of attributes by iterating over the list and calling element.set(name, value).
keyword arguments
The attributes can also be specified as comma separated keyword arguments of the form name=value.

The following lines:

<elem py:attrs="{'a':1, 'ns:b':2}" />
<elem py:attrs="'a':1, 'ns:b':2" />
<elem py:attrs="(('a',1), ('ns:b',2))" />
<elem py:attrs="a=1, ns:b=2" />

will all produce the same output:

<elem a="1" ns:b="2" />

Note that attributes whose values are None will be removed. If a blank attribute is desired, an empty string should be used.

If the expression specified is an empty dictionary or an empty list, the attributes are not modified in any way.

py:attrs is a Content Producing Construct, but can output only character data.

7.7   Named Template Functions (py:def)

<element py:def="template_name(arg_list)" />

The py:def attribute may appear on any element to create a "Named Template Function". Markup contained within an py:def element is not output during normal template expansion but can be referenced from other Content Producing Constructs to insert the markup at the point referenced.

Like normal Python functions, Named Template Functions have an optional argument list that may use all of the jazzy features of Python argument lists like variable and keyword arguments.

Named Template Functions are invoked exactly like normal Python functions. They are generally invoked from Content Producing Constructs like py:content or ${} substitution.

<ul py:def="display_list(seq)">
   <li py:for="item in seq" py:content="item" />
</ul>

<table py:def="display_dict(mapping)">
   <tr>
       <th>Key</th>
       <th>Value</th>
   </tr>
   <tr py:for="key, value in mapping.items()">
       <td py:content="key" />
       <td py:content="value" />
   </tr>
</table>

Here we've defined two Named Template Functions: display_list and display_dict. The first function takes a sequence and the second a mapping. We can invoke these functions from the same template by invoking them from a content producing construct:

<body>
   ${display_list(['apple', 'orange', 'kiwi'])}

   <div py:replace="display_dict({'x' : 'y', 'p' : 'q'})">
    Key/Value Table replaces this text
   </div>
</body>

7.8   Match Templates (py:match)

<element py:match="expr" />

The py:match attribute may appear on any element to create a "Match Template". Markup contained within a Match Template element is not output during normal template expansion. Instead, these constructs set up filters for expansion output that are capable of transforming content as it is generated.

Match Templates are generally used to insert content dynamically based on patterns in template expansion or to provide "custom tag" functionality similar to that found in JSP taglibs or XSLT.

A Match Template has two parts: the match expression part (expr) and the body part (the element and it's descendants).

Match Templates are processed as follows:

  1. Each element that is output from a template goes through the Match Template Filter.
  2. The Match Template Filter visits each of the Match Templates defined in the current template and the templates the current template extends in the order that they are defined and evaluates the associated match expression.
  3. If the match expression returns true as a boolean expression, the match template's body is expanded and replaces the original element and all of its descendants.

In both the match expression and in the match template's body, the item name is bound to the Element that is being output. However, there are some limitations to what can be accessed at each phase:

  1. During match expression evaluation, only the item Element and none of its descendants are available. This means that match expressions are limited to testing matches based on the immediate Element's tag and attributes [1].
  2. During match template expansion (that is, when the match expression is true), the element's descendants are available and may be referenced from Content Producing Constructs to output bits and pieces of the matched items structure.
[1] This is due to the streaming nature of the Kid processor. During normal template expansion, the entire tree is never fully retained in memory.

7.8.1   Example

The following simple example shows how to create a custom tag <greeting> that outputs one of two provided values based on the time of day the template is expanded:

<?xml version="1.0" encoding="utf-8"?>
<?python
from time import localtime
def timeofday():
    """Get time of day ('am' or 'pm')"""
    return localtime().tm_hour < 12 and 'am' or 'pm'
?>
<html xmlns:py="http://purl.org/kid/ns#">
  <!-- define the greeting match template -->
  <span py:match="item.tag == 'greeting'"
        py:replace="item.get(timeofday())">
  </span>

  <head>
    <title>Time of day demo</title>
  </head>
  <body>
    <p>
      Good <greeting am="Morning!" pm="Afternoon" />
    </p>
  </body>
</html>

An important thing to note is that the py:match expression and the match template body have access to the <greeting> element via the variable item. The item.get(timeofday()) bit retrieves the value of the am attribute or the pm attribute based on what is returned from the timeofday function.

At 9:00 AM, output from this template would look like this:

<html>
  <head>
    <title>Time of day demo</title>
  </head>
  <body>
    <p>
      Good Morning!
    </p>
  </body>
</html>

The obvious question at this point is how to reuse Match Templates? The example above demonstrates the use of a Match Template from the same main template but it is often desirable to have "libraries" of Match Templates that could be used by multiple individual templates. The answer is to have the main template extend a common template containing the Match Templates needed. We can rewrite the above example as two separate templates: main.kid and common.kid.

The common template would look like this:

<?xml version="1.0" encoding="utf-8"?>
<?python
from time import localtime
def timeofday():
    """Get time of day ('am' or 'pm')"""
    return localtime().tm_hour < 12 and 'am' or 'pm'
?>
<html xmlns:py="http://purl.org/kid/ns#">
  <!-- define the greeting match template -->
  <span py:match="item.tag == 'greeting'"
        py:replace="item.get(timeofday())">
  </span>
</html>

And the main template would look like this:

<?xml version="1.0" encoding="utf-8"?>
<html py:extends="'common.kid'">
  <head>
    <title>Time of day demo</title>
  </head>
  <body>
    <p>
      Good <greeting am="Morning!" pm="Afternoon" />
    </p>
  </body>
</html>

When a template extends another template (or set of templates), all of the Match Templates and Named Template Functions of the extended templates are available as if they were defined locally.

Warning

Match templates are an experimental feature. Syntax and semantics may change significantly or be removed entirely in future release. Actually, this statement applies to many aspects of Kid but this one is especially unstable.

7.9   Template Reuse (py:extends)

<root py:extends="template1, template2, ...">

The py:extends attribute may appear on the root element to specify that the template should inherit the Named Template Functions and Match Templates defined in another template (or set of templates). If a py:extends attribute is specified, it MUST be on the root element of the document.

The py:extends may contain a list of Python expressions separated by commas that reference templates. The rules for what types of values may be specified are:

string

The name of a template file, relative to the current template file.

Example:

<html py:extends="'common.kid'" />
module or Template class

The py:extends variable references a module or a Template class. If a module is referenced, an attempt is made to find a class named Template belonging to the that module.

Example:

<?python
import common
?>
<html py:extends="common" ...

Multiple templates may be referenced by separating each by a comma. The following example references templates common and forms, imported using the import hooks and a template filename named other.kid:

<?python
import common, forms
?>
<html py:extends="common, forms, 'other.kid'" ...

7.9.1   Example

For example, there is a template named common.kid that defines a template function, display_errors, and a match template that converts <b> elements to <strong> elements with uppercase content:

<html xmlns:py="http://purl.org/kid/ns#">

  <ul py:def="display_errors(errors)">
    <li py:for="error in errors" py:content="error" />
  </ul>

  <strong py:match="item.tag == 'b'"
    py:content="item.text.upper()" />

</html>

The functions and match templates may be imported into another template by referencing them with py:extends:

<html py:extends="'common.kid'"
      xmlns:py="http://purl.org/kid/ns#">
  <head>
    <title>Errors</title>
  </head>
  <body>
    <p>The following <b>errors</b> were found:</p>
    ${ display_errors(["Field is required", "Must be phone number.."]) }
  </body>
</html>

The <b>errors</b> item is transformed to <strong>ERRORS</strong> and the error list is displayed. Both the match template and the named template function are available in the derived template as if they were defined locally.

8   Processing Order

The order that py: attributes are processed is as follows:

  1. py:def
  2. py:match
  3. py:for
  4. py:if
  5. py:replace
  6. py:strip
  7. py:attrs
  8. py:content

Attribute substitution occurs after all other

posted @ 2005-12-26 16:11 wash 阅读(165) | 评论 (0)编辑 收藏

cherrypy documentation

     摘要: . Application developer reference AbstractCherryPy lets developers use Python to develop web applications, just as they would use Python for any other type of application. Building a web appli...  阅读全文

posted @ 2005-12-26 09:02 wash 阅读(1377) | 评论 (0)编辑 收藏

turbo gears get start

     摘要: Your model objects represent the data that your application is working with. Your controller pulls in information coming from the user's web browser. It uses that information to update information i...  阅读全文

posted @ 2005-12-21 09:24 wash 阅读(258) | 评论 (0)编辑 收藏

webworks chapter 1 -夏昕Webwork2 Developer’s Guide Version 1.0


ServletDispatcher 接受到Servlet Container 传递过来的请求,将进行一下几个动作:
1. 从请求的服务名(/login.action)中解析出对应的Action名称(login)
2. 遍历HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到
Webwork的Map实现中,至此之后,所有数据操作均在此Map结构中进行,从
而将内部结构与Servlet API相分离。
至此,Webwork 的工作阶段结束,数据将传递给XWork 进行下一步处理。从这里也可以看到Webwork和xwork之间的切分点,Webwork为xwork提供了一个面向Servlet 的协议转换器,将Servlet 相关的数据转构转换成xwork所需要的通用数据格式,而xwork将完成实际的服务调度和功能实现。
这样一来,以xwork为核心,只需替换外围的协议转换组件,即可实现不同技术平台之间的切换(如将面向Servlet的Webwork替换为面向JMS的协议转换器实现,即可在保留应用逻辑实现的情况下,实现不同外部技术平台之间的移植)。
3. 以上述信息作为参数,调用ActionProxyFactory创建对应的ActionProxy实例。
ActionProxyFactory 将根据Xwork 配置文件(xwork.xml)中的设定,创建
ActionProxy实例,ActionProxy中包含了Action的配置信息(包括Action名称,
对应实现类等等)。
4. ActionProxy创建对应的Action实例,并根据配置进行一系列的处理程序。包括
执行相应的预处理程序(如通过Interceptor 将Map 中的请求数据转换为Action
所需要的Java 输入数据对象等),以及对Action 运行结果进行后处理。
ActionInvocation 是这一过程的调度者。而com.opensymphony.xwork.
DefaultActionInvocation 则是XWork 中对ActionInvocation 接口的标准实现,如
果有精力可以对此类进行仔细研读,掌握了这里面的玄机,相信XWork的引擎
就不再神秘。
下面我们来看配置文件:
xwork.xml:
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<
xwork>
<include file="webwork-default.xml" /> ⑴
<package name="default" extends="webwork-default"> ⑵
<action name="login" ⑶
class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher"> ⑷
<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">
<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="params" /> ⑸
<interceptor-ref name="model-driven"/> ⑹
</action>
</package>
</xwork>
⑴ include
通过include 节点,我们可以将其他配置文件导入到默认配置文件xwork.xml 中。
从而实现良好的配置划分。
这里我们导入了Webwork 提供的默认配置webwork-default.xml(位于
webwork.jar 的根路径)。
⑵ package
XWork中,可以通过package对action进行分组。类似Java 中package和class的
关系。为可能出现的同名Action提供了命名空间上的隔离。
同时,package还支持继承关系。在这里的定义中,我们可以看到:
extends="webwork-default"
"webwork-default"是webwork-default.xml文件中定义的package,这里通
过继承,"default" package 自动拥有"webwork-default" package 中的所有
定义关系。这个特性为我们的配置带来了极大便利。在实际开发过程中,我们可以根据自身的应用特点,定义相应的package模板,并在各个项目中加以重用,无需再在重复繁琐的配置过程中消耗精力和时间。
此外,我们还可以在Package节点中指定namespace,将我们的action分为若干个
逻辑区间。如:
<package name="default" namespace="/user"
extends="webwork-default">
就将此package中的action定义划归为/user 区间,之后在页面调用action的时候,
我们需要用/user/login.action 作为form action 的属性值。其中的/user/就指定了此
action的namespace,通过这样的机制,我们可以将系统内的action进行逻辑分类,
从而使得各模块之间的划分更加清晰。
⑶ action
Action配置节点,这里可以设定Action的名称和对应实现类。
⑷ result
通过result 节点,可以定义Action 返回语义,即根据返回值,决定处理模式以及
响应界面。
这里,返回值"success"(Action 调用返回值为String 类型)对应的处理模式为
"dispatcher"。
可选的处理模式还有:
1. dispatcher
本系统页面间转向。类似forward。
2. redirect
浏览器跳转。可转向其他系统页面。
3. chain
将处理结果转交给另外一个Action处理,以实现Action的链式处理。
4. velocity
将指定的velocity模板作为结果呈现界面。
5. xslt
将指定的XSLT 作为结果呈现界面。
随后的param节点则设定了相匹配的资源名称。
⑷ interceptor-ref
设定了施加于此Action的拦截器(interceptor)。关于拦截器,请参见稍后的“XWork拦截器体系”部。
interceptor-ref定义的是一个拦截器的应用,具体的拦截器设定,实际上是继
承于webwork-default package,我们可以在webwork-default.xml 中找到
对应的"params"和"model-driven"拦截器设置:
<interceptors>
……
<interceptor name="params"class="com.opensymphony.xwork.interceptor.ParametersInt
erceptor" />
<interceptor name="model-driven"
class="com.opensymphony.xwork.interceptor.ModelDrivenIn
terceptor" />
……
</interceptors>
"params"大概是Webwork 中最重要、也最常用的一个Interceptor。上面曾经将
MVC工作流程划分为几个步骤,其中的第一步:
“将Web 页面中的输入元素封装为一个(请求)数据对象”
就是通过"params"拦截器完成。Interceptor 将在Action 之前被调用,因而,
Interceptor 也成为将Webwork传来的MAP 格式的数据转换为强类型Java 对象的
最佳实现场所。
"model-driven"则是针对Action 的Model驱动模式的interceptor 实现。具体描
述请参见稍后的“Action驱动模式”部分很可能我们的Action 都需要对这两个interceptor 进行引用。我们可以定义一个interceptor-stack,将其作为一个interceptor 组合在所有Action 中引用。如,上面的配置文件可修改为:
<xwork>
<include file="webwork-default.xml" />
<package name="default" extends="webwork-default">
<interceptors>
<interceptor-stack name="modelParamsStack">
<interceptor-ref name="params" />
<interceptor-ref name="model-driven" />
</interceptor-stack>
</interceptors>
<action name="login" class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher">
<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">
<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="modelParamsStack" />
</action>
</package>
</xwork>
通过引入interceptor-stack,我们可以减少interceptor 的重复申明。
下面是我们的Model对象:
LoginInfo.java:
public class LoginInfo {
private String password;
private String username;
private List messages = new ArrayList();
private String errorMessage;
public List getMessages() {
return messages;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
很简单,这只是一个纯粹的值对象(Value-Object)。这里,它扮演着模型(Model)的角色,并与Action的输入输出密切相关。与Spring MVC中的Command对象不同,Webwork 中的Model对象,扮演着承上启下的角色,它既是Action的输入参数,又包含了Action处理的结果数据。
换句话说,输入的Http请求参数,将被存储在Model对象传递给Action进行处理,Action处理完毕之后,也将结果数据放置到Model 对象中,之后,Model 对象与返回界面融合生成最后的反馈页面。也正由于此,笔者建议在实际开发中采用Model-Driven 模式,而非Property-Driven 模式(见稍后“Action驱动模式”部分),这将使得业务逻辑更加清晰可读。
对应的Action代码
public class LoginAction implements Action, ModelDriven {
private final static String LOGIN_FAIL="loginfail";
LoginInfo loginInfo = new LoginInfo();
public String execute() throws Exception {
if ("erica".equalsIgnoreCase(loginInfo.getUsername())
&& "mypass".equals(loginInfo.getPassword())) {
//将当前登录的用户名保存到Session
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
//出于演示目的,通过硬编码增加通知消息以供显示
loginInfo.getMessages().add("message1");
loginInfo.getMessages().add("message2");
loginInfo.getMessages().add("message3");
return SUCCESS;
}else{
loginInfo.setErrorMessage("Username/Password Error!");
return LOGIN_FAIL;
}
}
public Object getModel() {
return loginInfo;
}
}
可以看到,LoginAction实现了两个接口:
1. Action
Action接口非常简单,它指定了Action的入口方法(execute),并定义了
几个默认的返回值常量:
public interface Action extends Serializable {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}
private final static String LOGIN_FAIL="loginfail";
LoginInfo loginInfo = new LoginInfo();
public String execute() throws Exception {
if ("erica".equalsIgnoreCase(loginInfo.getUsername())
&& "mypass".equals(loginInfo.getPassword())) {
//将当前登录的用户名保存到Session
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
//出于演示目的,通过硬编码增加通知消息以供显示
loginInfo.getMessages().add("message1");
loginInfo.getMessages().add("message2");
loginInfo.getMessages().add("message3");
return SUCCESS;
}else{
loginInfo.setErrorMessage("Username/Password Error!");
return LOGIN_FAIL;
}
}
public Object getModel() {
return loginInfo;
}
}
可以看到,LoginAction实现了两个接口:
1. Action
Action接口非常简单,它指定了Action的入口方法(execute),并定义了
几个默认的返回值常量:
public interface Action extends Serializable {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}
SUCCESS、NONE、ERROR、INPUT、LOGIN 几个字符串常量定义了常用的
几类返回值。我们可以在Action 实现中定义自己的返回类型,如本例中的
LOGIN_FAIL定义。
而execute方法,则是Action的入口方法,XWork将调用每个Action的execute
方法以完成业务逻辑处理。
2. ModelDriven
ModelDriven接口更为简洁:
public interface ModelDriven {
Object getModel();
}
ModelDriven仅仅定义了一个getModel方法。XWork在调度Action时,将通
过此方法获取Model 对象实例,并根据请求参数为其设定属性值。而此后的
页面返回过程中,XWork 也将调用此方法获取Model 对象实例并将其与设定
的返回界面相融合。
注意这里与Spring MVC 不同,Spring MVC 会自动为逻辑处理单元创建
Command Class实例,但Webwork不会自动为Action创建Model对象实例,
Model 对象实例的创建需要我们在Action 代码中完成(如LoginAction 中
LoginInfo对象实例的创建)。
另外,如代码注释中所描述,登录成功之后,我们随即将username保存在Session之中,这也是大多数登录操作必不可少的一个操作过程。
这里面牵涉到了Webwork中的一个重要组成部分:ActionContext。
ActionContext为Action提供了与容器交互的途径。对于Web 应用而言,与容器的交互大多集中在Session、Parameter,通过ActionContext我们在代码中实现与Servlet API无关的容器交互。
如上面代码中的:
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
同样,我们也可以操作Parameter:
ActionContext ctx = ActionContext.getContext();
Map params = ctx.getParameters();
String username = ctx.getParameters("username");
上述的操作,将由XWork根据当前环境,调用容器相关的访问组件(Web 应用对应的就是Webwork)完成。上面的ActionContext.getSession(),XWork 实际上将通过Webwork提供的容器访问代码“HttpServletRequest.getSession()”完成。
注意到,ActionContext.getSession返回的是一个Map类型的数据对象,而非HttpSession。这是由于WebWork对HttpSession进行了转换,使其转变为与Servlet API无关的Map对象。通过这样的方式,保证了Xwork 所面向的是一个通用的开放结构。从而使得逻辑层与表现层无关。增加了代码重用的可能。此外, 为了提供与Web 容器直接交互的可能。WebWork 还提供了一个ServletActionContext实现。它扩展了ActionContext,提供了直接面向Servlet API的容器访问机制。
我们可以直接通过ServletActionContext.getRequest 得到当前HttpServletRequest 对象的引用,从而直接与Web 容器交互。
获得如此灵活性的代价就是,我们的代码从此与ServletAPI 紧密耦合,之后系统在不同平台之间移植就将面临更多的挑战(同时单元测试也难于进行)。
平台移植的需求并不是每个应用都具备。大部分系统在设计阶段就已经确定其运行平台,且无太多变更的可能。不过,如果条件允许,尽量通过ActionContext 与容器交互,而不是平台相关的ServletActionContext,这样在顺利实现功能的同时,也获得了平台迁移上的潜在优势,何乐而不为。
登录成功界面:
main.jsp:
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<p align="center">Login Success!</p>
<p align="center">Welcome!
<ww:property value="#session['username']"/>
</p>
<p align="center">
<b>Messages:</b><br>
<ww:iterator value="messages" status="index">
<ww:if test="#index.odd == true">
!<ww:property/><br>
</ww:if>
<ww:else>
*<ww:property/><br>
</ww:else>
</ww:iterator>
</p>
</body>
</html>
这里我们引入了Webwork的taglib,如页面代码第一行的申明语句。
下面主要使用了三个tag:
Ø <ww:property value="#session['username']"/>
读取Model对象的属性填充到当前位置。
value指定了需要读取的Model对象的属性名。
这里我们引用了LoginAction在session中保存的’username’对象。
由于对应的Model(LoginInfo)中也保存了username 属性。下面的语句与之
效果相同:
<ww:property value="username"/>
与JSP2 中的EL类似,对于级联对象,这里我们也可以通过“.”操作符获得
其属性值,如value="user.username"将得到Model 对象中所引用的user
对象的username 属性(假设LoginInfo中包含一个User 对象,并拥有一个名
为“username”的属性)。
关于EL的内容比较简单,本文就不再单独开辟章节进行探讨。
Webwork中包括以下几种特殊的EL表达式:
² parameter[‘username’] 相当于request.getParameter(“username”);
² request[‘username’] 相当于request.getAttribute(“username”);
² session[‘username’] 从session中取出以“username”为key的值
² application[‘username’] 从ServletContext中取出以“username”为key
的值
注意需要用“#”操作符引用这些特殊表达式。
另外对于常量,需要用单引号包围,如#session['username'] 中的
'username'。
Ø <ww:iterator value="messages" status="index">
迭代器。用于对java.util.Collection、java.util.Iterator、java.util.Enumeration,、
java.util.Map、Array类型的数据集进行循环处理。
其中,value属性的语义与<ww:property>中一致。
而status属性则指定了循环中的索引变量,在循环中,它将自动递增。
而在下面的<ww:if>中,我们通过“#”操作符引用这个索引变量的值。
索引变量提供了以下几个常用判定方法:
² first 当前是否为首次迭代
² last 当前是否为最后一次迭代
² odd 当前迭代次数是否奇数
² even 当前迭代次数是否偶数
Ø <ww:if test="#index.odd == true">和<ww:else>
用于条件判定。
test属性指定了判定表达式。表达式中可通过“#”操作符对变量进行引用。
表达式的编写语法与java 表达式类似。
类似的,还有<ww:elseif test="……">。

登录失败界面
实际上,这个界面即登录界面index.jsp。只是由于之前出于避免干扰的考虑,隐藏了index.jsp中显示错误信息的部分。
完整的index.jsp如下:
<%@ page pageEncoding="gb2312"
contentType="text/html;charset=gb2312"%>
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<form action="/login.action">
<p align="center">
登录<br>
<ww:if test="errorMessage != null">
<font color="red">
<ww:property value="errorMessage"/>
</font>
</ww:if>
</p>
用户名:
<input type="text" name="model.username" />
<br>
密码:
<input type="password" name="model.password" />
<br>
<p align="center">
<input type="submit" value="提交" name="B1"/>
<input type="reset" value="重置" name="B2"/>
</p>
</form>
</body>
</html>
这里首先我们进行判断,如果Model中的errorMessage不为null,则显示错误信息。这样,在用户第一次登录时,由于Model对象尚未创建,errorMessage自然为null,错误信息不会显示,即得到了与之前的index.jsp同样的效果。

posted @ 2005-10-08 11:23 wash 阅读(537) | 评论 (0)编辑 收藏

仅列出标题
共2页: 上一页 1 2