appfuse文档(六)--添加校验和菜单

类别:Java 点击:0 评论:0 推荐:

添加校验和菜单

本章将向你展示怎样为personForm添加校验逻辑,使得firstName和lastName在页面上变成必填field,并且添加一个屏幕列表来显示数据库中所有人的记录。

这部分内容依赖于Part III: 创建Action和JSP

关于本章

本章将向你展示怎样使用Struts的Validator 为你的PersonForm对象添加校验逻辑(客户端和服务器端)。我们也会使用Display Tag Library 创建一个屏幕列表来显示数据库中所有的people。

内容

·   [1] 为Person.java 添加XDoclet Validator标签

·   [2] 检查和测试添加了校验的JSP

·   [3] 为 DAO和Manager Test添加testGetPeople方法

·   [4] 为DAO和Manager添加getPeople()方法

·   [5] 为Action Test 添加testSearch() 方法

·   [6] 为Action添加search方法

·   [7] 创建personList.jsp和Canoo test

·   [8] 为菜单(Menu)添加链接(Link)

Person.java 添加XDoclet Validator标签

正常情况下,如果你想使用Struts的Validator,你必须用手写validation.xml文件。如果你不使用AppFuse, 你也必须在ApplicationResources_en.properties文件中配置Validator Plugin和error key。如果你想获得更多的信息,请参见Validation Made Easy Tutorial

我们要感谢Xdoclet,它使得这一切都变得很简单 – 你只需要为Person类添加几个@struts.validator标签。打开它 (src/dao/**/model/Person.java),然后修改getFirstName()getLastName() 方法的JavaDoc,使它们包含 @struts.validator type="required" 标签。

    /**
     * @struts.validator type="required"
     * @hibernate.property column="first_name" length="50"
     */
    public String getFirstName() {
        return this.firstName;
    }

    /**
     * @struts.validator type="required" 
     * @hibernate.property column="last_name" length="50"
     */
    public String getLastName() {
        return this.lastName;
    }

你还可以在这个标签里添加一个msgkey属性( attribute),它用来覆盖错误的默认消息的key。

@struts.validator type="required" msgkey="errors.required"

type="required"的默认的Key已经是errors.required了,所以我经常保留它做为默认值。这个key被定义在web/WEB-INF/classes/ApplicationResources_*.properties中。 你也许注意到:虽然XDoclet的文档 说应该把这些标签放在Setter部分,但我们放到了getter部分,这是因为模板文件(metadata/template/struts_form.xdt) 会将这些标签放在产生的PersonForm.javaSetter部分。

现在如果你保存Person.java,然后运行ant clean webdoclet,将会在目录build/你的项目名称/WEB-INF/下产生一个validation.xml文件. 它应该有一个为"personForm"使用的入口:

      <form name="personForm">
              <field property="firstName"
                     depends="required">

                  <arg0 key="personForm.firstName"/>
              </field>
              <field property="lastName"
                     depends="required">

                  <arg0 key="personForm.lastName"/>
              </field>
      </form>

在我们的PersonForm.jsp中可以使用客户端校验,我们在PersonForm.jsp文件的底部添加一个<html:javascript>标签和一些脚本。下面的内容是已经存在的(感谢viewgen) – 你只需要将它们从注释中放开,它们被注释的原因是:如果指定了一个Form的名字并且校验规则不存在,Validator将会抛出一个异常。

我个人认为这是一个 a bug ,但Struts的提交者们并不这样认为.

<html:javascript formName="personForm" cdata="false"

    dynamicJavascript="true" staticJavascript="false"/>

<script type="text/javascript"

    src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

检查和测试添加了校验的JSP

现在你已经为你的Form配置了Validation,当这个Form在action-mapping中被配置后,并且设置了validate="true",这些校验规则将会生效。在上一章中, 我们为PersonAction添加"savePerson" action-mapping。这个action-mapping 的Xdoclet标签应该是这样的:

 * @struts.action name="personForm" path="/savePerson" scope="request"
 *  validate="true" parameter="method" input="edit"

这样,只要web/pages/personForm.jsp<html:form action="savePerson">,当我们保存窗体时,校验将会生效。运行ant db-load deploy,启动Tomcat,然后浏览http://localhost:8080/appfuse/editPerson.html?id=1 。如果你删除了firstName和lastName field的值,然后单击save按钮,你应该会看到下面的JavaScript提示的警告:

