首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

9.5  Web层设计

在架构综述里面提到,系统的Web层采用的是经典的J2EE Web MVC框架Struts,其表现层也大量使用Struts的标签库,关于Struts标签库的详细用法,请参考第3章的介绍。

9.5.1  Action的实现

Struts的Action实现非常简单,通过继承Struts的Action基类重写execute方法,并在该方法里调用业务逻辑组件的业务方法。在这里,可以发现所有的Action有个共同之处——都需要调用业务逻辑组件。

在Spring与Struts的整合策略里介绍过,业务逻辑组件的实现也不是由Action自己控制的,而是接受Spring容器的依赖注入。因此必须为Action提供对应的setter方法,而且每个Action都必须为其注入业务逻辑组件,因此可写成一个Action基类,让所有的Action都从该基类派生。

下面是Action基类的源代码:

//BaseAction,作为其他Action的父类

public class BaseAction extends Action

{

    // FacadeManager属性,面向接口编程

    protected FacadeManager mgr;

    //依赖注入业务逻辑组件必需的setter方法

    public void setMgr(FacadeManager mgr)

    {

          this.mgr = mgr;

    }

}

注意:在BaseAction中,故意将mgr属性设置成protected的访问权限,目的是为了让其子类可以直接访问该属性,从而提供更简单的访问方式。

而其他的Action则简单地从BaseAction派生出来,派生出来的Action具有一个属性:mgr,该属性就是业务逻辑组件的引用。

下面以几个Action作为代表,分别介绍Action的实现。

下面的AddReviewAction用于拦截用户提交消息回复时的请求,其代码如下:

//业务控制器,以BaseAction作为基础

public class AddReviewAction extends BaseAction

{

    /   /必须重写该核心方法,该方法actionForm将表单的请求参数封装成值对象

    public ActionForward execute(ActionMapping mapping, ActionForm form,

          HttpServletRequest request, HttpServletResponse response)

throws

Exception

    {

        //解析ActionForm,用于获取请求参数

          DynaValidatorForm addForm = (DynaValidatorForm)form;

        //获取请求参数

          String content = (String)addForm.get("content");

          String newsId = (String)addForm.get("newsId");

          try

          {

            //调用mgr的业务方法加载News对象

               News news = mgr.getNews(newsId);

            //获取session中的user值

            String username = (String)request.getSession(true)

.getAttribute

(AppConstants.LOGIN_USER);

            //调用mgr的业务方法加载User对象

             User poster = mgr.getUser(username);

            //创建消息回复实例

             NewsReview newsReview = new NewsReview();

            //设置消息回复的属性

            newsReview.setNews(news);

            newsReview.setPoster(poster);

            newsReview.setContent(content);

            newsReview.setPostDate(new Date());

            newsReview.setLastModifyDate(new Date());

            //持久化消息回复

            mgr.saveNewsReview(newsReview);

          }

        //捕捉异常

          catch (Exception e)

          {

            //出现异常就跳转到failure

               request.setAttribute("newsId" , newsId);

               return mapping.findForward("failure");

          }

        //执行成功,跳转到success

          request.setAttribute("newsId" , newsId);

          return mapping.findForward("success");

    }

}

下面的LoadReviewsByNews用于拦截用户查看消息细节的Action,该Action根据用户请求的id加载该消息的回复以及消息本身:

public class LoadReviewsByNews extends BaseAction

{

    //必须重写该核心方法,该方法actionForm将表单的请求参数封装成值对象

    public ActionForward execute(ActionMapping mapping, ActionForm form,

          HttpServletRequest request, HttpServletResponse response)

throws

      Exception

    {

        //希望加载的消息id

          String newsId = null;

        //考虑到请求有可能从不同地方转发过来,request的参数和属性

        //分别获取newsId属性。

          if(request.getAttribute("newsId") == null)

          {

               newsId = request.getParameter("newsId");

          }

          else

          {

               newsId = (String)request.getAttribute("newsId");

          }

        //获取News实例

          News news = mgr.getNews(newsId);

        //将News实例设置成Request属性后转发

          request.setAttribute("news" , news);

        //同时将消息的回复也设置成Request属性后转发

          request.setAttribute("reviews",news.getNewsReviews());       

          return mapping.findForward("success");

    }

}

