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

1.1.6  动态生成客户端JavaScript代码

客户端验证代码也可以不作为静态模板文本放在JSP文件中,而是通过创建一个JSP标记库来生成。

使用JSP来生成客户端验证代码有以下优点:

q    可以减少在主JSP页面中的复杂代码,从长远来看将简化维护。

q    可以很容易地重用JavaScript代码和验证机制。

q    即使开发人员对JavaScript编码不熟,也可以只使用JSP标记库来实现客户端验证。

实际上,最后一个优点非常重要。当前许多基于JSP的框架就大量使用了这个功能来创建高度交互的用户界面组件,如JSFJavaServer Faces,见第8章)。

下一个实验与第一个实验的功能是一样的。不过使用了一个定制标记库来动态生成客户端验证代码。

实验:客户端数据验证代码的动态生成

对于这个例子,首先需要明确,它与第一个例子的作用是完全一样的。

代码流程完全相同:index.jsp 作为前端控制器,它使用动作转发来把请求定向到prodform.jsp 以输入商品的数据,或者定向到procprod.jsp 以显示表单输入数据。主要区别在于如何创建prodform.jsp,这将在本例后面详细解释。

首先确保已经在Tomcat 5服务器上部署了ch12.war文件。当然,服务器必须已经启动并正常运行。

将浏览器指向以下URL

http://localhost:8080/ch12/example2/index.jsp

显示的表单同图1-4

在表单中输入以下值:

q    sku 字段中,输入3212a

q    name 字段中,输入2 Beginning JavaServer Pages

q    desc 字段中,输入Excellent book to learn JSP with

q    price 字段中,输入M39

这些输入值包含一些可以由验证代码捕获的错误。

点击Add Product按钮,启动客户端验证代码。然后检测到错误,并弹出一个对话框,如图1-5所示。

点击OK按钮关闭对话框。表单中会突出显示检测到的错误。此时并不向服务器发送任何数据。表单现在应该类似于图1-6

1-6显示出检测到3个错误。第一个是在sku字段中输入的值有错误。sku 格式要求只能包含数字,而不允许出现任何字符。为了修正这个错误,可以删除最后面的a 字符。再点击Add Product 按钮,得到的界面如图1-7所示。

注意在图1-7 中,sku 已经合法,现在只有两个错误了。为了更正这些错误,需要做如下修正:

删除name 字段中最前面的2 ,因为这个字段必须以字母开头。

删除price 字段中最前面的M ,因为这个字段只能包含数字型的美元值。

再点击Add Product按钮。

这一次,所有值都通过了验证,表单数据最终提交到服务器。表单数据将由服务器端JSP处理。该JSP只是显示出表单字段的值。所得到的结果与图1-8类似。

不难发现,这里的客户端数据验证代码与第一个例子中的代码功能完全相同。所有的验证错误都会用红色的标志突出显示。只有当没有验证错误时,客户端验证代码才允许提交表单数据。

尽管这个新的prodform.jsp是用一个定制标记库而不是原始的JavaScript代码创建的,它与前面的JavaScript版本相比并没有明显的区别。

实验解析

此例中index.jspprocprod.jsp页面与前一个例子完全相同,所以这里不再讨论。

这两个例子的主要区别在于prodform.jsp页面,此文件位于<Tomcat安装目录>/webapps/ ch12/example2/prodform.jsp。以下将这个页面列出:

<%@ taglib prefix="my" tagdir="/WEB-INF/tags/wroxtags" %>

<html>

<head>

  <title>Product Information Entry Form</title>

  <my:validateFunctions>

    <my:checkField name="sku" type="digits"/>

    <my:checkField name="name" type="alphanum"/>

    <my:checkField name="desc" type="alphanum"/>

    <my:checkField name="price" type="digits"/>

  </my:validateFunctions>

</head>