如果你确定一切都真正的如同期望的那样工作后,你就可以关掉JavaScript以保证可以使用服务器端的校验可以正常使用。在 Mozilla Firebird (我喜欢的浏览器)中,这些很容易,只是到Tools → Options → Web Features,然后去掉"Enable JavaScript"。现在如果你清除field的值,然后保存,你会看到如下的界面:

如果你看不到上面的错误,那么可能你出现了下面的问题:

·   窗体显示保存成功,但firstName和lastName field是空的。

这是因为web/pages/personForm.jsp 中的<html:form>action="editPerson" – 你要改为action="savePerson"

·   你单击了save,但出现了一个空白页面(blank page)。

你的"savePerson" forward的"input" attribute配置不正确,一定要保证它指向了一个local或者global action-forward。在这个例子中,它应该是 input="edit"(指向.personDetail tile的定义)。根据我的经验,input的值必须是一个forward,不能是一个指向action的path

如果你只想使用服务器端校验(server-side validation(没有JavaScript)) ,你可以删除<html:form>的onsubmit属性(在 web/pages/personForm.jsp) ,同时也要删除页面底部的Validator JavaScript标签:

<html:javascript formName="personForm" cdata="false"
      dynamicJavascript="true" staticJavascript="false"/>
<script type="text/javascript" 
      src="<html:rewrite page="/scripts/validator.jsp"/>"></script>

DAOManager Test添加testGetPeople方法

为了创建一个List screen (也叫做master screen), 我们需要创建一个方法,这个方法将返回person 表中的所有记录,我们先为类PersonDAOTest和PersonManagerTest添加测试方法。我通常将这个方法命名为getEntities (如: getUsers),但你也可以使用getAll或者search这样的名字 – 这只是个人喜好问题。

打开test/dao/**/dao/PersonDAOTest.java然后添加testGetPeople方法:

    public void testGetPeople() {
        person = new Person();
        List results = dao.getPeople(person);
        assertTrue(results.size() > 0);
    }

我把一个person对象传入getPeople的原因是将来可以使用person的某个(些)属性进行过滤。 是否在getPeople()方法中添加这个参数是可选的,但本篇文章在剩余部分都假设你已经这样做了。

现在打开 test/service/**/service/PersonManagerTest.java,然后添加 testGetPeople 方法:

    public void testGetPeople() {
        List results = mgr.getPeople(new Person());
        assertTrue(results.size() > 0);
    }

为了使这些测试类能够被编译通过, 你需要为类PersonDAO和接口PersonManager添加getPeople()方法,然后实现它们。

DAOManager添加getPeople()方法

打开src/dao/**/dao/PersonDAO.java,然后添加getPeople()方法声明(method signature):

    public List getPeople(Person person);

现在为src/service/**/service/PersonManager.java添加同样的方法声明。保存你的文件,然后为你的测试文件添加必要的import。下一步我们需要在我们的实现类中实现getPeople() 方法。 打开 src/dao/**/dao/hibernate/PersonDAOHibernate.java,然后添加下面的方法:

    public List getPeople(Person person) {
        return getHibernateTemplate().find("from Person");
    }

你可能注意到这里没有用person参数做任何事情. 现在它只是一个占位符 – 将来你可能需要使用Hibernate's Query Language (HQL) or using Criteria Queries 来过滤它的属性值。

下面是一个使用 Criteria Query的例子:

    Example example = Example.create(person)
                             .excludeZeroes()    // exclude zero valued properties
                             .ignoreCase();      // perform case insensitive string comparisons
    try {
        return getSession().createCriteria(Person.class)
                           .add(example)
                           .list();
    } catch (Exception e) {
        throw new DataAccessException(e.getMessage());
    }
    return new ArrayList();

现在我们在src/service/**/impl/PersonManagerImpl.java 中实现getPeople() 方法:

    public List getPeople(Person person) {
        return dao.getPeople(person);
    }

保存你的修改,下面的命令应该都可以执行:

·   ant test-dao -Dtestcase=PersonDAO

·   ant test-service -Dtestcase=PersonManager

如果一切都工作正常 – 那真是漂亮的工作! 现在你需要为Web层添加这个retrieve all 功能。

Action Test 添加testSearch() 方法

打开test/web/**/action/PersonActionTest.java,然后添加如下方法:

    public void testSearch() {
        setRequestPathInfo("/editPerson");
        addRequestParameter("method", "Search");
        actionPerform();

        verifyForward("list");

        assertNotNull(getRequest().getAttribute(Constants.PERSON_LIST));
        verifyNoActionErrors();
    }