上面介绍了两种Action:一种有ActionForm的Action;另一种无需ActionForm的Action。这两种Action大同小异,都需要调用mgr的业务逻辑方法,区别在于获取请求参数的方式不同。

注意:在上面的Action中,多次重复访问PO对象,而Action中通常不允许访问PO对象,因为PO对象是持久层的组件,应该使用更普通的JavaBean作为VO(值对象);VO用于封装业务逻辑组件访问的值,并将这些值传递到JSP页面。因为本示例是个较小的示例,所以在Action中可直接访问PO对象。在第10章的示例中,读者将可以看到更加严格的控制。

9.5.2  Spring容器管理Action

正如前面介绍的,推荐使用Spring管理Struts的Action。因为这样可以充分利用Spring的IOC功能,使Action无须关心业务逻辑组件的实现,而由Spring负责为Action注入业务逻辑组件引用,从而实现更好地解耦。

为了让Struts将请求转发到Spring容器内的bean,系统将采用DelegatingRequest Processor的整合策略。因为这种策略无需Struts创建Action实例,直接由Spring容器负责创建Action实例,并为其注入依赖关系。使系统更早将请求转发给Spring容器控制。

采用这种整策略,必须在struts-config.xml文件中配置controller元素,并通过指定processorClass属性指定DelegatingRequestProcessor处理器。即在配置文件中增加如下代码:

<controller inputForward="true"

processorClass="org.springframework.web.struts.DelegatingRequestProcessor"/>

经过这个简单的配置,则无须为struts-config.xml中的Action配置class属性,因为Struts无须负责创建Action实例,由DelegatingRequestProcessor直接将请求转发到Spring容器内。

下面是struts-config.xml文件中Action的配置代码:

<!--  添加消息评论  -->

<action path="/addNewsReview"

        name="addNewsReviewForm"

        scope="request"

        validate="true"

        input="input">

    <forward name="failure" path="/loadNewsReviewByNews.do"/>

    <forward name="success" path="/loadNewsReviewByNews.do"/>

</action>

在上面的配置代码中,没有为action元素确定class属性,只有当采用Delegating RequestProcessor代替了默认的RequestProcessor后,才允许这样配置。

DelegatingRequestProcessor转发请求时,请求被转发给Spring容器中同名的bean处理,因此必须在Spring容器中配置同名的bean。对于上面Action的配置,必须在Spring容器中配置如下的bean:

<bean name="/addNewsReview" class="org.yeeku.action.AddReviewAction">

    <property name="mgr" ref="facadeManager"/>

</bean>

下面是整个应用struts-config.xml配置文件的源代码:

<?xml version="1.0" encoding="GBK"?>

<!--  Struts配置文件的文件头,包含DTD等信息-->

<!DOCTYPE struts-config PUBLIC

          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

          "http://struts.apache.org/dtds/struts-config_1_2.dtd">

<!--  Struts配置文件的根元素-->

