您的位置:首页 > 理论基础 > 计算机网络

利用 XMLHttpRequest 提交 JSF 表单

2008-09-12 23:39 239 查看
导读:
  级别: 中级
  Andrei Cioroianu, 高级 Java 开发人员和顾问, Devsphere
  2007 年 8 月 22 日
  在这个包含两部分的系列文章中,作家兼 Java ?开发人员 Andrei Cioroianu 将教您如何使用 Ajax(Asynchronous JavaScript + XML)技术和 JSF(JavaServer Faces)技术自动保存 Java Web 应用程序中的表单数据。您将学会如何用 Ajax 提交 Web 表单、如何使用 JSF 框架处理 Ajax 请求、如何控制 JSF 请求处理生命周期、如何在服务器端管理表单数据,以及如何识别浏览器会话间的匿名用户。此外,本系列还将探究几个经常发生的开发错误,包括错误的表单数据编码和可能导致请求失败及内存泄漏的错误的 Ajax 请求管理。
  简介
  很多桌面应用程序允许用户随时保存文件,还有一些产品会自动保存正在编辑的文件以最小化因程序崩溃而造成的数据损失。当用户与 Web 应用程序进行交互时,通常只有当表单提交给服务器时,用户的数据才会被保存。大多数 Web 应用程序不允许用户保存只部分填充的表单,关闭浏览器,然后再继续此任务。此外,如果由于网络问题突然中断了连接,用户的数据将得不到保存,他们的某些工作成果也有可能会因此丢失。
  developerWorks Ajax 资源中心
  请访问 Ajax 资源中心,这里几乎囊括了关于 Ajax 编程模型的所有信息,包括各种文章、教程、论坛、博客、wikis、活动和新闻。
  Ajax 是解决这类问题的理想解决方案。当使用 Ajax 提交表单数据时,页面不必刷新,滚动条的位置也可以保持不变,就好像所处理的是一个桌面应用程序一样。若用户需要填写复杂表单或者面临数据丢失的风险,他们将会非常青睐这种自动保存功能。例如,假设用户在测试一个产品,当他们填写支持表单以便报告测试问题时,这个 Web 支持表单就应该能定期地自动保存。在测试产品时,用户的系统可能会变得不稳定,在提交完整的表单前,他们可能需要多次重启计算机。表单自动保存功能可以节省时间并且能够防止这种情况下的数据丢失。
  作为包含两部分的系列文章中的第 1 部分,本期的重点将放在如何用 Ajax 发送表单数据以及如何用 JSF 处理 Ajax 请求。它将向您展示实现数据自动保存的完整数据流程,内容涵盖如何使用 JavaScript 在 Web 浏览器中获取、编码和提交表单数据。本篇文章还会涉及到 JSF 侦听程序如何在服务器端处理已提交的数据,定制 JSF 请求处理生命周期以使它能有效地处理 Ajax 请求。无论是否需要表单自动保存功能或其他类似功能,您都可以将本篇文章介绍的技术应用到基于 Ajax 和 JSF 的任何 Java Web 应用程序中。
  当用户与 Web 应用程序进行交互时,通常只有当表单提交给服务器后,用户的数据才会被保存。大多数 Web 应用程序不允许用户保存只部分填充的表单、关闭浏览器,然后再继续此任务。此外,如果由于网络问题突然中断了连接,用户的数据将得不到保存,他们的某些工作成果也有可能会因此丢失。Ajax 是解决这类问题的理想解决方案。
  在客户端获取表单数据
  本节将给出一个 JSF 表单,其数据通过 JavaScript 和 DOM 在 Web 浏览器中获得。 您可以在自已的 Web 表单应用程序中重用这里介绍的 JavaScript 代码。本节还将解释如何正确地编码表单数据以将它提交给服务器。
  构建 JSF 表单
  让我们先来看一个典型的 JSF 例子。SupportForm.jsp这个页面包括一些基本 HTML 的元素,比如输入框、列表、单选按钮、复选框和提交按钮。所有输入组件都将其值绑定到称为 SupportBean的 JavaBean 属性。这个页面的头部包括一个 标记,用来导入 AutoSaveScript.js文件(请参阅 下载部分)。此 JavaScript 文件包括一个函数,名为 setAutoSaving(),它在 标记的 onload属性内调用,以便在 Web 浏览器加载页面后激活表单的自动保存功能。 清单 1 显示了 SupportForm.jsp页的部分源代码。
  清单 1. 包含示例 JSF 表单的 SupportForm.jsp 页
  

<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>


Support Form





Support Form



...








...










  当用户为了打开 JSF 页面而单击一个 JSF 链接或输入一个 URL 时,Web 浏览器会构建 HTTP 请求并把此请求发送至 Web 服务器,服务器识别包含页面的应用程序并会调用 FacesServlet(在 web.xml内配置)来处理此请求。在进行了某些上下文初始化后,就会执行此页面,而且,JSF 框架还会创建组件树以镜像该 Web 页面所用的 JSF 标记。这些组件的呈现程序生成含有表单元素的 HTML 代码(见清单 2)。
  清单 2. SupportForm.jsp 页面生成的 HTML 代码
  



Support Form




Support Form

Name:


...
Platform:

WindowsLinuxMac
...
Problem:












  在这个 HTML 表单的结尾,有一些隐藏元素。如果 javax.faces.STATE_S***ING_METHOD参数在 web.xml文件中被设为 client,那么 JSF 实现会内部使用这些隐藏元素以识别所提交的表单和存储组件树在请求间的状态。从浏览器的角度看,一个 JSF 表单与其他 HTML 表单无异,可以使用 JavaScript 和 DOM 在 Web 浏览器中访问表单元素。

  获取及编码表单数据

  AutoSaveScript.js文件包含一个称为 getFormData()的 JavaScript 函数,它获取一个 form对象并对其元素进行迭代以构建包含名称-值对的字符串。这个字符串遵循标准的 application/x-www-form-urlencoded格式,用 &分隔参数,并在每个参数的名称和值之间使用 =。内部函数 addParam()可以对单一参数进行编码,所使用的是 JavaScript API 提供的 escape()函数。escape()函数用 %后跟被编码字符的两位 16 进制码代替了几乎所有非字母数字的 ASCII 字符。 我之所以说 “几乎所有非字母数字的 ASCII 字符”,是因为除此之外,还有字母数字字符以及无编码的其他一些字符(例如+)。

  例如,如果将字符串 a + b传递给 escape(),结果将是 a%20+%20b(20是空格符的 16 进制码)。这是一个符合 RFC 1738 的有效 URL 编码,但如果将这个已编码的字符串提交给服务器端脚本,例如 JSP,结果将是一个空格而不是 +字符。这也是正确的,因为描述 application/x-www-form-urlencoded格式的 RFC 1866 中规定空格符的编码为 +,并且所有非字母数字字符都以一个 %后跟 16 进制码替代。当服务器对这个字符串进行解码时,任何 +字符都会被还原成一个空格。

  总之,由 escape()执行的 URL 编码并不完全与 application/x-www-form-urlencoded一样,因为 escape()将 +字符保留,不做编码,而在 application/x-www-form-urlencoded中,空格则被编码为 +字符。解决这个问题最简单的方法是将所有 +字符都编码为 %2B(2B是 +的 16 进制代码)。也可以使用 replace()对 escape()的返回结果执行此操作。在本例中,可以将字符串 a + b编码为 a%20%2B%20b。清单 3 所示的是 addParam()内部函数,它将一个编码后的名称-值对添加到 getFormData()的本地 dataString变量。

  清单 3. 对单一请求参数进行编码

  

function getFormData(form) {

var dataString = "";

function addParam(name, value) {

dataString += (dataString.length >0 ? "&" : "")

+ escape(name).replace(//+/g, "%2B") + "="

+ escape(value ? value : "").replace(//+/g, "%2B");

}

...

}

  getFormData()函数获取 form对象的 elements数组,并根据每个元素的类型调用 addParam()。单个名称-值会针对每个文本框、密码和隐藏字段添加。只有当对应的表单元素被选中时,复选框和单选按钮的值才会被编码。如果是列表,单个名称-值对会针对每个选中项添加。之后,getFormData()会返回包含表单编码数据的字符串(见清单 4)。

  清单 4. 获取和编码表单数据

  

function getFormData(form) {

...

var elemArray = form.elements;

for (var i = 0; i



autosave.AutoSaveListener



...

  正如前面所说明的,验证阶段过后,对自动保存请求的处理必须终止以便 JSF 框架不会更新 JavaBean 数据模型,该模型不应受到自动保存的影响。因此,只有在验证阶段以后,侦听程序才开始接收通知,其 ID 通过 getPhaseId()方法返回。如果请求被标记为 Ajax-Request报头,那么侦听程序的 afterPhase()方法就会调用 FacesContext对象的 responseComplete()方法,告知 JSF 框架,它应该停止处理请求。 (见清单 12)。

  清单 12. 实现 JSF 阶段侦听程序

  

package autosave;

import javax.faces.component.EditableValueHolder;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.event.PhaseEvent;

import javax.faces.event.PhaseId;

import javax.faces.event.PhaseListener;

import java.util.Iterator;

import java.util.Map;

public class AutoSaveListener implements PhaseListener {

public PhaseId getPhaseId() {

return PhaseId.PROCESS_VALIDATIONS;

}



public void beforePhase(PhaseEvent e) {

}

public void afterPhase(PhaseEvent e) {

System.out.println();

System.out.print(e.getPhaseId());

System.out.print(" - ");

FacesContext ctx = e.getFacesContext();

Map headers = ctx.getExternalContext().getRequestHeaderMap();

if ("Auto-Save".equals(headers.get("Ajax-Request"))) {

System.out.println("Auto-Save");

ctx.responseComplete();

} else

System.out.println("Submit");

printTree(ctx.getViewRoot(), 0);

}

...

}

  打印组件树

  除了在合适的时候停止请求处理外,侦听程序会通过称为 printTree()的递归方法打印组件树。此方法会输出组件成员、呈现程序类型、惟一 ID 及每个组件验证后的值(见清单 13)。

  清单 13. 打印 JSF 组件树

  

public class AutoSaveListener implements PhaseListener {

...

public void printTree(UIComponent comp, int level) {

if (comp == null)

return;

Object value = null;

if (comp instanceof EditableValueHolder)

value = ((EditableValueHolder) comp).getValue();

for (int i = 0; i http://www.ibm.com/developerworks/cn/web/wa-aj-jsf1.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: