`
nwj2010
  • 浏览: 89622 次
  • 性别: Icon_minigender_1
  • 来自: 宁波
社区版块
存档分类
最新评论

简化 Ajax 和 Java 开发,第 1 部分: 用 JSP 标记文件动态生成 JavaScript 代码

阅读更多
基于简单开发的 JSP 标记文件构建可重用的 Ajax 和 Java 组件
Andrei Cioroianu, 高级 Java 开发人员和顾问, Devsphere
  转自http://www.ibm.com/developerworks/cn/web/wa-aj-simplejava1/index.html
   简介: 很多 Web 开发人员都经常抱怨说 Java™ EE 太复杂、构建新的 Web 组件太难、定制现有的组件没有预想的那样简单,并且即便是很小的更改都需要重新启动应用程序。本系列给出了针对这些问题的解决方案,即采用代码生成器、约定、脚本语言和先进的 JavaServer Pages ™ (JSP) 特性。在本文中,您将了解如何基于 JSP 标记文件构建可重用的 Ajax 和 Java 组件,而这些 JSP 标记文件很容易开发和部署。更改之后,JSP 标记文件会由 Java EE 服务器自动重编译,而无须重启应用程序。此外,您还能完全控制所生成的代码,并能轻松地定制这些轻量级组件,因为它们使用的是 JSP 语法。
   本系列含 4 部分,展示了一种基于 JSP 的技术,用以生成 JavaScript 代码、显著减少需要手动编写的代码量,本文是第 1 部分。本文的示例应用程序展示了如何生成 JavaScript 函数来发送 Ajax 请求和处理 Ajax 响应。如果想要轻松地更改 Ajax 代码,可以将这里讨论的简单技巧应用到实际的应用程序中。本文更宽泛的目标是展示如何使用 JSP 标记文件针对具体需求生成 JavaScript 代码,而非只是 Ajax 例程。
使用框架和代码生成器
如果您很幸运地找到了一种能满足您需要的组件或框架,那么就请使用它吧。如果没有找到也没关系,因为您总是可以开发自己的解决方案,也可以定制现有的一段代码。不管是哪种情况,一种很好的做法是 “参数化” 代码并将其放入一个可重用的库,而非将参数硬编码到您的代码里。不过有时候,实现泛型并不实际,因为它会使开发变得复杂,而非简化。在将泛型代码放入可重用组件或框架时,可以考虑使用代码生成器来更有效地生成特定的代码。
在开发的过程中避免复制 & 粘贴
假设,您需要应用程序使用 Ajax 请求站点上的某些信息,最快的(当然不是最好的)方法是找到一些如清单 1 这样的免费代码、更改 URL 并将这些代码粘贴到 Web 页面。很多开发人员都会这么做,但这种做法会导致巨大的维护问题。如果应用程序具有数百个页面,最后的结果将是出现大量像清单 1 中的 getInfo() 这样的函数。不好的一面是每次需要进行添加或更改(比如实现 Ajax 请求的错误处理)时,您都必须要手动修改所有页面并重新测试它们。好的一面是您可以通过使用库、框架和代码生成器,很容易地避免这个维护问题。

清单 1. Ajax 函数
function getInfo(country, city) {
    var request = null;
    if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    else
        return;

    var url = "CityInfo.jsp?country=" + escape(country) 
                           + "&city=" + escape(city);
    request.open("GET", url, true);

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                // ...
            }
        }
    }

    request.onreadystatechange = processResponse;
    request.send(null);
}

开发泛型函数
   一种好的开发实践是将尽量多的代码移入可重用的例程、函数、类或组件,而这些例程、函数、类或组件均能划分到库或框架中。在我们的示例中,您能找到一个泛型函数,此函数能创建 XMLHttpRequest 实例和调用实例的 open() 和 send() 方法。
   假设您决定使用的函数的名称为 xhr(),它能接受 5 个参数:返回信息的页面的 URL、包含名称和请求参数的值的两个数组、HTTP 方法和用来处理 Ajax 响应的一个回调函数。现在,您的应用程序将会包含更为简单的函数,比如清单 2 中所示的 getInfo2(),而且代码也更容易维护。如果想要更改发送 Ajax 请求的代码,只能修改 xhr() 函数。

清单 2. 使用泛型函数
function xhr(url, paramNames, paramValues, method, callback) {
    // send Ajax request ...
}

function getInfo2(country, city) {
    function processResponse(request) {
        // process Ajax response ...
    }
    xhr("CityInfo.jsp", ["country", "city"], [country, city],
        "GET", processResponse);
}

清单 2 包含的泛型函数,名为 xhr(),特定于应用程序的函数名为 getInfo2()。泛型代码应被移入单独的 JavaScript 文件以便能将可重用函数导入需要它们的任何页面。对于特定的代码,比如 getInfo2() 函数,如果应用程序需要基于相同模式的很多函数,就应该考虑使用代码生成器。
动态生成代码
    代码生成器能显著提高开发和维护 Web 应用程序的效率。比如,您可以使用 JSP、Java 代码或任何其他语言来从模板生成 JavaScript 函数。得益于属性名称,用来指定生成器参数的基于 XML 的语法能让代码可读性更好,也更容易理解。此外,标记属性没有像 JavaScript 函数或 Java 方法的参数那样的固定顺序。
    对比起来,使用 XML 标记的一个明显优点就是它们能为属性使用默认值,然而编程语言只提供了有限的可能性来删除方法参数。请考虑这些关于代码生成器可扩展性的诸多方面,因为在不打乱现有代码的情况下向标记增加新的属性要比更改方法签名简单得多。使用 XML 和 JSP 的这些句法方面的优点是很明显的,尤其是当代码生成器需要大量属性的时候。
JSP 是在服务器端生成 JavaScript 代码的一种很好的选择,因为:
开发人员已经了解 JSP 语法。
JSTL 为条件及循环结构提供了标记。
   JSP 页面让您可以很方便地生成任何形式的文本,包括 JavaScript 函数。
   此外,JSP 技术已经有了一种很强大的机制来将可执行代码放在定制标记后面,所以,不必为基于 JSP 语法的模板实现解析器。最后,在每次您做更改时,也不需要外部工具来重新生成代码。
清单 3 中包含取自于本文下一章节所要展示的应用程序的一个代码片段。此处使用了一个名为 <da:xhr> 的定制的 JSP 标记来生成 getInfo3() 函数,当用户单击一个标签为 Get Info 的按钮时,该函数就会在 Web 浏览器中被调用。

清单 3. 使用 JSP 标记文件生成 Ajax 函数
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>
...
<script type="text/javascript">
    <da:xhr function="getInfo3(country, city)" url="CityInfo.jsp" method="GET">
        // process Ajax response ...
    </da:xhr>
</script>
...
<button ... onclick="getInfo3(...)">Get Info</button>


   所生成的 JavaScript 代码可被放入调用清单 3 中所示的生成函数的 Web 页面的 <script> 元素中。如果多个 Web 页面需要同样的 JavaScript 代码,就像任何常规的 JavaScript 文件一样, 动态生成此代码的一个单独的 JSP 文件将被导入到这个应用程序的 Web 页面,在 <script> 元素的 src 属性中指定其 URI(参见清单 4)。

清单 4. 导入由 JSP 页面生成的 JavaScript
<script src="DynamicJavaScript.jsp" type="text/javascript">
</script>

缓存生成的代码
虽然在开发阶段为每个请求生成 JavaScript 代码并不会产生问题,但您不得不考虑在生产环境中的性能损失。解决的方法就是缓存代码,比如用 SJTL 将生成的代码存储到 JSP application 作用域,如清单 5 所示。然后,可以在 Web 页面中用 EL 结构(例如 ${applicationScope.cachedCode})插入所缓存的代码。

清单 5. 缓存生成的代码
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:if test="${empty cachedCode}">
    <c:set var="cachedCode" scope="application">
        alert("Cached Code");
    </c:set>
</c:if>

${applicationScope.cachedCode}


创建一个简单的 Ajax 应用程序
本节描述了示例应用程序的 JSP 页面。CityForm.jsp 页面包括一个 Web 表单,其数据由 Ajax 发送到 Web 服务器。另一个名为 CityInfo.jsp 的页面生成 Ajax 响应。
构建 Ajax 页面
此示例应用程序的 CityForm.jsp 页面使用了两个定制标记,名称分别为 <da:xhr> 和 <da:innerHTML>,是作为 JSP 标记文件实现的。xhr.tag 文件生成能发送 Ajax 请求的一个 JavaScript 函数,而 innerHTML.tag 则生成单一一行代码,它用 innerHTML 属性在 HTML 元素内插入一些内容。两个标记文件的 JSP 代码将在本文的稍后部分给出。
JSP 页面(见清单 6)声明了所使用的标记库,它们是 JSTL Core(前缀为 c)和标记文件库(前缀为 da)。CityForm.jsp 还导入了两个 JavaScript 文件,名字分别为 ajax.js 和 encode.js ,其函数从由 <da:xhr> 和 <da:innerHTML> 生成的代码调用。这些定制标记用于在 <script> 元素内生成名为 getInfo() 的一个 JavaScript 函数的代码。

清单 6. CityForm.jsp 示例
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>

<html>
<head>
<title>Ajax Demo</title>
    <script src="ajax.js" type="text/javascript">
    </script>
    <script src="encode.js" type="text/javascript">
    </script>
    <script type="text/javascript">
        <da:xhr function="getInfo(country, city)" method="GET"
                url="CityInfo.jsp" sync="false" json="true" cpr="true">
            <da:innerHTML id="info" value="json.info" encode="true"/>
        </da:xhr>
        
        function getInfo2(form) {
            var country = form.country.options[form.country.selectedIndex].text;
            var city = form.city.value;
            getInfo(country, city);
        }
    </script>
</head>
<body>
    <form name="data">
        Country:
        <select name="country" size="1">
            <option>Canada</option>
            <option>UK</option>
            <option selected>USA</option>
        </select>
        City: <input name="city" size="20">
        <button type="button" onclick="getInfo2(this.form)">Get Info</button>
    </form>
    <div id="info"></div>
</body>
</html>


CityForm.jsp 页面的 Web 表单包含一个标签为 Get Info 的按钮、一个国家列表和一个让用户输入城市名称的输入字段。当用户单击这个按钮时,Web 浏览器将调用 getInfo2() 函数,它的调用是在 onclick 属性内编码的。此函数包含 Web 表单中的 country 和 city 字段的值,并且会将这些值传递给 getInfo() 函数,该函数会将 Ajax 请求发送给服务器。Ajax 响应将包含需要插入到置于 Web 表单下的 <div> 元素的信息。
<da:xhr> 的属性允许指定已生成的 JavaScript 函数的头、用来发送 Ajax 请求的 HTTP 方法以及生成 Ajax 响应的那个页面的 URL。名为 sync、json 和 cpr 的属性则可以让您指定已生成代码的各种特性。
如果 sync 为 true,那么信息就会被同步请求,这意味着当从服务器检索数据时,用户界面将会被阻塞。 如果 sync 为 false,请求就是异步的,意味着在信息通过网络传递和由服务器处理时,用户可以进行操作。
如果 json 属性为 true,那么这个代码生成器将会用 eval(request.responseText) 添加一行 JavaScript 代码来评估 Ajax 响应。最后,如果 <da:xhr> 标记的 cpr 属性是 true,那么 xhr.tag 文件将产生一些 JavaScript 代码片段,它将在发送一个新请求前关闭之前的请求。我在讨论 ajax.js 和 xhr.tag 文件时,将会介绍更多关于此特性的内容。
所生成的代码
清单 7 显示了 CityForm.jsp 页面已生成的 getInfo(country, city) 函数。所使用的 HTTP 方法是 GET,生成 Ajax 响应的页面的 URL 是 CityInfo.jsp,sync 属性是 false,json 与 cpr 属性都是true。所生成的 JavaScript 代码使用 ajax.js 文件的 openRequest()、sendRequest()、closeRequest() 和 httpError() 函数,以及 encode.js 的 appendParam() 与 htmlEncode() 函数。

清单 7. 为使用 GET 的 Ajax 请求所生成的函数
var getInfoRequest = null;

function getInfo(country, city) {
    if (getInfoRequest) closeRequest(getInfoRequest);

    var url = "CityInfo.jsp";
    url += '?'; 
    url = appendParam(url, "country", country);
    url = appendParam(url, "city", city);

    var request = openRequest("GET", url, true);
    getInfoRequest = request;
    if (request == null) return null;

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                eval(request.responseText);
                document.getElementById("info").innerHTML 
                    = htmlEncode(json.info);
            } else {
                httpError(request);
                document.location = url;
            }
        }
    }

    request.onreadystatechange = processResponse;
    sendRequest(request, null);
    return request;
}


如果将 HTTP 方法改为 POST,那么所生成的代码也会相应地被修改,如清单 8 中所示。这里并没有向 url 添加请求参数,相反,getInfo() 函数会将这些参数追加给一个名为 body 的变量,此变量之后会被传递给 ajax.js 文件的 sendRequest() 函数。

清单 8. 为使用 POST 的 Ajax 请求所生成的函数
function getInfo(country, city) {
    ...
    var url = "CityInfo.jsp";
    
    var body = "";
    body = appendParam(body, "country", country);
    body = appendParam(body, "city", city);

    var request = openRequest("POST", url, true);
    ...
    sendRequest(request, body);
    return request;
}


生成 JSON 响应
CityInfo.jsp 页面(见清单 9)使用 JavaScript Object Notation (JSON) 生成对 Ajax 请求的响应。为了保持页面无脚本,Java 代码将被移入名为 noCache.tag 和 jstring.tag 的两个 JSP 标记文件,它们将由 <da:noCache> 和 <da:jstring> 从 JSP 页面调用。将 Java 代码放入 JSP 标记文件要比开发标记处理程序类容易得多,因为 JSP 容器会在不必重启应用程序的情况下为您生成这些类并且会在代码更改后自动重新编译 Java 代码。

清单 9. CityInfo.jsp 示例
<%@ taglib prefix="da" tagdir="/WEB-INF/tags/dynamic/ajax" %>

<da:noCache/>

json = {
    country: <da:jstring value="${param.country}"/>,
    city: <da:jstring value="${param.city}"/>,
    info: <da:jstring>Info on ${param.city}, ${param.country}</da:jstring>
}


清单 10 给出了 JSON 响应。

清单 10. 所生成的 JSON
json = {
    country: "UK",
    city: "London",
    info: "Info on London, UK"
}


noCache.tag 文件(参见清单 11)包含单一一行 Java 代码,可以设置 HTTP 响应的 Cache-Control 头。

清单 11. noCache.tag 文件
<% response.setHeader("Cache-Control", "no-cache"); %>


jstring.tag 文件(如清单 12 所示)编码一个 JavaScript 字符串,其值可以作为属性,也可以作为内容主体传递给标记文件。如果 value 属性不指定,<jsp:doBody> 动作就会执行包括在 <da:jstring> 和 </da:jstring> 之间的 JSP 代码,设置 page 作用域的 value 变量。 在这两种情况下,Java 代码用 jspContext.getAttribute() 获得字符串值并会逐个地输出字符,转义特殊和非 ASCII 字符。

清单 12. jstring.tag 文件
<%@ attribute name="value" required="false" rtexprvalue="true" %>

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

<c:if test="${empty value}">
    <jsp:doBody var="value"/>
</c:if>

<%
    String value = (String) jspContext.getAttribute("value");
    out.write('"');
    int len = value.length();
    for (int i = 0; i < len; i++) {
        char ch = value.charAt(i);
        switch (ch) {
            case '\\': out.write("\\\\"); break;
            case '\n': out.write("\\n"); break;
            case '\r': out.write("\\r"); break;
            case '\t': out.write("\\t"); break;
            case '"':  out.write("\\\""); break;
            default: {
                if (' ' <= ch && ch <= '~')
                    out.write(ch);
                else {
                    out.write("\\u");
                    for (int j = 3; j >= 0; j--) {
                        int k = (((int) ch) >> (j << 2)) & 0x0f;
                        out.write((char) (k < 10 ? k + 48 : k + 55));
                    }
                }
            }
        }
    }
    out.write('"');
%>


开发 JavaScript 函数
本节介绍 ajax.js 和 encode.js 文件,其函数均从由 xhr.tag 和 innerHTML.tag 文件生成的 JavaScript 代码调用。
XMLHttpRequest 相关的函数
ajax.js 文件的 openRequest() 函数(参见清单 13)接受 3 个参数(method、url 和 async)并会创建一个 XMLHttpRequest 实例。然后,它会调用 open() 方法并返回初始化了的 request 对象。如果 body 参数不是 null,sendRequest() 函数就会设置 Content-Type 头并调用 request 对象的 send() 方法。

清单 13. ajax.js 文件
function openRequest(method, url, async) {
    var request = null;
    if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");
    else if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    if (request)
        request.open(method, url, async);
    return request;
}

function sendRequest(request, body) {
    if (body)
        request.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded");
    request.send(body);
}

function closeRequest(request) {
    request.onreadystatechange = function() { };
    request.abort();
    delete request;
}

function httpError(request) {
    alert("Http Error: " + request.status);
}


closeRequest() 方法将 onreadystatechange 属性设置为一个空函数,调用 abort() 方法并之后使用 JavaScript 的 delete 操作符释放 request 对象的内存。该函数应该在处理完 Ajax 请求之后对每个 XMLHttpRequest 实例调用。否则,很可能导致 Web 浏览器内的内存泄露。ajax.js 的最后一个函数是 httpError(),它在警告窗口显示请求的状态。
HTML 和 URL 编码函数
encode.js 文件的 htmlEncode() 函数(参见清单 14)接受字符串参数并会用 &amp;、&lt; 和 &gt; 替换 &、< 和 > 字符。attrEncode() 函数执行的是同样的操作,之后用 &quot; 替换 " 字符以便所编码的字符串可用作属性的值。
JavaScript 的 escape() 函数通常用来编码 Ajax 请求的请求参数。应该注意 escape() 没有编码 + 字符,这是个问题,因为所有的 + 均会在服务器上被解码为空格字符。
上面谈及的问题可由 encode.js 文件的 urlEncode() 函数修复,此文件使用了 JavaScript 的 escape() 来执行 URL 编码,并会在之后用 %2B 替换所有的 + 字符以便编码后的字符串可以在服务器端正确解码。

清单 14. encode.js 文件
function htmlEncode(value) {
    return value ? value.replace(/&/g, "&amp;")
        .replace(/</g, "&lt;").replace(/>/g, "&gt;") : "";
}

function attrEncode(value) {
    return htmlEncode(value).replace(/"/g, "&quot;");
}

function urlEncode(str) {
    return str ? escape(str).replace(/\+/g, "%2B") : "";
}

function isArray(a) {
    return a.sort ? true : false;
}

function appendParam(url, name, value) {
    if (isArray(value)) {
        for (var i = 0; i < value.length; i++)
            url = appendParam(url, name, value[i]);
    } else {
        if (url && url.length > 0) {
            if (url.charAt(url.length-1) != '?')
                url += "&";
        } else
            url = "";
        url += urlEncode(name) + "=" + urlEncode(value);
    }
    return url;
}


appendParam() 函数向给定 URL 添加名称/值对。如果第三个参数是一个数组,JavaScript 代码就会在其元素上迭代并会递归调用 appendParam(),以便针对数组中的每个元素将名称/值对添加至 url。
使用 JSP 标记文件生成 JavaScript 代码
本节介绍的是 xhr.tag 和 innerHTML.tag 文件。前者生成能将 Ajax 请求发送给服务器的 JavaScript 函数,后者则生成能在 Web 浏览器内处理 Ajax 请求的回调函数的代码。
发送 Ajax 请求
<da:xhr> 标记接受 6 个属性:所生成的 JavaScript 函数的头部、HTTP 方法、将响应返回给 Ajax 请求的页面的 URL、指示请求应该同步还是异步发送的一个布尔属性、指定请求是否使用 JSON 格式的另一个布尔属性以及用来启用本文称为 “关闭之前请求” 特性的属性(cpr)。所有这些属性均通过 <%@attribute%> 指令在 xhr.tag 文件中声明(参见清单 15)。
接下来,标记文件使用 <%@taglib%> 指令声明所使用的标记库,将 method 属性的字符转变成大写,定义称为 reqVarName 的 JSP 变量,该变量构建自所生成的 JavaScript 函数的名称和 Request 字符串。这之后,xhr.tag 文件开始生成 JavaScript 代码。如果 cpr 属性为 true,就会声明一个 JavaScript 变量并由 null 对该变量进行初始化。此变量用来保存之前的请求,而该请求必须在下次调用所生成的函数时被 “关闭”。本文前一章节已经介绍过 ajax.js 文件的 closeRequest() 函数。
<dau:appendParams> 标记用于 xhr.tag 文件以便向 url 变量追加请求参数(如果 method 为 GET)或向 body 变量追加请求参数(如果 method 为 POST)。ajax.js 文件的 openRequest() 函数创建和初始化 XMLHttpRequest 实例。Ajax 请求通过 sendRequest() 函数发送给服务器,该函数的代码可以在同一个 ajax.js 文件找到。

清单 15. xhr.tag 文件
<%@ attribute name="function" required="true" rtexprvalue="true" %>
<%@ attribute name="method" required="false" rtexprvalue="true" %>
<%@ attribute name="url" required="true" rtexprvalue="true" %>
<%@ attribute name="sync" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>
<%@ attribute name="json" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>
<%@ attribute name="cpr" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="dau" tagdir="/WEB-INF/tags/dynamic/ajax/util" %>

<c:set var="method" value="${empty method ? 'GET' : fn:toUpperCase(method)}"/>
<c:set var="reqVarName"
    value="${fn:trim(fn:substringBefore(function, '('))}Request"/>

<c:if test="${cpr}">var ${reqVarName} = null;</c:if>

function ${function} {
    <c:if test="${cpr}">if (${reqVarName}) closeRequest(${reqVarName});</c:if>

    var url = "${url}";
    <c:if test="${method == 'GET'}">
        url += '?';
        <dau:appendParams jsVarName="url" function="${function}"/>
    </c:if>
    <c:if test="${method == 'POST'}">
        var body = "";
        <dau:appendParams jsVarName="body" function="${function}"/>
    </c:if>

    var request = openRequest("${method}", url, ${!sync});
    <c:if test="${cpr}">${reqVarName} = request;</c:if>
    if (request == null) return null;

    function processResponse() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                <c:if test="${json}">eval(request.responseText);</c:if>
                <jsp:doBody/>
            } else {
                httpError(request);
                <c:if test="${method == 'POST'}">url += '?' + body;</c:if>
                document.location = url;
            }
        }
    }

    request.onreadystatechange = processResponse;
    <c:if test="${method == 'GET'}">sendRequest(request, null);</c:if>
    <c:if test="${method == 'POST'}">sendRequest(request, body);</c:if>
    return request;
}


内部函数名为 processResponse(),是通过 onreadystatechange 属性传递给 request 对象的一个回调函数。此回调函数在 Ajax 请求的整个生命周期进行了多次调用,而请求的当前状态可从 readyState 属性获得。当 readyState 为 4 时,请求完成。
如果 HTTP 状态码为 200,表示没有发生错误,并且 Ajax 请求也可以由 <da:xhr> 和 </da:xhr> 之间的 JSP 代码处理。此代码由 <jsp:doBody/> 自 xhr.tag 文件调用。如果出现 HTTP 错误,所生成的代码会调用 ajax.js 文件的 httpError() 函数,并且浏览器也会被重定向到生成 Ajax 响应的 URL,因此开发人员就能够看到导致 HTTP 错误的服务器端错误。例如,如果 HTTP 错误码为 500(内部错误),应该可以看到一个 Java 堆栈跟踪。
清单 16 给出了此 appendParams.tag 文件,该文件在给定函数头的参数上迭代并生成一个 JavaScript 代码行,此代码行调用 encode.js 文件的 appendParam() 函数。本文前面介绍的清单 7 和 8 中给出了所生成的代码。

清单 16. appendParams.tag 文件
<%@ attribute name="jsVarName" required="true" rtexprvalue="true" %>
<%@ attribute name="function" required="true" rtexprvalue="true" %>

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

<c:set var="paramList"
    value="${fn:substringBefore(fn:substringAfter(function, '('), ')')}"/>

<c:forEach var="paramName" items="${paramList}">
    <c:set var="paramName" value="${fn:trim(paramName)}"/>
    ${jsVarName} = appendParam(${jsVarName}, "${paramName}", ${paramName});
</c:forEach>



developerWorks Ajax 资源中心
请访问 Ajax 资源中心,这是有关开发 Ajax 应用程序所需的免费工具、代码和信息的一站式中心。由 Ajax 专家 Herrington 主持的 活跃 Ajax 社区论坛 也许可以帮助您解答疑问。
处理 Ajax 请求
innerHTML.tag 文件(如清单 17 所示)中有一行 JavaScript 代码,包含 DOM 对象,用来代表具有给定 id 的 HTML 元素,所使用的是 document.getElementById()。随后,该元素的内容会被更改,方式是设置 innerHTML 属性,其新值可以通过 <da:innerHTML> 标记的 value 属性传递,也可以在标记体内传递。如果 encode 属性为 true,所生成的代码就会调用 encode.js 文件的 htmlEncode() 函数。

清单 17. innerHTML.tag 文件
<%@ attribute name="id" required="true" rtexprvalue="true" %>
<%@ attribute name="value" required="false" rtexprvalue="true" %>
<%@ attribute name="encode" required="false" rtexprvalue="true"
    type="java.lang.Boolean" %>

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

<c:if test="${empty value}">
    <jsp:doBody var="value"/>
</c:if>
<c:if test="${encode}">
    document.getElementById("${id}").innerHTML = htmlEncode(${value});
</c:if>
<c:if test="${!encode}">
    document.getElementById("${id}").innerHTML = ${value};
</c:if>


<da:innerHTML> 标记可用在 <da:xhr> 之内以处理 Ajax 响应,如本文的示例应用程序所示。您可以构建类似的标记文件以根据应用程序的具体需要执行不同的处理操作。
结束语
在本文,您接触了如何利用 JSP 标记文件生成 Ajax 例程。您可以使用相同的技术生成服务器端其他类型的 JavaScript 函数,这样一来,您就能够更容易地添加或更改特性,因为每次修改了充当模板的标记文件时,代码都会自动重生成。请继续关注本系列的 下一篇文章,您将会了解如何使用数据绑定、页面导航和样式约定以最小化设置和配置。

参考资料
学习
您可以参阅本文在 developerWorks 全球站点上的 英文原文 。

developerWorks Web 开发专区 富含 Web 2.0 开发的各种工具和信息。

developerWorks Ajax 资源中心 包含不断增加的大量 Ajax 内容以及有用资源,可以让您立即开始开发 Ajax 应用程序。

访问 developer.mozilla.org 获得 JavaScript 工具和文档。

访问 JSP 主页 获得有关 JavaServer Pages 的更多信息。

阅读 Andrei Cioroianu 所著的更多 developerWorks 文章,这些文章涵盖了可用来增强应用程序开发的中高级 Ajax 技巧。

随时关注 developerWorks 技术活动和网络广播。

浏览 技术书店 获得有关这些主题以及其他技术主题的书籍。

讨论
参与论坛讨论。

参与 developerWorks blogs 加入 developerWorks 社区。

关于作者
Andrei Cioroianu 是 Devsphere 公司的一名高级 Java 开发人员和顾问,该公司专门提供定制 Java EE 开发服务以及 Ajax/JSF 顾问服务。您可以通过 www.devsphere.com 的联系表单与 Andrei 联系。
分享到:
评论

相关推荐

    简化 AJAX 和 JAVA 开发-第 1 部分 用 JSP 标记文件动态生成 JAVASCRIPT 代码.doc

    简化 AJAX 和 JAVA 开发-第 1 部分 用 JSP 标记文件动态生成 JAVASCRIPT 代码

    JAVA上百实例源码以及开源项目

    2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

    JAVA上百实例源码以及开源项目源代码

    2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

    java开源包8

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包1

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包10

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包11

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包6

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包4

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包9

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包5

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包101

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包3

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包2

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    java开源包7

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    Java资源包01

    ftp4j是一个FTP客户端Java类库,实现了FTP客户端应具有的大部分功能文件(包括上传和下 载),浏览远程FTP服务器上的目录和文件,创建、删除、重命,移动远程目录和文件。ftp4j提供多种方式连接到远程FTP服务器包括...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    1、将业务层与表示层分离:使用JSP技术,网络开发人员可充分使用HTML来设计页面显示部分(如字体颜色等),并使用JSP指令或者JAVA程序片段来生成网页上的动态内容; 2、能够跨平台:JSP支持绝大部分平台,包括现在...

    GoodProject Maven Webapp.zip

    JSP技术有点类似ASP技术,它是在传统的网页HTML(标准通用标记语言的子集)文件(*.htm,*.html)中插入Java程序段(Scriptlet)和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。 用JSP开发的Web应用是跨平台的,既能在...

Global site tag (gtag.js) - Google Analytics