<struts-config>

    <!--  在form-beans元素里配置所有的ActionForm-->

    <form-beans>

        <!--  配置登录所使用的Form-->

        <form-bean name="loginForm" type="org.apache.struts.validator.

        DynaValidatorForm">

            <!--  配置loginForm的两个属性-->

            <form-property name="user" type="java.lang.String" />

            <form-property name="pass" type="java.lang.String" />

        </form-bean>

        <!--  添加消息所用的Form-->

        <form-bean name="addNewsForm" type="org.apache.struts.validator.

        DynaValidatorForm">

            <!--  配置addNewsForm的三属性-->

            <form-property name="title" type="java.lang.String" />

            <form-property name="content" type="java.lang.String" />

            <form-property name="categoryId" type="java.lang.String" />

        </form-bean>

        <!--  添加消息评论所用的Form-->

        <form-bean name="addNewsReviewForm" type="org.apache.struts.

        validator.DynaValidatorForm">

            <!--  配置addNewsReviewForm的两个属性-->

            <form-property name="content" type="java.lang.String" />

            <form-property name="newsId" type="java.lang.String" />

        </form-bean>

    </form-beans>

    <!--  配置所有的Action映射-->

    <action-mappings>

        <!--  处理登录  -->

        <action path="/processLogin"

                name="loginForm"

                scope="request"

                validate="true"

                input="input">

            <forward name="input" path="/index.jsp" />

            <forward name="success" path="/listCate.do" />

        </action>

        <!--  登出系统  -->

        <action path="/logout" scope="request">

            <forward name="success" path="/index.jsp"/>

        </action>

        <!--  进入主页面  -->

        <action path="/listCate" scope="request">

            <forward name="success" path="/main.jsp"/>

        </action>

        <!--  根据种类加载所有消息  -->

        <action path="/loadNewsByCategory" scope="request">

            <forward name="failure" path="/listCate.do"/>

            <forward name="success" path="/category_view.jsp"/>

        </action>

        <!--  添加消息  -->

        <action path="/addNews"

                name="addNewsForm"

                scope="request"

                validate="true"

                input="input">

            <forward name="failure" path="/loadNewsByCategory.do"/>

            <forward name="success" path="/loadNewsByCategory.do"/>

        </action>

        <!--  根据消息id加载所有评论  -->

        <action path="/loadNewsReviewByNews" scope="request">

            <forward name="success" path="/news_view.jsp"/>

        </action>

        <!--  添加消息评论  -->

        <action path="/addNewsReview"

                name="addNewsReviewForm"

                scope="request"

                validate="true"

                input="input">

            <forward name="failure" path="/loadNewsReviewByNews.do"/>

            <forward name="success" path="/loadNewsReviewByNews.do"/>

        </action>

    </action-mappings>

    <!--  配置控制属性-->

    <controller inputForward="true"

        processorClass="org.springframework.web.struts.DelegatingRequest

        Processor"/>

    <!--  配置国际化的消息资源-->

    <message-resources parameter="resource"/>

    <!--  配置数据校验的框架-->

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

    <set-property property="pathnames" value="/WEB-INF/validator-rules. xml,

                                          /WEB-INF/validation.xml"/>

        <set-property property="stopOnFirstError" value="true"/>

    </plug-in>

    <!--  配置用于Spring整合的插件框架-->

<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">

        <set-property property="contextConfigLocation" value="/WEB-INF/

        daoContext.xml,

/WEB-INF/applicationContext.xml,

/WEB-INF/action-Servlet.xml"/>

    </plug-in>

</struts-config>

这个配置文件与Struts基本的配置文件并没有太多的不同,区别在于该配置文件的action元素没有class属性,以及使用DelegatingRequestProcessor代替了系统默认的RequestProcessor。

注意:虽然action元素没有确定class属性,但也允许指定class属性,只是不会有任何作用。

Spring对Action的配置采用单独的文件配置action-Servlet.xml,该文件中配置了所有的Action bean。

因为所有的Action都需要为其注入业务逻辑组件,所以此处采用继承简化了Action bean的配置。具体的配置文件代码如下:

<?xml version="1.0" encoding="GBK"?>

<!--  Spring配置文件的文件头,包含DTD等信息-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素-->

<beans>

    <!--  配置Action模板,用于被其他Action继承-->

    <bean id="actionTemplate" abstract="true" singleton="false">

        <property name="mgr" ref="facadeManager"/>

    </bean>

    <!--  处理登录-->

    <bean name="/processLogin" class="org.yeeku.action.LoginAction"

parent="actionTemplate"/>

    <!--  登出系统-->

    <bean name="/logout" class="org.yeeku.action.Logout"/>

    <!--  列出所有的消息分类-->

    <bean name="/listCate" class="org.yeeku.action.ListCate" parent="