<body>

  <h1>Enter Product Information</h1>

  <form name="prodform" action="index.jsp" method="post"

    onsubmit="return validateForm()">

  <table border="0" >

    <tr>

      <td>SKU:</td>

      <td>

       <input name="sku" type="text" width="40"/>

       <my:validateErrMsg name="sku"

         msg="<-- please enter digits only"/>

      </td>

    </tr>

 

    <tr>

      <td>Name:</td>

      <td>

      <input name="name" type="text" width="40"/>

      <my:validateErrMsg name="name"

       msg="<-- please enter letters or numbers only, do not start with a

number"/>

      </td>

    </tr>

    <tr>

      <td>Description:</td>

      <td>

       <input name="desc" type="text" width="40"/>

       <my:validateErrMsg name="desc"

         msg="<-- please enter letters or numbers only, do not start with a

number"/>

      </td>

    </tr>

    <tr>

      <td>Price:</td>

      <td>

       <input name="price" type="text" width="40"/>

       <my:validateErrMsg name="price"

         msg="<-- please enter price in dollars only, do not enter cents"/>

      </td>

    </tr>

    <tr>

      <td colspan="2">

       <input type="submit" value="Add Product" />

       <input type="hidden" name="action" value="prodsubmit"/>

      </td>

    </tr>

 

 

  </table>

  </form>

</body>

</html>

与第一个例子中的prodform.jsp相比,可以发现一些改进。

1)使用定制标记库完成客户端数据验证

这个新 prodform.jsp 页面没有使用嵌入的JavaScript代码,而是包含以下标记:

<my:validateFunctions>

  <my:checkField name="sku" type="digits"/>

  <my:checkField name="name" type="alphanum"/>

  <my:checkField name="desc" type="alphanum"/>

  <my:checkField name="price" type="digits"/>

</my:validateFunctions>

在此使用my前缀来指示一个定制标记库。使用以下<%@ taglib %> 指令来包含这个定制标记库

<%@ taglib prefix="my" tagdir="/WEB-INF/tags/wroxtags" %>

通过使用这个 <%@ taglib %> 指令,指定构成此标记库的标记文件可以在 <Tomcat安装目录>/webapps/ch12/WEB-INF/tags/wroxtags 目录找到。

这个标记库里有3个定制标记,表1-3介绍了各个标记的用法。

1-3 定制标记库中标记的用法

标  记

说  明

validateFunctions

用于包装一组checkField 标记

checkField

每个checkField 标记有一个name 属性,这应当是<input> 元素(包含用户输入)的名。checkField 标记还有一个type属性。这个属性可以包含alphanum 来验证是否为字母数字输入(以字母打头),也可以包含digits 来验证是否仅为数字输入

续表

标  记

说  明

validateErrMsg

用于生成一条错误消息,只有当相关的字段验证失败时这个错误消息才可见。name属性应当是<input>元素(包含待验证的用户输入)的名。msg属性应当包含验证失败时要显示的错误消息

通过使用这些标记,JSP开发人员可以做到:

q    提供客户端数据验证,而无需了解如何编写JavaScript函数。

q    很容易地向表单增加新的验证字段。

为了描述验证错误,这里使用了<my:validiateErrMsg> 标记。例如,以下代码描述了sku字段的验证错误:

<tr>

  <td>SKU:</td>

  <td>

    <input name="sku" type="text" width="40"/>

    <my:validateErrMsg name="sku"

      msg="<-- please enter digits only"/>

  </td>

</tr>

突出显示的代码就是用于描述验证错误消息的<my:validateErrMsg> 标记。

2)标记库实现细节

如果查看实现这个标记库的标记文件,可以很清楚地看到,标记文件中的代码与第一个例子中使用的JavaScript代码完全相同。

l    <my:validateFunctions>定制标记

首先,我们列出包含在<Tomcat安装目录>/webapps/ch12/WEB-INF/tags/wroxtags/ validateFunctions.tag文件中的<my:validateFunctions> 标记:

<script language="JavaScript">

function isDigitsOnly(inParam)

{

    var chkExp = /^\d+$/;

    return (chkExp.test(inParam));

}

function isTextOnly(inParam)