只有你在src/dao/**/Constants.java 文件中添加了PERSON_LIST变量后,这个类才能编译通过。

我一般会复制一个已经存在的类似的变量 – 如:USER_LIST.

    /**
     * The request scope attribute that holds the person list
     */
    public static final String PERSON_LIST = "personList";

保存你修改的文件,因为PersonAction.search() 方法不存在,所以你还不能运行ant test-web -Dtestcase=PersonAction

Action添加search方法

打开src/web/**/action/PersonAction.java,然后在文件顶部添加Xdoclet标签- 为list screen添加forward。

 * @struts.action-forward name="list" path="/WEB-INF/pages/PersonFormList.jsp"

现在在PersonAction类的主题部分添加search方法。

我使用UserAction.search()作为这个方法的模板。

    public ActionForward search(ActionMapping mapping, ActionForm form,
                                HttpServletRequest request,
                                HttpServletResponse response)
            throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Entering 'search' method");
        }

        PersonManager mgr = (PersonManager) getBean("personManager");
        List people = mgr.getPeople(null);
        request.setAttribute(Constants.PERSON_LIST, people);

        // return a forward to the person list definition
        return mapping.findForward("list");
    }

运行ant test-web -Dtestcase=PersonAction.

多漂亮啊!

BUILD SUCCESSFUL
Total time: 1 minute 26 seconds

创建personFormList.jspPersonListjspCanoo test

在目录web/pages 下应该已经有一个PersonFormList.jsp(PersonList.Jsp)文件了,如果没有你可以使用viewgen创建它。使用命令行,进入到目录extras/viewgen下,运行ant -Dform.name=PersonForm。这将会在目录extras/viewgen/build下产生一个PersonFormList.jsp(PersonList.Jsp)文件。

在目录web/pages下找到PersonFormList.jsp(PersonList.Jsp),打开。

这个文件的头部是一个 <bean:struts>标签,它将edit screen的 forward作为一个page范围的变量。这个"editPerson"应该已经有一个值了。(This should already have a value of "editPerson")

<%-- For linking to edit screen --%>
<bean:struts id="editURL" forward="editPerson"/>

将下面内容添加到metadata/web/global-forwards.xml,也是一个列表的视图。这样,它们就可以被包含到struts-config.xml文件中了。

(Add this to the metadata/web/global-forwards.xml, as well as one for viewing the list. This way, they will get included in our struts-config.xml file.)

        <forward name="editPerson" path="/editPerson.html"/>
        <forward name="viewPeople" path="/editPerson.html?method=Search"/>

你用来创建这个JSP的模板应该有一个对应硬编码的id属性的列。所以Xdoclet会把它添加2次。从PersonFormList.jsp(PersonList.Jsp)文件中删除下面的内容。

    <display:column property="id" sort="true" headerClass="sortable"
        titleKey="personForm.id"/>

如果你知道更改 extras/viewgen/src/List_jsp.xdt文件能够不包含这个列标签的办法,请告诉我。

你可能想去改变的另一件事情是:这个例子中产生的名字是"persons" ,它可能是people,在第31行附近,你应该可以看到下面的内容:

<display:setProperty name="paging.banner.items_name" value="persons"/>

更改为:

<display:setProperty name="paging.banner.items_name" value="people"/>

最后,在web/WEB-INF/classes/ApplicationResources_en.properties 文件中添加title 和heading的key (personList.titlepersonList.heading)。打开这个文件,然后添加下面的内容:

# -- person list page --

personList.title=Person List

personList.heading=All People

此时,你应该可以执行ant clean deploy,启动Tomcat,查看这个页面: http://localhost:8080/appfuse/editPerson.html?method=Search

现在我们已经有一个List Screen, 我们来修改这个添加或者删除了一个新Person后显示的页面。打开 src/web/**/action/PersonAction.java,在save, delete和cancel方法中改变mapping.findForward("mainMenu")为下面这样:

    return mapping.findForward("viewPeople");

你也需要改变test/web/**/action/PersonActionTest.java的testRemove方法的 verifyForward("mainMenu") 为verifyForward("viewPeople") 。最后,Canoo test的 "AddPerson"和"DeletePerson"需要被修改。打开test/web/web-tests.xml,然后在"AddPerson" target 部分更改如下内容:

<verifytitle stepid="Main Menu appears if save successful"

    text="${webapp.prefix}${mainMenu.title}"/>

改为:

<verifytitle stepid="Person List appears if save successful"

    text="${webapp.prefix}${personList.title}"/>

然后,在"DeletePerson" target部分,将下面的内容:

<verifytitle stepid="display Main Menu"

    text="${webapp.prefix}$(mainMenu.title)"/>

更改为:

<verifytitle stepid="display Person List" text="${webapp.prefix}${personList.title}"/>

我们使用"viewPeople"代替"list",这样search方法将会被执行。这比使用指向PersonForm.jsp的简单的list (它是一个forward)要好。

我们要测试这个显示页是否工作,在test/web/web-tests.xml中创建一个新的JSP测试:

    <!-- Verify the people list screen displays without errors -->
    <target name="SearchPeople" 
        description="Tests search for and displaying all people">
        <canoo name="searchPeople">
            &config;
            <steps>
                &login;
                <invoke stepid="click View People link" url="/editPerson.html?method=Search"/>
                <verifytitle stepid="we should see the personList title" 
                    text="${webapp.prefix}${personList.title}"/>
            </steps>
        </canoo>
    </target>

我们在"PersonTests" target 中添加"SearchPeople" target,这样它将和其他与person相关的测试用例一起执行。

    <!-- runs person-related tests -->
    <target name="PersonTests" 
        depends="SearchPeople,EditPerson,SavePerson,AddPerson,DeletePerson"
        description="Call and executes all person test cases (targets)">
        <echo>Successfully ran all Person JSP tests!</echo>
    </target>

现在我们可以运行ant test-canoo -Dtestcase=SearchPeople (或者:Tomcat没有运行时,我们执行ant test-jsp) 。

为菜单(Menu)添加链接(Link)

对于这个list的最后一步: 使用户可以看到add, edit和delete功能。最简单的办法是在web/pages/mainMenu.jsp文件中添加一个链接的列表:

NOTE: 不要使用mainMenu.jsp 中的其他的链接,这样可以保证JSP能够被其他的Web框架使用(如:Spring MVC、WebWork)。[The other links in mainMenu.jsp don't use so this JSP can be shared among the various web framework implementations in AppFuse (如:Spring MVC and WebWork).]

    <li>
        <html:link forward="viewPeople">
            <fmt:message key="menu.viewPeople"/>
        </html:link>
    </li>

web/WEB-INF/classes/ApplicationResources_en.properties 中有一个条目: menu.viewPeople

menu.viewPeople=View People

另一种办法是:添加下面的内容到web/WEB-INF/menu-config.xml中:

[The other (more likely) alternative is that you'll want to add it to the menu. To do this, add the following to web/WEB-INF/menu-config.xml:]

<Menu name="PeopleMenu" title="menu.viewPeople" forward="viewPeople"/>

要保证<Menus> tag中有上面的XML代码,而不是在另一个<Menu>中。 然后添加这个心菜单到web/pages/menu.jsp – 现在它看起来应该是这样的:

<%@ include file="/common/taglibs.jsp"%>

<div id="menu">
<menu:useMenuDisplayer name="ListMenu" permissions="rolesAdapter">
    <menu:displayMenu name="AdminMenu"/>
    <menu:displayMenu name="UserMenu"/>
    <menu:displayMenu name="PeopleMenu"/>
    <menu:displayMenu name="FileUpload"/>
    <menu:displayMenu name="FlushCache"/>
    <menu:displayMenu name="Clickstream"/>
</menu:useMenuDisplayer>
</div>

现在你运行ant clean deploy,启动Tomcat,然后查看 http://localhost:8080/appfuse/mainMenu.html ,你应该可以看到下面的内容:

需要注意的是左边多了一个新的链接,它从mainMenu.jsp中获取,在右边的菜单中也多了一个新的链接,它从menu.jsp中获取。

这就是它!

恭喜你,至此,你已经完成了全部的使用AppFuse进行开发的流程!如果你成功的运行了上面的所有的测试用例,那我们现在开始真正的测试。停止Tomcat,运行ant clean test-all. 这将运行所有的工程中的单元测试. 提示:使用ant setup-db setup-tomcat test-all 可以很容易的对我们的系统雏形进行测试。另外,如果你正在寻找稳定性和健壮性的例子 - checkout Struts Resume.

多好的一天!

BUILD SUCCESSFUL
Total time: 2 minutes 31 seconds

如果你喜欢,你可以在这里下载我们这个指南中创建的文件。

本文地址:http://com.8s8s.com/it/it11214.htm