actionTemplate"/>

    <!--  根据种类列出所有消息-->

    <bean name="/loadNewsByCategory" class="org.yeeku.action.LoadNewsBy

Category"

        parent="actionTemplate"/>

    <!--  添加消息-->

    <bean name="/addNews" class="org.yeeku.action.AddNewsAction" parent="

actionTemplate"/>

    <!--  根据消息查看所有的评论-->

    <bean name="/loadNewsReviewByNews" class="org.yeeku.action.LoadReviews

ByNews"

        parent="actionTemplate"/>

    <!--  添加消息评论-->

    <bean name="/addNewsReview" class="org.yeeku.action.AddReviewAction"

        parent="actionTemplate"/>

</beans>

至此,已经基本完成了Struts与Spring的整合。当ActionServlet拦截到用户请求时,则调用DelegatingRequestProcessor,该处理器将请求转发到Spring容器中的bean,由该bean负责调用业务逻辑组件处理用户用户请求,并将处理结果呈现给用户。

9.5.3  数据校验的选择

数据校验是表现层必须处理的基本问题。根据第3章的介绍,表现层的数据校验分成客户端校验和服务器端校验。不管是客户端校验,还是服务器端校验,Struts都有很好的支持,完全可以弹出JavaScript校验。

此处要提醒读者的是,Struts的客户端校验有一个弊端。

看如图9.5所示的简单页面。

图9.5  简单的登录页面

在这个简单的登录页面中,假设只需要对页面中三个表单域进行校验,如用户名、密码及电子邮件这三项必填,并且电子邮件为有效的地址,可以采用如下的JavaScript代码校验:

<script>

function check(form)

{

    var errMsg = "";

    if (form.user.value == null || trim(form.email.value)=="")

    {

        errMsg += "用户名必填" ;

    }

    if (form.pass.value == null || trim(form.pass.value)=="")

    {

        errMsg += "密码必填" ;

    }

    if (form.email.value == null || trim(form. email.value)=="")

    {

        errMsg += "电子邮件必填" ;

    }

    if(trim(form. email.value)!="" && !/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\ w+([-.]\w+)*$/.test(form.email.value))

    {

        errMsg += "电子邮件格式不对 ;

        return false;

    }

    if(errMsg == null )

    {

        return true;

    }

    return false

}

//用于截取两端的空格

function trim(s)

{

     return s.replace( /^\s*/, "" ).replace( /\s*$/, "" );

}

</script>

这段JavaScript代码结构清晰,相当简洁。

通过第3章的学习,我们也可以使用Struts的验证框架来生成客户端校验,但Struts生成的JavaScript的校验代码非常多,笔者在此处不可能完全列出,但读者以通过客户端查看网页源代码看到这将近1000行的JavaScript代码。

仔细检查Struts生成的JavaScript代码,可以发现这些JavaScript代码包含了最小长度校验、最长长度校验及有效范围校验等,而这些与该页面的需求没有丝毫关系。

这正是手写JavaScript校验和Struts自动生成JavaScript校验的区别:Struts生成的校验会包含更多的代码,即使页面只需要校验一个简单的必填项,Struts也会生成将近1000行的JavaScript代码。虽然这些代码不需要程序员手写,但这些代码必须要下载到客户端执行,显然这些无用的代码将加重客户端网络带宽的负担。因此客户端校验依然建议使用手写校验。

注意:笔者在这里介绍给读者一个技巧,仔细观察Struts生成的JavaScript客户端校验代码,就可发现每个页面的JavaScript代码只有前面数行不同,其他部分则完全相同的。没错,这些部分是通用,我们完全可以将这些通用的部分提取出来,作为通用的JavaScript代码,并以单独的JavaScript文件保存,当每个页面需要进行校验时,只需导入该通用的JavaScript文件即可;而页面则只需加入Struts为不同页面生成的不同部分。关于客户端校验,还可以直接采用ProtoType校验。