{

    var chkExp = /^[a-z][a-z\d ]+$/i;

    return (chkExp.test(inParam));

}

function validateForm()

{

    var formValid = true;

    <jsp:doBody/>

    return formValid;

}

</script>

<style>

  span.validateError

  {

    color: red;

    visibility: hidden;

  }

</style>

这个标记包含内嵌的JavaScript代码,有一个内嵌的<jsp:doBody/>标准动作。<my: validateFunctions>标记体的内容会生成到该标记所在的位置上。

这个标记首先定义了JavaScript函数:

<script language="JavaScript">

function isDigitsOnly(inParam)

{

    var chkExp = /^\d+$/;

    return (chkExp.test(inParam));

}

function isTextOnly(inParam)

{

    var chkExp = /^[a-z][a-z\d ]+$/i;

    return (chkExp.test(inParam));

}

接下来,标记定义了validateForm()函数,不过没有具体的验证代码。这是因为,对于表单上的各个字段,将由单独的<my:checkField> 标记生成验证代码。这些<my:checkField> 标记的输出将生成到<jsp:dobody/> 标记所在的位置上:

function validateForm()

{

    var formValid = true;

    <jsp:doBody/>

    return formValid;

}

</script>

最后,这个标记使用了<style> 元素为验证错误消息定义CSS样式:

<style>

  span.validateError

  {

    color: red;

    visibility: hidden;

  }

</style>

l    <my:checkField>定制标记

第二个标记<my:checkField>用于生成代码,从而使用JavaScript工具函数完成验证;它取两个属性作为参数。这个标记包含在<Tomcat安装目录>/webapps/ch12/WEB-INF/tags/ wroxtags/checkField.tag 文件中,列出如下:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@attribute name="name" type="java.lang.String" %>

<%@attribute name="type" type="java.lang.String" %>

 

<c:choose>

  <c:when test="${type eq 'alphanum' }">

    if (!isTextOnly(document.prodform.${name}.value))

    {

  </c:when>

 

  <c:when test="${type eq 'digits' }">

    if (!isDigitsOnly(document.prodform.${name}.value))

    {

  </c:when>

</c:choose>

document.getElementById("${name}Error").style.visibility = "visible";

formValid = false;

    }

  else

    document.getElementById("${name}Error").style.visibility = "hidden";

这个标记使用了JSTL库,并且使用<jsp:attribute>指令将两个属性(nametype)都声明为String类型

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ attribute name="name" type="java.lang.String" %>

<%@ attribute name="type" type="java.lang.String" %>

然后使用JSTL <c:choose> 来生成调用isTextOnly() isDigitsOnly()验证函数的代码。具体调用哪一个由<my:checkField>标记的type属性值决定。注意这里使用EL表达式${name}来生成name参数的值:

<c:choose>

  <c:when test="${type eq 'alphanum' }">

    if (!isTextOnly(document.prodform.${name}.value))

    {

  </c:when>

 

  <c:when test="${type eq 'digits' }">

    if (!isDigitsOnly(document.prodform.${name}.value))

    {

  </c:when>

</c:choose>

标记文件的最后一部分设置了隐藏的错误消息<span>元素的可见性,以及formValid布尔变量的值:

    document.getElementById("${name}Error").style.visibility = "visible";

    formValid = false;

}

else

    document.getElementById("${name}Error").style.visibility = "hidden";

上述代码中,同样需要注意EL表达式${name}生成待验证字段具体名的用法。

l    <my:validateErrMsg> 定制标记

库中最后一个标记是<my:validateErrMsg>,它用于生成包含验证错误消息的<span><my:validateErrMsg>标记包含在<Tomcat安装目录>/webapps/ch12/WEB-INF/tags/ wroxtags/validateErrMsg.tag文件中,在此将其列出:

<%@attribute name="name" type="java.lang.String" %>

<%@attribute name="msg" type="java.lang.String" %>

<span id="${name}Error" class="validateError">${msg}</span>

这个标记相当直观,两个属性的类型都是String,且都使用EL表达式生成并放在HTML代码段中。所得到的 <span> 标记最初是不可见的,不过如果在相关的字段中出现验证错误就会置为可见。

第二个例子的介绍到此就结束了,动态生成客户端JavaScript代码的讨论至此也告一段落。

JSP经常生成的另外一种很有意思的非HTML内容类型是XML。本章最后一个例子就是关于XML数据的生成。

1.1.7  使用JSP动态生成XML

本小节我们来回顾使用JSP生成XML文档,如其中的一些技术和概念,并作进一步探讨。(这部分内容的基础知识可以参考《JSP程序设计》第7章。)

使用JSP生成XML时,要记住以下几点:

q    JSP可以用于生成任何文本数据,XML只是一种文本数据类型而已。

q    使用<%@page %> 指令的contentType属性,并将其设置为text/xml以提供最大程度的浏览器/应用兼容性。

q    所有JSP 2.x页面都可以采用XML视图来表述,视图中整个未处理的JSP页面都在一个良构(well-formed,也称合式)的XML文档中。

这一点对于大多数开发人员来说并不是太重要,不过对于工具开发商来说却很重要。如果JSP采用一种纯XML格式,开发工具就能很容易地解析和处理这些页面。

本章最后一个实验将分析使用JSP生成XML文档时的常见问题。

实验:生成XML

这是一个用JSP生成XML文档的实例。这个XML文档为用户显示一个每日编程提示。这种每日提示本应该从数据库获取,不过在这个例子中,我们使用了一个定制标记来生成显示数据。

可以使用浏览器来查看所生成的XML文档。如果使用的是Microsoft Internet Explorer 浏览器,就能以一种可折叠的视图显示一个良构的XML文件。

在开始之前,需要确保Tomcat 5服务器已经运行且已在其上部署了ch12.war文件。然后访问以下URL

http://localhost:8080/ch12/example3/index.jsp

如果使用Internet Explorer 浏览器,会发现所得到的XML文件有一个错误,如图1-10所示。

所得到的XML文档不合法,因为某些XML保留字符与打印代码中的字符有冲突。例如,字符&<XML中都是保留字符。

1-10 XML文档生成期间遇到的错误

要想看到没有这个问题的正确JSP,请访问以下URL

http://localhost:8080/ch12/example3/index1.jsp

这一次,JSP生成了一个良构的XML,浏览器将以一种可折叠的视图显示(如果使用的是Internet Explorer浏览器)。图1-11展示了这个良构的XML文档。

1-11 修正错误后生成的XML文档

实验解析

首先来看生成前一个XML文档的JSP页面。如下所示的index.jsp页面位于<Tomcat安装目录>/webapps/ ch12/example3/index.jsp

<%@ taglib prefix="my" tagdir="/WEB-INF/tags/wroxtags" %>

<%@page contentType="text/xml" %>

<?xml version="1.0"?>

<entry>

  <my:getInfo/>

  <name>

    ${name}

  </name>

  <comment>

    ${comment}

  </comment>

  <code>

    ${code}

  </code>

</entry>

首先,<%@ taglib %>指令将my前缀与<Tomcat安装目录>/webapps/ch12/WEB-INF/ tags/wroxtags 目录中的标记文件相关联:

<%@ taglib prefix="my" tagdir="/WEB-INF/tags/wroxtags" %>

接下来,使用<%@ page %> 指令将生成的内容设置为XML类型后面紧跟一个必要的XML声明:

<%@page contentType="text/xml" %>

<?xml version="1.0"?>

XML声明之后就是根元素<entry>。在此调用标记库中一个名为<my:getInfo>的标记以获取“每日编程提示”的详细内容。调用这个标记时,只是设置了3个作用域变量,分别名为namecommentcode

这些变量的值使用XML模板数据中的EL表达式来生成。下面突出显示了生成变量值的EL表达式:

<entry>

  <my:getInfo/>

  <name>

    ${name}

  </name>

  <comment>

    ${comment}

  </comment>

  <code>

    ${code}

  </code>

</entry>

看上去一切正常,但前面已经看到,这个JSP会产生一个非良构的XML文档。

1)生成XML时转义保留字符

罪魁祸首就在code 变量中,该变量包含以下代码段:

if ((!playLevel) && (retries < 3))

{

    var int = playLevel.probe();

}

&字符和<字符都是保留字符,在XML文档中有着特殊的含义。要在XML文档中的其他部分使用这些字符,必须对其转义。表1-4显示了相应的转义字符。

1-4 XML转义字符

字  符

XML转义字符

&

&amp;

< 

&lt;

因此,对于一个良构的XML文档,代码段必须表述如下:

if ((!playLevel) &amp;&amp; (retries &lt; 3))

{

    var int = playLevel.probe();

}

遗憾的是,只使用EL表达式还不够。不过,好在JSTL 输出标记<c:out>会默认地完成XML转义。

因此,只需将EL表达式代之以JSTL动作<c:out>。位于<Tomcat安装目录>/webapps/ ch12/example3/index1.jspindex1.jsp页面正是这样做的。这个页面的内容如下,其中突出显示了前面没有的<c:out> 标记:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="my" tagdir="/WEB-INF/tags/wroxtags" %>

<%@page contentType="text/xml" %>

<?xml version="1.0"?>

<entry>

  <my:getInfo/>

  <name>

    <c:out value="${name}"/>

  </name>

  <comment>

    <c:out value="${comment}"/>

  </comment>

  <code>

    <c:out value="${code}"/>

  </code>

</entry>

也可以不用JSTL <c:out>而使用一个名为escapeXml()EL函数。例如使用${fn:escapeXml(name)}也可以达到同样的效果。

处理XML数据时,了解动态生成的数据是否要对XML特殊字符转义非常重要,如果需要就应完成所需的转义。了解这一点将有助于避免一些常见的编程错误,例如:

q    XML文档使用未转义的文本,如前一个例子所示。

q    XML文档中出现了转义文本。

2<my:getInfo> 定制标记

本例中只剩下<my:getInfo> 定制标记还没有讨论。该标记为XML文件生成数据,其代码可以在<Tomcat安装目录>/webapps/ch12/WEB-INF/wroxtags/getInfo.tag 文件找到。

这个文件的内容如下:

<%@ tag body-content="empty" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ variable name-given="code" variable-class="java.lang.String" scope="AT_END" %>

<%@ variable name-given="name" variable-class="java.lang.String" scope="AT_END" %>

<%@ variable name-given="comment" variable-class="java.lang.String"

scope="AT_END" %>

<c:set var="code">

  if ((!playLevel) && (retries < 3))

  {

      var int = playLevel.probe();

  }

</c:set>

<c:set var="name">

  Joe Malone

</c:set>

<c:set var="comment">

  This code can be used to determine the level of play.

</c:set>

首先,使用 <%@ tag %> 指令来指定这个标记体为空:

<%@ tag body-content="empty" %>[3]

接下来要包含JSTL标记库,并使用<%@ variable %>指令定义3个变量。所有这些变量的类型都是String,它们都在最后同步。所以对于调用这个标记的JSP页面来说,在标记的最后才可以访问这些变量:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ variable name-given="code" variable-class="java.lang.String" scope="AT_END" %>

<%@ variable name-given="name" variable-class="java.lang.String" scope="AT_END" %>

<%@ variable name-given="comment" variable-class="java.lang.String"

scope="AT_END" %>

code变量的值设置为以下代码段:

<c:set var="code">

  if ((!playLevel) && (retries < 3))

  {

      var int = playLevel.probe();

  }

</c:set>

最后,设置name comment变量的值:

<c:set var="name">

  Joe Malone

</c:set>

<c:set var="comment">

  This code can be used to determine the level of play.

</c:set>

执行了这个标记后,就可以在调用页面中使用这3个变量了,因为其中已包含了所需的数据。

查看所有评论(0)条】

最近评论



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