发送请求。在先前的文章中,我介绍了 XMLHttpRequest API 和一个实用函数 sendHttpRequest(),您可以在提供下载的示例中的 ajaxUtil.js 文件中找到。
该函数有四个参数(HTTP 方法、URL、一个参数数组和一个回调),可创建 XMLHttpRequest 对象,设置其属性并调用 send() 方法。如果提供了回调参数,则异步发送请求,并在收到响应后调用回调函数。否则,将同步发送请求,您可以在 sendHttpRequest() 返回后即刻处理响应。如您所见,在使用 XMLHttpRequest 时必须进行一些重要选择
- 将要使用的 HTTP 方法(GET 或 POST)
- 用于编码请求参数的格式(本文前面已探讨了 XML 和 URL 编码)
- 是进行同步(等待响应)调用还是异步(使用回调)调用
- 响应的格式,如 XML、XHTML、HTML 或 JavaScript Object Notation (JSON)(本文稍后将对此进行探讨)。
假设您希望从数据馈送了解一些股价信息,且无需用户干预即可定期刷新信息。在本例中,应异步发送 HTTP 请求,这是为了在检索信息时不阻塞用户界面。请求参数是一个符号数组,可在 URL 中进行编码。由于服务器可能超载,因此您不希望在进行频繁请求时发送 XML 文档。由于您只对最新的股价感兴趣,因此应终止任何未完成的先前请求:
var ctrlURL = "ajaxCtrl.jsp";
var feedRequest = null;
function sendInfoRequest(symbols, callback) {
if (feedRequest)
abortRequest(feedRequest);
var params = new Array();
for (var i = 0; i < symbols.length; i++)
params[i] = {
name:"symbol",
value:symbols[i]
};
feedRequest = sendHttpRequest(
"GET", ctrlURL, params, callback);
}
在调用请求对象的 abort() 方法之前,abortRequest() 函数(可在 ajaxUtil.js 文件中找到)会将 onreadystatechange 属性设置为不执行任何操作的回调。此外,删除该请求对象以避免内存泄漏,这点至关重要:
function abortRequest(request) {
function doNothing() {
}
request.onreadystatechange = doNothing;
request.abort();
delete feedRequest;
}
我们来考虑另一种情况:在传输要保存在数据库中的整个用户数据时,应同步发送请求,因为您可能不希望用户在保存这些数据进行时对其进行修改。在这种情况下,首选 XML 格式,这是因为在文档中进行对象模型编码通常要比使用很多字符串参数更简单。此外,保存数据的请求并不频繁,服务器可以毫无问题地处理负载。可将 XML 文档编码为参数,这样您就可以使用 EL 语法 (${param.xml}) 在 JSP 页面中访问该文档了。以下就是发送在 XML 文档中编码的模型数据的函数:
function sendSaveRequest(xml) {
var params = [ { name:"xml", value:xml } ];
var saveRequest = sendHttpRequest("POST", ctrlURL, params);
if (saveRequest)
delete saveRequest;
}
如果需要恢复对象模型,则也可同步发送请求,从服务器检索数据。在这种情况下,服务器应当返回一个 JSON 响应,以便您可利用 eval(loadRequest.responseText) 轻松将其转换为 JavaScript 对象树:
function sendLoadRequest() {
var model = null;
var loadRequest = sendHttpRequest("GET", ctrlURL);
if (loadRequest) {
model = eval(loadRequest.responseText);
delete loadRequest;
}
return model;
}
以下两部分介绍了通常在服务器上对 XML 文档执行的操作,以及如何响应 Ajax 请求。
在服务器端处理请求
Servlet/JSP 容器分析各个 HTTP 请求并创建一个 ServletRequest 实例,该实例使您可以通过 getParameter() / getParameterValues() 获得请求参数,或通过 getInputStream() 获得请求正文。在 JSP 页面中,也可以使用 EL 语法(${param...} 和 ${paramValues...})获得这些参数。请注意,只有在 Ajax 客户端使用了类似于 buildQueryString() 之类的实用函数,通过 application/x-www-form-urlencoded 格式来编码数据(本文前一部分有述)的情况下,才可通过 getParameter() 或 ${param...} 获得请求参数。如果在客户端上将 XML 文档或 DOM 树传递至 XMLHttpRequest 的 send() 方法,则必须在服务器端使用 ServletRequest 的 getInputStream() 方法。
数据验证。典型的 Web 应用程序会进行许多数据验证操作。多数可能的错误相当简单,例如缺少请求参数、数字格式错误等等。这些错误通常是由于用户忘记输入表单元素的值或提供了无效值引起的。Web 框架(如 JSF 和 Oracle ADF Faces)非常善于处理这些用户错误。在 Ajax 应用程序中,这些错误可以在客户端使用 JavaScript 来捕获和处理。例如,您可使用 isNaN(new Number(value)) 验证数字值是否无效。
出于安全和可靠性的考虑,应当在服务器端对数据进行重新验证,而不应想当然地认为 XML 请求格式设置正确。XML 模式是在服务器端验证复杂请求的有用工具。示例代码下载中包含了一个名为 XMLUtil 的类,它提供用于加载和使用模式文档的方法。以下代码段显示了如何初始化 SchemaFactory:
import javax.xml.*;
import javax.xml.validation.*;
...
protected static SchemaFactory schemaFactory;
static {
schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setErrorHandler(newErrorHandler());
}
The newErrorHandler() method returns a SAX error handler:
import org.xml.sax.*;
...
public static ErrorHandler newErrorHandler() {
return new ErrorHandler() {
public void warning(SAXParseException e)
throws SAXException {
Logger.global.warning(e.getMessage());
}
public void error(SAXParseException e)
throws SAXException {
throw e;
}
public void fatalError(SAXParseException e)
throws SAXException {
throw e;
}
};
}
可以使用 getResourceAsStream() 查找并加载某个目录中的 XSD 文件或 CLASSPATH 中指定的 JAR:
public static InputStream getResourceAsStream(String name)
throws IOException {
InputStream in = XMLUtil.class.getResourceAsStream(name);
if (in == null)
throw new FileNotFoundException(name);
return in;
}
然后,使用 SchemaFactory 实例通过 newSchema() 方法获取 Schema 对象:
import javax.xml.validation.*;
...
public static Schema newSchema(String name)
throws IOException, SAXException {
Schema schema;
InputStream in = getResourceAsStream(name);
try{
schema = schemaFactory.newSchema(new StreamSource(in));
}finally{
in.close();
}
return schema;
}
您还可以使用以下方法创建 Oracle XMLSchema 对象:
import oracle.xml.parser.schema.XMLSchema;
import oracle.xml.parser.schema.XSDBuilder;
...
public static XMLSchema newOracleSchema(String name)
throws IOException, SAXException {
XMLSchema schema;
InputStream in = getResourceAsStream(name);
try{
XSDBuilder builder = new XSDBuilder();
schema = builder.build(new InputSource(in));
} catch (Exception e){
throw new SAXException(e);
}finally{
in.close();
}
return schema;
}
接下来,您需要创建一个 DocumentBuilderFactory。如果在 CLASSPATH 中找到的是 JAXP 1.1 实现,则由 JAXP 1.2 定义的 setSchema() 方法可能会抛出 UnsupportedOperationException,此时需要将 JAXP 1.1 实现替换为 Java SE 5.0 的 JAXP 1.2 实现。在这种情况下,您仍可使用 newOracleSchema() 创建模式对象,并通过 setAttribute()方法对其进行设置:
import javax.xml.parsers.*;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
...
public static DocumentBuilderFactory newParserFactory(
String schemaName) throws IOException, SAXException {
DocumentBuilderFactory parserFactory
= DocumentBuilderFactory.newInstance();
try{
parserFactory.setSchema(newSchema(schemaName));
} catch (UnsupportedOperationException e) {
if (parserFactory instanceof JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT,
newOracleSchema(schemaName));
}
}
return parserFactory;
}
然后,创建一个 DocumentBuilder 对象,并使用该对象验证和分析 XML 文档:
import javax.xml.parsers.*;
...
public static DocumentBuilder newParser(
DocumentBuilderFactory parserFactory)
throws ParserConfigurationException {
DocumentBuilder parser = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
return parser;
};
假设您要根据 portfolio.xsd 模式示例验证 XML 文档:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="portfolio" type="portfolioType" /> <xsd:complexType name="portfolioType"> <xsd:sequence> <xsd:element name="stock" minOccurs="0" maxOccurs="unbounded"> <xsd:complexType> <xsd:attribute name="symbol" type="xsd:string" use="required"/> <xsd:attribute name="shares" type="xsd:positiveInteger" use="required"/> <xsd:attribute name="paidPrice" type="xsd:decimal" use="required"/> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:schema>
DataModel 类的 parsePortfolioDoc() 方法使用 XMLUtil 验证和分析 xml 参数,并返回一个 DOM 文档:
private static final String SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
private static DocumentBuilderFactory parserFactory;
private static Document parsePortfolioDoc(String xml)
throws IOException, SAXException,
ParserConfigurationException {
synchronized (DataModel.class) {
if (parserFactory == null)
parserFactory = XMLUtil.newParserFactory(SCHEMA_NAME);
}
DocumentBuilder parser = XMLUtil.newParser(parserFactory);
InputSource in = new InputSource(new StringReader(xml));
return parser.parse(in);
}
现在,您拥有了一个 DOM 树,接下来要获取形成 DOM 节点所需的数据。
提取所需信息。您可以使用 DOM API 或查询语言(如 XQuery 或 XPath)来浏览 DOM 树。Java 为 XPath 提供了标准的 API,后面会用到。XMLUtil 类创建一个具有 newXPath() 方法的 XPathFactory:
import javax.xml.xpath.*;
...
protected static XPathFactory xpathFactory;
static {
xpathFactory = XPathFactory.newInstance();
}
public static XPath newXPath() {
return xpathFactory.newXPath();
}
以下方法在给定的上下文中求解 XPath 表达式,返回结果值:
import javax.xml.xpath.*;
import org.w3c.dom.*;
...
public static String evalToString(String expression,
Object context) throws XPathExpressionException {
return (String) newXPath().evaluate(expression, context,
XPathConstants.STRING);
}
public static boolean evalToBoolean(String expression,
Object context) throws XPathExpressionException {
return ((Boolean) newXPath().evaluate(expression, context,
XPathConstants.BOOLEAN)).booleanValue();
}
public static double evalToNumber(String expression,
Object context) throws XPathExpressionException {
return ((Double) newXPath().evaluate(expression, context,
XPathConstants.NUMBER)).doubleValue();
}
public static Node evalToNode(String expression,
Object context) throws XPathExpressionException {
return (Node) newXPath().evaluate(expression, context,
XPathConstants.NODE);
}
public static NodeList evalToNodeList(String expression,
Object context) throws XPathExpressionException {
return (NodeList) newXPath().evaluate(expression, context,
XPathConstants.NODESET);
}
DataModel 的 setData() 方法使用 XPath 求解方法从组合 XML 文档提取信息:
public synchronized void setData(String xml)
throws IOException, SAXException,
ParserConfigurationException,
XPathExpressionException {
try{
ArrayList stockList
= new ArrayList();
Document doc = parsePortfolioDoc(xml);
NodeList nodeList = XMLUtil.evalToNodeList(
"/portfolio/stock", doc);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
StockBean stock = new StockBean();
stock.setSymbol(
XMLUtil.evalToString("@symbol", node));
stock.setShares(
(int) XMLUtil.evalToNumber("@shares", node));
stock.setPaidPrice(
XMLUtil.evalToNumber("@paidPrice", node));
stockList.add(stock);
}
this.stockList = stockList;
} catch (Exception e){
Logger.global.logp(Level.SEVERE, "DataModel", "setData",
e.getMessage(), e);
}
}
一旦服务器端的数据模型中具备了数据,就可根据应用程序的要求对其进行处理了。然后,您必须响应 Ajax 请求。
相关专题
- Ajax技术专题 (556篇文章)
- Ajax技术应用开发 (556篇文章)
- Ajax入门与提高 (85篇文章)
- Ajax框架与实例 (220篇文章)
- AJAX应用实践 (116篇文章)
- 教你正确的理解什么是数据库恢复 (14次浏览)
- Oracle数据库系统使用的几条经验分享 (5次浏览)
- JDBC连接Oracle数据库的十个技巧 (4次浏览)
- Oracle数据库三种标准的备份方法 (4次浏览)
- Oracle数据库的四种启动方式 (3次浏览)
- 在TransactionScope中优先使用Oracle的.NET驱 (2次浏览)
- Eclipse连接Oracle数据库的具体步骤 (2次浏览)
- 如何手工创建Oracle数据库 (1次浏览)
- 提高Oracle数据库系统Import的性能 (0次浏览)
- Oracle 9i在AIX上的性能调整 (0次浏览)