在网络带宽受限的情况下,建议不要采用Struts的JavaScript校验。并不是意味可以不使用Struts的校验框架。因为服务器端校验依赖于Struts的校验要简单得多。

增加校验的详细步骤请参考第3章的内容。此处给出本应用的校验规则文件,validation.xml文件的源代码如下:

<?xml version="1.0" encoding="iso-8859-1"?>

<!--  校验文件的文件头,包含DTD等信息-->

<!DOCTYPE form-validation PUBLIC

          "-//Apache Software Foundation//DTD Commons Validator Rules

Configuration 1.1.3//EN"

          "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">

<!--  校验文件的根元素-->

<form-validation>

    <!--  需要校验的Form都放在formset元素里-->

    <formset>

        <!--  需要校验的第一个form:登录用的loginForm-->

        <form name="loginForm">

            <!--  需要校验的user域,需满足最小长度,必填两个规则-->

            <field property="user" depends="required,minlength">

                <arg key="loginForm.user" position="0"/>

                <arg name="minlength" key="${var:minlength}" resource="

                false" position="1"/>

                <var>

                    <var-name>minlength</var-name>

                    <var-value>4</var-value>

                </var>

            </field>

            <!--  需要校验的pass域,需满足最小长度,必填两个规则-->

            <field property="pass" depends="required,minlength">

                <arg key="loginForm.pass" position="0"/>

                <arg name="minlength" key="${var:minlength}" resource="

                false" position="1"/>

                <var>

                    <var-name>minlength</var-name>

                    <var-value>4</var-value>

                </var>

            </field>

        </form>

        <!--  需要校验的第二个form:添加消息用的addNewsForm -->

        <form name="addNewsForm">

            <!--  需要校验的title域,需满足必填规则-->

            <field property="title" depends="required">

                <arg key="addNewsForm.title" position="0"/>

            </field>

            <!--  需要校验的content域,需满足必填规则-->

            <field property="content" depends="required">

                <arg key="addNewsForm.content" position="0"/>

            </field>

            <!--  需要校验的categoryId域,需满足必填,整数规则-->

            <field property="categoryId" depends="required,integer">

                <arg key="addNewsForm.categoryId" position="0"/>

            </field>

        </form>

        <!--  需要校验的第三个form:添加消息评论用的addNewsReviewForm -->

        <form name="addNewsReviewForm">

            <!--  需要校验的content域,需满足必填规则-->

            <field property="content" depends="required">

                <arg key="addNewsReviewForm.content" position="0"/>

            </field>

            <!--  需要校验的newsId域,需满足必填,整数规则-->

            <field property="newsId" depends="required,integer">

                <arg key="addNewsReviewForm.categoryId" position="0"/>

            </field>

        </form>

  </formset>

</form-validation>

推荐的校验策略是:在客户端采用手写的JavaScript校验;而服务器端采用Struts的校验框架完成。

9.5.4  访问权限的控制

本系统访问权限的控制非常简单,在使用本消息发布系统之前,必须先登录本系统。如果某个未登录的用户企图进入系统使用页面时,则系统将请求转到登录页面。

权限控制的最传统做法是:在Action里手动控制,在每次调用业务逻辑方法之前,首先判断用户是否有足够的权限。在本系统中就是判断用户是否登录,但这种方法相当烦琐,而且需要重复判断用户是否登录,这与DRY(不要书写重复的代码)规则相违背。

但权限控制有更好的选择:利用Filter过滤请求,或者使用AOP框架拦截请求。笔者在本章采用Filter过滤请求完成权限检查,在第10章将采用AOP框架来完成更复杂的权限检查。

相对而言,利用AOP框架的权限检查支持更细的粒度,灵活性更好。

下面是控制权限检查的Filter源代码:

//将请求转换成HttpServletRequest

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

//将响应转换成HttpServletResponse

HttpServletResponse httpServletResponse = (HttpServletResponse) response;

//获取客户端的请求的地址

