Der einzige Zweck der Ajax-Kern-API (sog. XMLHttpRequest) besteht darin, HTTP-Anfragen zum Datenaustausch zwischen dem Webbrowser und dem Server zu senden. JavaScript-Code, der auf einer Webseite ausgeführt wird, kann XMLHttpRequest verwenden, um die Anforderungsparameter an ein serverseitiges Skript zu senden, beispielsweise an ein Servlet oder eine JSP-Seite. Das aufrufende Servlet/JSP sendet eine Antwort zurück, die Daten enthält, die normalerweise zum Aktualisieren der Benutzeransicht verwendet werden, ohne die gesamte Seite zu aktualisieren. Dieser Ansatz bietet einzigartige Vorteile in Bezug auf Leistung und Benutzerfreundlichkeit, da der Netzwerkverkehr reduziert wird und die Web-Benutzeroberfläche fast genauso benutzerfreundlich ist wie die Desktop-GUI.
Die Entwicklung einer solchen Benutzeroberfläche ist jedoch nicht einfach, da Sie den Datenaustausch, die Validierung und die Verarbeitung mithilfe von JavaScript auf der Clientseite und Java (oder einer gleichwertigen Sprache) auf der Serverseite implementieren müssen. In vielen Fällen lohnt sich jedoch der zusätzliche Aufwand für den Aufbau einer Ajax-basierten Schnittstelle angesichts der damit verbundenen Vorteile.
In diesem Artikel stelle ich eine der wichtigsten Methoden zum Übertragen von Daten zwischen Ajax-Clients und -Servern vor und vergleiche die Unterschiede zwischen dem traditionellen Webanwendungsmodell und dem Ajax-Modell. Darüber hinaus werden in dem Artikel auch Techniken zur Verarbeitung von Daten auf der Server- und Clientseite untersucht.
Zunächst lernen Sie, wie Sie JavaScript verwenden, um die Parameter eines Anforderungsobjekts auf der Clientseite zu codieren. Sie können die sogenannte URL-Kodierung (die von Webbrowsern verwendete Standardkodierung) verwenden oder die Anforderungsparameter in das XML-Dokument einbinden. Der Server verarbeitet die Anfrage und gibt eine Antwort zurück, deren Daten ebenfalls verschlüsselt werden müssen. In diesem Artikel werden JavaScript Object Notation (JSON) und XML untersucht, die wichtigsten Optionen für das Antwortdatenformat.
Der Großteil dieses Artikels konzentriert sich auf XML-bezogene APIs, die häufig in Ajax-Anwendungen verwendet werden. Auf der Clientseite ist die XML-API sehr eingeschränkt, aber ausreichend. In den meisten Fällen können alle notwendigen Vorgänge mit XMLHttpRequest durchgeführt werden. Darüber hinaus kann JavaScript zum Parsen von XML-Dokumenten und zum Serialisieren von DOM-Bäumen in einem Webbrowser verwendet werden. Auf der Serverseite stehen zahlreiche APIs und Frameworks zur Verarbeitung von XML-Dokumenten zur Verfügung. In diesem Artikel wird beschrieben, wie Sie grundlegende Aufgaben mit der Standard-Java-API für XML ausführen, die XML-Schema, XPath, DOM und viele andere Standards unterstützt.
In diesem Artikel erfahren Sie mehr über die besten Techniken und die neuesten APIs für den Datenaustausch in Ajax-Anwendungen. Der beteiligte Beispielcode ist in drei Paketen enthalten: util, model und feed. Klassen im util-Paket stellen Methoden für XML-Parsing, schemabasierte Validierung, XPath-basierte Abfrage, DOM-Serialisierung und JSON-Codierung bereit. Das Modellpaket enthält Beispieldatenmodelle, die aus XML-Dokumenten initialisiert und dann in das JSON-Format konvertiert werden können. Es gibt auch ein Schema-Beispiel im Modellverzeichnis, das für die XML-Validierung verwendet werden kann. Mit den Klassen im Feed-Paket kann ein Daten-Feed simuliert werden, der alle 5 Sekunden Informationen über Ajax abruft, um die Webseite zu aktualisieren. In diesem Artikel wird erläutert, wie Sie Speicherlecks in Ihrem Webbrowser vermeiden, indem Sie ausstehende Ajax-Anfragen beenden und das XMLHttpRequest-Objekt löschen, wenn Sie es nicht mehr verwenden.
JSP- und JavaScript-Beispiele sind im Webverzeichnis enthalten. ajaxUtil.js enthält Hilfsfunktionen zum Senden von Ajax-Anfragen, zum Beenden von Anfragen und zum Behandeln von HTTP-Fehlern. Diese Datei stellt außerdem JavaScript-Dienstprogramme für XML- und URL-Kodierung, XML-Analyse und DOM-Serialisierung bereit. Die Datei ajaxCtrl.jsp fungiert als Ajax-Controller, der jede Ajax-Anfrage empfängt, Parameter an das Datenmodell weiterleitet oder die Verarbeitung bereitstellt und dann eine Ajax-Antwort zurückgibt. Die restlichen Webdateien sind Beispiele, die die Verwendung dieser praktischen Methode veranschaulichen.
Der einfachste Weg, auf der Clientseite eine Anfrage
zum Senden von Daten an einen Webserver zu erstellen, besteht darin, die Anfrage als Abfragezeichenfolge zu kodieren, die je nach verwendeter HTTP-Methode entweder an die URL angehängt oder in den Anfragetext eingefügt werden kann. Wenn Sie komplexe Datenstrukturen senden müssen, ist es eine bessere Lösung, die Informationen in einem XML-Dokument zu kodieren. Ich werde in diesem Abschnitt beide Methoden behandeln.
Anforderungsparameter verschlüsseln. Bei der Entwicklung herkömmlicher Webanwendungen müssen Sie sich keine Gedanken über die Kodierung von Formulardaten machen, da der Webbrowser dies automatisch erledigt, wenn der Benutzer die Daten übermittelt. In einer Ajax-Anwendung müssen Sie die Anforderungsparameter jedoch selbst codieren. JavaScript bietet eine sehr nützliche Funktion escape(), die alle Zeichen, die nicht Teil der URL sein können, durch %HH ersetzt (wobei HH der Hexadezimalcode ist). Beispielsweise werden alle Leerzeichen durch %20 ersetzt.
Der Beispielcode-Download stellt eine Hilfsfunktion, buildQueryString(), bereit, die aus einem Array abgerufene Parameter verkettet, den Namen und den Wert jedes Parameters durch = trennt und das &-Zeichen zwischen jedem Name-Wert-Paar platziert:
function buildQueryString(params) {
var query = "";
for (var i = 0; i < params.length; i++) {
query += (i > 0 ? "&" : "")
+ escape(params[i].name) + "="
+ escape(params[i].value);
}
Rückfrage;
}
Angenommen, Sie möchten die folgenden Parameter kodieren:
var someParams = [
{ Name: „Name“, Wert: „John Smith“ },
{ Name: "E-Mail", Wert: " [email protected] " },
{ Name:"Telefon", Wert: "(123) 456 7890" }
];
Ein Aufruf von buildQueryString(someParams) führt zu folgenden Ergebnissen:
name=John%20Smith&[email protected]&phone=%28123%29%20456%207890
Wenn Sie die GET-Methode verwenden möchten, müssen Sie die Abfrage nach dem ?-Zeichen an die URL anhängen. Bei Verwendung von POST sollte der Content-Type-Header über setRequestHeader() auf application/x-www-form-urlencoded gesetzt werden und die Abfragezeichenfolge muss an die send()-Methode von XMLHttpRequest übergeben werden, an die die HTTP-Anfrage gesendet wird Server.
Erstellen Sie ein XML-Dokument. Die Verwendung von Zeichenfolgen zum Erstellen von Elementen aus ihren Attributen und Daten ist die einfachste Möglichkeit, XML-Dokumente mit JavaScript zu erstellen. Wenn Sie diese Lösung übernehmen, benötigen Sie eine Hilfsmethode, um die Zeichen &, <, >, " und zu maskieren:
function escapeXML(content) {
if (Inhalt == undefiniert)
zurückkehren "";
if (!content.length || !content.charAt)
content = new String(content);
var result = "";
var length = content.length;
for (var i = 0; i < length; i++) {
var ch = content.charAt(i);
Schalter (ch) {
Fall &:
Ergebnis += "&";
brechen;
Fall < :
Ergebnis += "< ";
brechen;
Fall >:
Ergebnis += ">";
brechen;
Fall ":
Ergebnis += """;
brechen;
Fall \:
result += "'";
brechen;
Standard:
Ergebnis += ch;
}
}
Ergebnis zurückgeben;
}
Um die Aufgabe zu vereinfachen, sind einige zusätzliche Hilfsmethoden erforderlich, wie zum Beispiel:
function attribute(name, value) {
return " " + name + "="" + escapeXML(value) + """;
}
Das folgende Beispiel erstellt ein XML-Dokument aus einem Array von Objekten mit drei Eigenschaften: Symbol, Aktien und bezahlter Preis:
function buildPortfolioDoc(stocks) {
var xml = "<portfolio>";
for (var i = 0; i < stocks.length; i++) {
var stock = stocks[i];
xml += "< stock ";
xml += attribute("symbol", stock.symbol);
xml += attribute("shares", stock.shares);
xml += attribute("paidPrice", stock.paidPrice);
xml += "";
}
xml += "< /portfolio>";
XML zurückgeben;
}
Wenn Sie lieber mit DOM arbeiten, können Sie die API Ihres Webbrowsers verwenden, um XML zu analysieren und den DOM-Baum zu serialisieren. Mit IE können Sie ein leeres Dokument mit einem neuen ActiveXObject („Microsoft.XMLDOM“) erstellen. Das XML kann dann mithilfe der Methoden „loadXML()“ bzw. „load()“ aus einem String oder einer URL analysiert werden. Im Fall von IE verfügt jeder Knoten über ein Attribut namens xml, mit dem Sie eine XML-Darstellung des Knotens und aller seiner untergeordneten Knoten erhalten können. Daher können Sie eine XML-Zeichenfolge analysieren, den DOM-Baum ändern und das DOM dann wieder in XML serialisieren.
In den Browsern Firefox und Netscape können Sie mit document.implementation.createDocument(...) ein leeres Dokument erstellen. DOM-Knoten können dann mit createElement(), createTextNode(), createCDATASection() usw. erstellt werden. Der Mozilla-Browser bietet außerdem zwei APIs namens DOMParser und XMLSerializer. Die DOMParser-API enthält die Methoden parseFromStream() und parseFromString(). Die XMLSerializer-Klasse verfügt über entsprechende Methoden zum Serialisieren des DOM-Baums: serializeToStream() und serializeToString().
Die folgende Funktion analysiert einen XML-String und gibt ein DOM-Dokument zurück:
function parse(xml) {
var dom;
versuchen{
dom = new ActiveXObject("Microsoft.XMLDOM");
dom.async = false;
dom.loadXML(xml);
} Catch (Fehler) {
versuchen{
var parser = new DOMParser();
dom = parser.parseFromString(xml, "text/xml");
Parser löschen;
} Catch (error2) {
if(debug)
Alert("XML-Parsing wird nicht unterstützt.");
}
}
return dom;
}
Die zweite Funktion serialisiert einen DOM-Knoten und alle seine untergeordneten Knoten und gibt das XML als Zeichenfolge zurück:
function serialize(dom) {
var xml = dom.xml;
if (xml == undefiniert) {
versuchen{
var serializer = new XMLSerializer();
xml = serializer.serializeToString(dom);
Serialisierer löschen;
} Catch (Fehler) {
if(debug)
alarm("DOM-Serialisierung wird nicht unterstützt.");
}
}
XML zurückgeben;
}
Sie können XMLHttpRequest auch als Parser oder Serialisierer verwenden. Nachdem eine Antwort auf eine Ajax-Anfrage vom Server empfangen wurde, wird die Antwort automatisch analysiert. Auf die Textversion und den DOM-Baum kann über die Eigenschaften „responseText“ und „responseXML“ von XMLHttpRequest zugegriffen werden. Darüber hinaus wird der DOM-Baum automatisch serialisiert, wenn er an die send()-Methode übergeben wird.
Senden Sie eine Anfrage. In einem früheren Artikel habe ich die XMLHttpRequest-API und eine Hilfsfunktion, sendHttpRequest(), vorgestellt, die Sie in der Datei ajaxUtil.js im zum Download bereitgestellten Beispiel finden. Diese Funktion benötigt vier Parameter (HTTP-Methode, URL, ein Parameterarray und einen Rückruf), erstellt ein XMLHttpRequest-Objekt, legt seine Eigenschaften fest und ruft die send()-Methode auf. Wenn ein Callback-Parameter bereitgestellt wird, wird die Anfrage asynchron gesendet und die Callback-Funktion aufgerufen, nachdem die Antwort empfangen wurde. Andernfalls wird die Anfrage synchron gesendet und Sie können die Antwort verarbeiten, sobald sendHttpRequest() zurückkommt.
Wie Sie sehen, müssen Sie bei der Verwendung von XMLHttpRequest einige wichtige Entscheidungen treffen
. Welche HTTP-Methode soll verwendet werden (GET oder POST)?
Das zum Kodieren von Anforderungsparametern verwendete Format (XML- und URL-Kodierung wurden weiter oben in diesem Artikel besprochen)
Ob der Aufruf synchron (Warten auf eine Antwort) oder asynchron (mit einem Rückruf) erfolgen soll. Das Format der Antwort, z. B. XML, XHTML, HTML oder JavaScript Object Notation (JSON) (wird später in diesem Artikel besprochen). Nehmen wir an, Sie möchten einige Aktienkursinformationen aus einem Daten-Feed abrufen und die Informationen regelmäßig aktualisieren, ohne dass der Benutzer eingreifen muss. In diesem Fall sollte die HTTP-Anfrage asynchron gesendet werden, damit die Benutzeroberfläche beim Abrufen der Informationen nicht blockiert wird. Der Anforderungsparameter ist ein Array von Symbolen, die in der URL codiert werden können. Da der Server möglicherweise überlastet ist, möchten Sie bei häufigen Anfragen keine XML-Dokumente senden. Da Sie nur am aktuellen Aktienkurs interessiert sind, sollten alle ausstehenden vorherigen Anfragen beendet werden:
var ctrlURL = "ajaxCtrl.jsp";
var feedRequest = null;
function sendInfoRequest(symbols, callback) {
if(feedRequest)
abortRequest(feedRequest);
var params = new Array();
for (var i = 0; i < symbole.länge; i++)
params[i] = {
Name: „Symbol“,
Wert:Symbole[i]
};
feedRequest = sendHttpRequest(
„GET“, ctrlURL, params, callback);
}
Bevor die abort()-Methode des Anforderungsobjekts aufgerufen wird, setzt die Funktion abortRequest() (in der Datei ajaxUtil.js) die Eigenschaft onreadystatechange auf einen Rückruf, der nichts bewirkt. Darüber hinaus ist es wichtig, das Anforderungsobjekt zu löschen, um Speicherlecks zu vermeiden:
function abortRequest(request) {
Funktion doNothing() {
}
request.onreadystatechange = doNothing;
request.abort();
FeedRequest löschen;
}
Betrachten wir einen anderen Fall: Bei der Übertragung der gesamten Benutzerdaten, die in der Datenbank gespeichert werden sollen, sollte die Anfrage synchron gesendet werden, da Sie wahrscheinlich nicht möchten, dass der Benutzer sie ändert, während diese Daten gespeichert werden. In diesem Fall wird das XML-Format bevorzugt, da es oft einfacher ist, das Objektmodell im Dokument zu kodieren, als viele String-Parameter zu verwenden. Darüber hinaus sind Anfragen zum Speichern von Daten selten und der Server bewältigt die Last problemlos. Ein XML-Dokument kann als Parameter codiert werden, sodass Sie in einer JSP-Seite mithilfe der EL-Syntax (${param.xml}) darauf zugreifen können. Hier ist die Funktion, die in einem XML-Dokument codierte Modelldaten sendet:
function sendSaveRequest(xml) {
var params = [ { name:"xml", value:xml } ];
var saveRequest = sendHttpRequest("POST", ctrlURL, params);
if(saveRequest)
saveRequest löschen;
}
Wenn Sie das Objektmodell wiederherstellen müssen, können Sie auch synchron eine Anfrage senden, um Daten vom Server abzurufen. In diesem Fall sollte der Server eine JSON-Antwort zurückgeben, damit Sie sie mithilfe von eval(loadRequest.responseText) problemlos in einen JavaScript-Objektbaum konvertieren können:
function sendLoadRequest() {
var model = null;
var loadRequest = sendHttpRequest("GET", ctrlURL);
if (loadRequest) {
model = eval(loadRequest.responseText);
LoadRequest löschen;
}
Rückgabemodell;
}
In den folgenden beiden Abschnitten werden Vorgänge beschrieben, die normalerweise an XML-Dokumenten auf dem Server ausgeführt werden, und wie auf Ajax-Anfragen reagiert wird.
Anfragen auf der Serverseite verarbeiten
Der Servlet/JSP-Container analysiert jede HTTP-Anfrage und erstellt eine ServletRequest-Instanz, die es Ihnen ermöglicht, die Anfrageparameter über getParameter() / getParameterValues() oder den Anfragetext über getInputStream() abzurufen. In JSP-Seiten können diese Parameter auch mithilfe der EL-Syntax (${param...} und ${paramValues...}) abgerufen werden. Beachten Sie, dass die Übergabe von getParameter nur möglich ist, wenn der Ajax-Client eine Hilfsfunktion wie buildQueryString() verwendet, um die Daten im Format application/x-www-form-urlencoded (beschrieben im vorherigen Abschnitt dieses Artikels) oder $ zu kodieren {param...}, um Anforderungsparameter abzurufen. Wenn Sie auf der Clientseite ein XML-Dokument oder einen DOM-Baum an die send()-Methode von XMLHttpRequest übergeben, müssen Sie auf der Serverseite die getInputStream()-Methode von ServletRequest verwenden.
Datenvalidierung. Eine typische Webanwendung führt viele Datenvalidierungsvorgänge durch. Die meisten möglichen Fehler sind relativ einfach, wie z. B. fehlende Anforderungsparameter, falsche Zahlenformate usw. Diese Fehler werden normalerweise dadurch verursacht, dass der Benutzer vergisst, einen Wert für ein Formularelement einzugeben, oder einen ungültigen Wert angibt. Web-Frameworks wie JSF und Oracle ADF Faces sind sehr gut im Umgang mit diesen Benutzerfehlern. In Ajax-Anwendungen können diese Fehler mithilfe von JavaScript auf der Clientseite abgefangen und behandelt werden. Sie können beispielsweise isNaN(new Number(value)) verwenden, um zu überprüfen, ob ein numerischer Wert ungültig ist.
Aus Sicherheits- und Zuverlässigkeitsgründen sollten Daten auf der Serverseite erneut validiert werden und XML-Anfragen sollten nicht als gut formatiert angesehen werden. XML-Schemas sind ein nützliches Werkzeug zur Validierung komplexer Anfragen auf der Serverseite. Der Beispielcode-Download enthält eine Klasse namens XMLUtil, die Methoden zum Laden und Verwenden von Schemadokumenten bereitstellt. Der folgende Codeausschnitt zeigt, wie SchemaFactory initialisiert wird:
import javax.xml.*;
import javax.xml.validation.*;
...
protected static SchemaFactory schemaFactory;
statisch {
schemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setErrorHandler(newErrorHandler());
}
Die Methode newErrorHandler() gibt einen SAX-Fehlerhandler zurück:
import org.xml.sax.*;
...
öffentlicher statischer ErrorHandler newErrorHandler() {
return new ErrorHandler() {
Warnung vor öffentlicher Leere (SAXParseException e)
wirft SAXException {
Logger.global.warning(e.getMessage());
}
public void error(SAXParseException e)
wirft SAXException {
wirf e;
}
public void fatalError(SAXParseException e)
wirft SAXException {
wirf e;
}
};
}
Sie können getResourceAsStream() verwenden, um eine XSD-Datei in einem Verzeichnis oder einer JAR zu finden und zu laden, die im CLASSPATH angegeben ist:
public static InputStream getResourceAsStream(String name)
wirft IOException {
InputStream in = XMLUtil.class.getResourceAsStream(name);
if (in == null)
throw new FileNotFoundException(name);
zurückkehren;
}
Verwenden Sie dann die SchemaFactory-Instanz, um das Schema-Objekt über die Methode newSchema() abzurufen:
import javax.xml.validation.*;
...
öffentliches statisches Schema newSchema(String name)
wirft IOException, SAXException {
Schemaschema;
InputStream in = getResourceAsStream(name);
versuchen{
schema = schemaFactory.newSchema(new StreamSource(in));
}Endlich{
in.close();
}
Rückgabeschema;
}
Sie können ein Oracle XMLSchema-Objekt auch mit folgendem Befehl erstellen:
import oracle.xml.parser.schema.XMLSchema;
import oracle.xml.parser.schema.XSDBuilder;
...
öffentliches statisches XMLSchema newOracleSchema(String name)
wirft IOException, SAXException {
XMLSchema-Schema;
InputStream in = getResourceAsStream(name);
versuchen{
XSDBuilder builder = new XSDBuilder();
schema = builder.build(new InputSource(in));
} Catch (Ausnahme e){
throw new SAXException(e);
}Endlich{
in.close();
}
Rückgabeschema;
}
Als Nächstes müssen Sie eine DocumentBuilderFactory erstellen. Wenn im CLASSPATH eine JAXP 1.1-Implementierung gefunden wird, kann die von JAXP 1.2 definierte setSchema()-Methode eine UnsupportedOperationException auslösen. In diesem Fall muss die JAXP 1.1-Implementierung durch die JAXP 1.2-Implementierung von Java SE 5.0 ersetzt werden. In diesem Fall können Sie weiterhin ein Schemaobjekt mit newOracleSchema() erstellen und es über die Methode setAttribute() festlegen:
import javax.xml.parsers.*;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
...
öffentliche statische DocumentBuilderFactory newParserFactory(
String schemaName) löst IOException, SAXException {
DocumentBuilderFactory parserFactory
= DocumentBuilderFactory.newInstance();
versuchen{
parserFactory.setSchema(newSchema(schemaName));
} Catch (UnsupportedOperationException e) {
if (parserFactory-Instanz von JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT,
newOracleSchema(schemaName));
}
}
return parserFactory;
}
Erstellen Sie dann ein DocumentBuilder-Objekt und verwenden Sie es, um das XML-Dokument zu validieren und zu analysieren:
import javax.xml.parsers.*;
...
öffentlicher statischer DocumentBuilder newParser(
DocumentBuilderFactory (parserFactory)
wirft ParserConfigurationException {
DocumentBuilder parser = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
Rückgabeparser;
};
Angenommen, Sie möchten ein XML-Dokument anhand des Beispielschemas „portfolio.xsd“ validieren:
< xsd:schema xmlns:xsd=" http://www.w3.org/2001/XMLSchema ">
< xsd:element name="portfolio" Typ ="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>
Die parsePortfolioDoc()-Methode der DataModel-Klasse verwendet XMLUtil, um die XML-Parameter zu validieren und zu analysieren und ein DOM-Dokument zurückzugeben:
private static final String SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
private static DocumentBuilderFactory parserFactory;
privates statisches Dokument parsePortfolioDoc(String xml)
wirft IOException, SAXException,
ParserConfigurationException {
synchronisiert (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);
}
Da Sie nun über einen DOM-Baum verfügen, müssen Sie die Daten abrufen, die zum Bilden der DOM-Knoten erforderlich sind.
Extrahieren Sie die erforderlichen Informationen. Sie können die DOM-API oder eine Abfragesprache (z. B. XQuery oder XPath) verwenden, um den DOM-Baum zu durchsuchen. Java stellt eine Standard-API für XPath bereit, die später verwendet wird. Die XMLUtil-Klasse erstellt eine XPathFactory mit einer newXPath()-Methode:
import javax.xml.xpath.*;
...
protected static XPathFactory xpathFactory;
statisch {
xpathFactory = XPathFactory.newInstance();
}
öffentlicher statischer XPath newXPath() {
return xpathFactory.newXPath();
}
Die folgenden Methoden werten einen XPath-Ausdruck in einem bestimmten Kontext aus und geben den resultierenden Wert zurück:
import javax.xml.xpath.*;
import org.w3c.dom.*;
...
öffentlicher statischer String evalToString(String expression,
Objektkontext) löst XPathExpressionException {
return (String) newXPath().evaluate(expression, context,
XPathConstants.STRING);
}
public static boolean evalToBoolean(String expression,
Objektkontext) löst XPathExpressionException {
return ((Boolean) newXPath().evaluate(expression, context,
XPathConstants.BOOLEAN)).booleanValue();
}
public static double evalToNumber(String expression,
Objektkontext) löst XPathExpressionException {
return ((Double) newXPath().evaluate(expression, context,
XPathConstants.NUMBER)).doubleValue();
}
öffentlicher statischer Knoten evalToNode(String expression,
Objektkontext) löst XPathExpressionException {
return (Node) newXPath().evaluate(expression, context,
XPathConstants.NODE);
}
öffentliche statische NodeList evalToNodeList(String expression,
Objektkontext) löst XPathExpressionException {
return (NodeList) newXPath().evaluate(expression, context,
XPathConstants.NODESET);
}
Die setData()-Methode des DataModel verwendet die XPath-Solver-Methode, um Informationen aus dem kombinierten XML-Dokument zu extrahieren:
public synchronisiert void setData(String xml)
wirft IOException, SAXException,
ParserConfigurationException,
XPathExpressionException {
versuchen{
ArrayList stockList
= neue ArrayList();
Document doc = parsePortfolioDoc(xml);
NodeList nodeList = XMLUtil.evalToNodeList(
„/portfolio/stock“, doc);
for (int i = 0; i < nodeList.getLength(); i++) {
Knoten 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 (Ausnahme e){
Logger.global.logp(Level.SEVERE, "DataModel", "setData",
e.getMessage(), e);
}
}
Sobald die Daten im serverseitigen Datenmodell verfügbar sind, können sie entsprechend den Anforderungen der Anwendung verarbeitet werden. Anschließend müssen Sie auf die Ajax-Anfrage antworten.
Die Antwort auf der Serverseite generieren
Die Rückgabe von HTML als Antwort auf eine Ajax-Anfrage ist die einfachste Lösung, da Sie das Markup mithilfe der JSP-Syntax erstellen können und der Ajax-Client einfach das Element <div> oder <span> verwendet. Die innerHTML-Eigenschaft fügt HTML ein irgendwo auf der Seite. Es ist jedoch effizienter, Daten ohne Präsentations-Markup an den Ajax-Client zurückzugeben. Sie können das XML-Format oder JSON verwenden.
XML-Antwort generieren. Java EE bietet viele Optionen zum Erstellen von XML-Dokumenten: generiert über JSP, erstellt aus einem Objektbaum über JAXB oder generiert mit javax.xml.transform. Der Transformer im folgenden Beispiel serialisiert einen DOM-Baum:
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
...
öffentliche statische TransformerFactory serializerFctory;
statisch {
serializerFctory = TransformerFactory.newInstance();
}
public static void serialize(Knotenknoten, OutputStream out)
wirft TransformerException {
Transformer serializer = serializerFctory.newTransformer();
Eigenschaften serializerProps = new Properties();
serializerProps.put(OutputKeys.METHOD, "xml");
serializer.setOutputProperties(serializerProps);
Quellquelle = new DOMSource(node);
Ergebnis result = new StreamResult(out);
serializer.transform(Quelle, Ergebnis);
}
Es gibt so viele Standardoptionen und Entwicklungsquell-Frameworks zum Generieren von XML auf der Serverseite, dass Sie nur noch diejenige auswählen müssen, die für Sie am besten geeignet ist. Auf dem Client sieht die Situation jedoch ganz anders aus, da XML nur über das DOM geparst werden kann. Einige Browser unterstützen auch XPath und XSLT.
In früheren Ajax-Artikeln haben Sie gelernt, wie Sie XML über JSP generieren und es dann auf dem Client mithilfe von JavaScript und dem DOM analysieren. Eine andere Lösung besteht darin, JSON anstelle von XML als Datenformat für die Beantwortung von Ajax-Anfragen zu verwenden. Wie bereits erwähnt, kann ein JSON-String mithilfe der Funktion eval() in einen JavaScript-Objektbaum umgewandelt werden. Dies ist einfacher als die Verwendung von JavaScript zum Extrahieren von Informationen aus dem DOM-Baum. Alles, was Sie brauchen, ist eine gute Utility-Klasse, die JSON auf der Serverseite generiert.
JSON-Kodierung. Die JSONEncoder-Klasse stellt Methoden zum Codieren von Literalen, Objekten und Arrays bereit. Die Ergebnisse werden in java.lang.StringBuilder gespeichert:
package ajaxapp.util
public class JSONEncoder {;
privater StringBuilder buf;
public JSONEncoder() {
buf = new StringBuilder();
}
...
}
Die Methode „character()“ kodiert ein einzelnes Zeichen:
public void Character(char ch) {
Schalter (ch) {
Fall \:
Fall \":
Fall \ :
buf.append( \ );
buf.append(ch);
brechen;
Fall :
buf.append( \ );
buf.append( );
brechen;
Fall
:
buf.append( \ );
buf.append(
);
brechen;
Fall
:
buf.append( \ );
buf.append(
);
brechen;
Standard:
if (ch >= 32 && ch < 128)
buf.append(ch);
anders{
buf.append( \ );
buf.append(u);
for (int j = 12; j >= 0; j-=4) {
int k = (((int) ch) >> j) & 0x0f;
int c = k < 10 ? + k :a + k - 10;
buf.append((char) c);
}
}
}
}
Die Methode string() kodiert den gesamten String:
public void string(String str) {
int length = str.length();
for (int i = 0; i < length; i++)
Charakter(str.charAt(i));
}
literal()-Methode kodiert JavaScript-Literal:
public void literal(Object value) {
if (Wertinstanz von String) {
buf.append(");
string((String) value);
buf.append(");
} else if (Wertinstanz des Zeichens) {
buf.append(\);
zeichen(((Zeichen) wert).charValue());
buf.append(\);
} anders
buf.append(value.toString());
}
Die Methode „comma()“ fügt ein Kommazeichen hinzu:
private void comma() {
buf.append(,);
}
Die Methode deleteLastComma() entfernt das letzte Kommazeichen am Ende des Puffers (falls vorhanden):
private void deleteLastComma() {
if (buf. length() > 0)
if (buf.charAt(buf.length()-1) == ,)
buf.deleteCharAt(buf.length()-1);
}
Die startObject()-Methode hängt ein {-Zeichen an, um den Anfang eines JavaScript-Objekts anzuzeigen:
public void startObject() {
buf.append({);
}
Die Methode property() kodiert JavaScript-Eigenschaften:
public void property(String name, Object value) {
buf.append(name);
buf.append(:);
Literal(Wert);
Komma();
}
Die endObject()-Methode hängt ein }-Zeichen an, um das Ende eines JavaScript-Objekts anzuzeigen:
public void endObject() {
deleteLastComma();
buf.append(});
Komma();
}
Die startArray()-Methode hängt ein [-Zeichen an, um den Anfang eines JavaScript-Arrays anzuzeigen:
public void startArray() {
buf.append([);
}
Die Methode element() codiert die Elemente eines JavaScript-Arrays:
public void element(Object value) {
Literal(Wert);
Komma();
}
Die endArray()-Methode hängt ein ]-Zeichen an, um das Ende eines JavaScript-Arrays anzuzeigen:
public void endArray() {
deleteLastComma();
buf.append(]);
Komma();
}
Die Methode toString() gibt einen JSON-String zurück:
public String toString() {
deleteLastComma();
return buf.toString();
}
Die Methode „clear()“ löscht den Puffer:
public void clear() {
buf.setLength(0);
}
DataModel verwendet die JSONEncoder-Klasse, um die von ihm verwalteten Daten zu kodieren:
public synchronisiert String getData() {
JSONEncoder json = new JSONEncoder();
json.startArray();
for (int i = 0; i < stockList.size(); i++) {
StockBean stock = stockList.get(i);
json.startObject();
json.property("symbol", stock.getSymbol());
json.property("shares", stock.getShares());
json.property("paidPrice", stock.getPaidPrice());
json.endObject();
}
json.endArray();
return json.toString();
}
Wenn XML-Anforderungsparameter bereitgestellt werden, legt die Seite ajaxCtrl.jsp die Daten des Modells fest. Andernfalls verwendet die Seite den EL-Ausdruck ${dataModel.data}, um die von getData() zurückgegebene JSON-Zeichenfolge auszugeben:
< %@ taglib prefix="c" uri=" http://java.sun.com/jsp/ jstl /core " %>
...
< jsp:useBean id="dataModel" Scope="session"
class="ajaxapp.model.DataModel" />
< c:choose>
...
< c:when test="${!empty param.xml}">
< c:set target="${dataModel}"
property="data"
value="${param.xml}" />
< /c:when>
<c:sonst>
${dataModel.data}
< /c:sonst>
< /c:choose>
Dieser Job ist nicht abgeschlossen, da der Ajax-Client die JSON-Daten verarbeiten muss.
Antworten auf der Clientseite verarbeiten
In einer typischen Webanwendung generieren Sie Inhalte auf der Serverseite mithilfe von JSPs, Web-Frameworks und Tag-Bibliotheken. Ajax-Anwendungen sind für diese Situation ideal, da Web-Frameworks wie JavaServer Faces und Oracle ADF Faces beim Erstellen von Ajax-Anwendungen sehr nützlich sind. Allerdings gibt es immer noch erhebliche Unterschiede zwischen Ajax- und Nicht-Ajax-Anwendungen. Wenn Sie Ajax verwenden, müssen Sie die Daten auf der Clientseite verarbeiten und JavaScript verwenden, um dynamisch Inhalte zu generieren, um die Daten dem Benutzer bereitzustellen.
Wenn Sie das JSON-Format für die Datenkonvertierung verwenden, ist es sehr einfach, Text mithilfe der von JavaScript bereitgestellten Funktion eval() in einen Objektbaum zu konvertieren. Wenn Sie lieber XML verwenden möchten, müssen Sie noch viele andere Dinge tun, aber dieses Format hat auch seine eigenen Vorteile. XML kann beispielsweise von vielen Arten von Clients verwendet werden, während JSON nur in einer JavaScript-Umgebung einfach zu analysieren ist. Darüber hinaus können bei der Verwendung von XML Fehler schneller gefunden und behoben werden, wodurch die Debugging-Zeit verkürzt wird.
Verwenden Sie JavaScript, um auf den DOM-Baum zuzugreifen. Die DOM-API von JavaScript ist dem Java-Paket org.w3c.dom sehr ähnlich. Der Hauptunterschied besteht im Zugang zu Immobilien. In JavaScript können Sie direkt auf Eigenschaften zugreifen, während Java Eigenschaften als privat behandelt und Sie über Get- und Set-Methoden auf sie zugreifen müssen. Beispielsweise können Sie das Stammelement eines Dokuments über dom.documentElement abrufen.
Das DOM ist eine Low-Level-API, die Zugriff auf die Struktur des analysierten Dokuments bietet. Beispielsweise möchten Sie Kommentare in den meisten Fällen ignorieren und möglicherweise keine angrenzenden Textknoten haben. Betrachten Sie das folgende einfache Beispiel:
var xml = "< element>da< !--comment-->ta&"
+ "< ![CDATA[cdata< /element>";
Sie können die obige XML-Zeichenfolge mit der zuvor eingeführten Dienstprogrammfunktion analysieren:
var dom = parse(xml);
Den Code für die Funktion parse() finden Sie in ajaxUtil.js. In diesem Fall gibt die Funktion einen DOM-Baum zurück, dessen Wurzelelement einen Textknoten enthält, gefolgt von einem Kommentar, einem weiteren Textknoten und einem Zeichendatenknoten. Wenn Sie Text ohne Kommentare einschließen möchten, müssen Sie über die untergeordneten Elemente des Elements iterieren und dabei die Werte der Text- und Zeichendatenknoten (die jeweils die Typen 3 und 4 haben) verketten:
var element = dom.documentElement;
var childNodes = element.childNodes;
var text = "";
for (var i = 0; i < childNodes.length; i++)
if (childNodes[i].nodeValue) {
var type = childNodes[i].nodeType;
if (Typ == 3 || Typ == 4)
text += childNodes[i].nodeValue;
}
Wenn Sie mit dem DOM arbeiten, sollten Sie einen kleinen Satz von Hilfsfunktionen erstellen, um die Auseinandersetzung mit diesen Low-Level-Details zu vermeiden.
Verwenden Sie JavaScript, um dynamische Inhalte zu generieren. Mit Webbrowsern können Sie über Dokumentobjekte auf die DOM-Struktur einer Webseite zugreifen. Sie können beispielsweise document.getElementById(...) verwenden, um ein Element ganz einfach zu finden. Sie können auch neue Elemente und Textknoten erstellen, die in bestehende Dokumente eingefügt werden können. Es ist jedoch einfacher, HTML durch die Verkettung von Zeichenfolgen zu erstellen, wie unten gezeigt:
function updateInfo(request) {
var share = eval(request.responseText);
var table = "< table border=1 cellpadding=5>";
Tabelle += "<tr>";
table += "< th>Symbol< /th>";
table += "< th>Trend< /th>";
table += "< th>Letzter Preis< /th>";
Tabelle += "< /tr>";
for (var i = 0; i < share.length; i++) {
var share = share[i];
var symbol = escapeXML(share.symbol)
var trend = share.trend > 0 ? "+" : "-";
var lastPrice = new Number(share.lastPrice).toFixed(2);
Tabelle += "<tr>";
Tabelle += "< td>" + Symbol + "< /td>";
table += "< td>" + trend + "< /td>";
table += "< td>" + lastPrice + "< /td>";
Tabelle += "< /tr>";
}
table += "< /table>";
document.getElementById("table").innerHTML = table;
}
Das generierte HTML kann in ein leeres Element eingefügt werden
, indem die von GetElementById () zurückgegebene Innerhtml -Eigenschaft des Objekts festgelegt wird:
<div id = "table">
< /div>
Das Beispiel in diesem Artikel verwendet die Funktion updateInfo () als Rückruf, um Antworten auf AJAX -Anforderungen zu verarbeiten, die über SendeInforequest in der Datei ajaxlogic.js an den Server gesendet werden. Wenn Sie alle 5 Sekunden Informationen aktualisieren möchten, können Sie JavaScripts setInterval () -Funktion:
var symbole = [...] verwenden;
setInterval ("sendInforequest (Symbole, UpdateInfo)", 5000);
Eine Klasse namens DataFeed simuliert einen serverseitigen Feed. Die Ajaxctrl.jsp -Seite ruft die methode des Feeds auf und gibt die Antwort als JSON -Zeichenfolge zurück. Auf der Clientseite analysiert die Funktion updateInfo () die JSON -Zeichenfolge mithilfe von Eval (Request.ResponSeText), wie im Vorbild des vorhergehenden Codes gezeigt.