String requesturi = httpServletRequest.getRequestURI();

// 通过检查session中的变量,过滤请求

HttpSession session = httpServletRequest.getSession();

Object currentUser = session.getAttribute(AppConstants.LOGIN_USER);

// 当前会话用户为空而且不是请求登录,则退出登录,欢迎页面和根目录则退回到应用的根目录

if (currentUser == null

    && !requesturi.endsWith("/processLogin.do")

    && !requesturi.endsWith("/logout.do")

    && !requesturi.endsWith("/index.jsp")

    && !requesturi.endsWith(httpServletRequest.getContextPath()

        + "/"))

    {

        httpServletResponse.sendRedirect(httpServletRequest.

getContextPath() + "/");

        return;

    }

    //否则,允许继续处理请求

    chain.doFilter(request, response);

}

Filter本身不能处理用户请求,也不能生成响应,它是个典型的链式处理,拦截用户请求。增加Filter的额外处理后,依然将请求转发给Servlet,由Servlet生成客户端响应。

在该用户请求中,如果请求地址不是请求登录、退出登录、欢迎页面或根目录,并且当前用户没有登录时,则退回到应用的根目录。

9.5.5  解决中文编码问题

来自中国地区的请求,基本都以GBK方式编码,而Struts的ActionServlet默认以ISO8859-1方式解码。在这种情况下,不可避免地会产生乱码问题,为避免这种乱码问题,可以有以下两个做法:

— 扩展ActionServlet。

— 利用Filter处理编码方式。

两种方式的处理原理相似,都是通过设置HttpServletRequest的解码方式。关于扩展ActionServlet在第3章已经介绍过了,此处介绍第二种方式——利用Filter处理编码。

类似于扩展ActionServlet,利用Filter也只需要在doFilter方法的中增加设置HttpServletRequest的解码方式即可。

下面是本应用所使用的Filter的源代码:

public class UserLoginFilter implements Filter

{

    //用于本系统所使用的编码方式

    protected String encoding = null;

    //保存Filter的配置对象

    protected FilterConfig filterConfig = null;

    //是否忽略配置的编码方式

    protected boolean ignore = false;

    protected String forwardPath = null;

    //销毁Filter时调用该方法

    public void destroy()

    {

        this.encoding = null;

        this.filterConfig = null;

    }

    //处理用户请求

    public void doFilter(ServletRequest request, ServletResponse response,

            FilterChain chain) throws IOException, ServletException

    {

        // 设置编码方式,web.xml里面有filter参数的初始化设置

        if (ignore || (request.getCharacterEncoding() == null))

        {

            String encoding = selectEncoding(request);

            //设置编码方式

            if (encoding != null)

                request.setCharacterEncoding(encoding);

        }

        //将请求转换成HttpServletRequest

             HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        //将响应转换成HttpServletResponse

             HttpServletResponse httpServletResponse =

(HttpServletResponse) response;

        //获取客户端的请求地址

        String requesturi = httpServletRequest.getRequestURI();

        //通过检查session中的变量,过滤请求

        HttpSession session = httpServletRequest.getSession();

        Object currentUser = session.getAttribute(AppConstants.LOGIN_USER);

        //当前会话用户为空而且不是请求登录,则退出登录,欢迎页面和根目录则退回到应用的根目录

        if (currentUser == null

                && !requesturi.endsWith("/processLogin.do")

                && !requesturi.endsWith("/logout.do")

                && !requesturi.endsWith("/index.jsp")

                  && !requesturi.endsWith(httpServletRequest.getContextPath()

                        + "/")) {

            httpServletResponse.sendRedirect(httpServletRequest

                    .getContextPath()

                    + "/");

            return;

        }

        chain.doFilter(request, response);

    }

    //初始化该Filter时调用该方法

    public void init(FilterConfig filterConfig) throws ServletException

    {

        //初始化FilterConfig对象

        this.filterConfig = filterConfig;

        //通过filterConfig获取请求参数:encoding

        this.encoding = filterConfig.getInitParameter("encoding");

        //通过filterConfig获取请求参数:forwardpath

        this.forwardPath = filterConfig.getInitParameter("forwardpath");

        //通过filterConfig获取请求参数:ignore

        String value = filterConfig.getInitParameter("ignore");

        //处理ignore参数的值

        if (value == null)

            this.ignore = true;

        else if (value.equalsIgnoreCase("true"))

            this.ignore = true;

        else if (value.equalsIgnoreCase("yes"))

            this.ignore = true;

        else

            this.ignore = false;

    }  

    protected String selectEncoding(ServletRequest request) {

        return (this.encoding);

    }

}

该Filter用于拦截整个应用的全部请求,但必须为其配置对应的参数,关于该Filter的配置代码如下:

<filter>

    <!--  指定Filter的名字-->

    <filter-name>Login Filter</filter-name>

    <!--  指定Filter的实现类-->

    <filter-class>

        org.yeeku.webapp.filter.UserLoginFilter

    </filter-class>

    <!--  指定Filter的第一个初始化参数:encoding -->

    <init-param>

        <param-name>encoding</param-name>

        <param-value>GBK</param-value>

    </init-param>

    <!--  指定Filter的第二个初始化参数:ignore -->

    <init-param>

        <param-name>ignore</param-name>

        <param-value>false</param-value>

    </init-param>

    <!--  指定Filter的第三个初始化参数:forwardpath -->

    <init-param>

        <param-name>forwardpath</param-name>

        <param-value>index.jsp</param-value>

    </init-param>

</filter>

<!--  指定Filter负责拦截的URL:负责拦截所有请求 -->

<filter-mapping>

    <filter-name>Login Filter</filter-name>

    <url-pattern>/*</url-pattern>

</filter-mapping>

9.5.6  JSP页面输出

JSP的功能相当简单,除了完成数据的收集,就是完成简单的数据显示。此处的数据显示主要用于从HttpServletRequest中获取Attribute,然后将其显示在JSP页面中,也包括对集合对象的显示。

JSP页面的显示主要依赖于Struts的标签库,下面以几个简单示例,来介绍JSP如何利用Struts输出集合内容。

<logic:present name="categories" scope="request">

<logic:iterate id="item" name="categories" indexId="index" scope="request">

    <tr>

        <td width="10%" align="center">

            <input type="radio" name="categoryId" value='<bean:write name="item" property="id"/>'>

        </td>

        <td width="30%" align="center">

            <bean:write name="item" property="name"/>

        </td>

    </tr>  

</logic:iterate>

</logic:present>

该代码用于输出所有的消息分类,从Action转发到该页面时,则在HttpServletRequest中封装了categories的属性,该属性是个集合对象,集合里每个元素都是Category对象。

页面首先通过logic:present标签判断categories属性是否存在,如果该属性不存在,则不用输出;如果该属性存在,则使用logic.iterate迭代器标签,循环输出categories集合中每个元素。该迭代器每次迭代时将集合中的元素放在page范围内,并在迭代器标签的标签体内使用bean:write标签,输出集合属性中每个元素的属性值。

图9.6显示了该代码的效果。

图9.6  迭代器标签的输出效果

再看下面的的页面输出代码:

<logic:messagesPresent>

   <div class="title">  

   <bean:message key="errors.header"/>

   <ul>

   <html:messages id="error">

      <li><bean:write name="error"/></li>

   </html:messages>

   </ul>

   </div>

</logic:messagesPresent>

该片段主要用于输出校验信息,页面首先通过logic:messagesPresent标签来判断是否包含出错提示信息,如果包含出错提示,则输出该出错提示;如果不存在,则无须输出任何内容。

当出错提示存在时,先使用<bean:message key="errors.header"/>输出国际化信息,然后使用html:messages标签,对系统包含的出错提示逐行输出。

图9.7显示了出错提示的显示效果。

图9.7  服务器端数据校验的提示信息

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论