El único propósito de la API central de Ajax (llamada XMLHttpRequest) es enviar solicitudes HTTP para intercambiar datos entre el navegador web y el servidor. El código JavaScript que se ejecuta en una página web puede utilizar XMLHttpRequest para enviar los parámetros de solicitud a un script del lado del servidor, como un servlet o una página JSP. El Servlet/JSP que llama devolverá una respuesta que contiene datos que normalmente se utilizan para actualizar la vista del usuario sin actualizar toda la página. Este enfoque ofrece ventajas únicas en términos de rendimiento y usabilidad porque se reduce el tráfico de red y la interfaz de usuario web es casi tan utilizable como la GUI de escritorio.
Sin embargo, desarrollar una interfaz de usuario de este tipo no es sencillo, ya que debe implementar el intercambio, la validación y el procesamiento de datos utilizando JavaScript en el lado del cliente y Java (o lenguaje equivalente) en el lado del servidor. Sin embargo, en muchos casos, el esfuerzo adicional para construir una interfaz basada en Ajax vale la pena, considerando los beneficios que se obtendrán.
En este artículo, presentaré uno de los métodos principales para transferir datos entre clientes y servidores Ajax y compararé las diferencias entre el modelo de aplicación web tradicional y el modelo Ajax. Además, el artículo también explorará técnicas para procesar datos en el lado del servidor y del lado del cliente.
Primero, aprenderá a utilizar JavaScript para codificar los parámetros de un objeto de solicitud en el lado del cliente. Puede utilizar la denominada codificación URL (la codificación predeterminada utilizada por los navegadores web) o puede incluir los parámetros de solicitud en el documento XML. El servidor procesará la solicitud y devolverá una respuesta cuyos datos también deberán estar codificados. Este artículo explora la notación de objetos JavaScript (JSON) y XML, que son las principales opciones de formato de datos de respuesta.
La mayor parte de este artículo se centrará en las API relacionadas con XML que se utilizan habitualmente en aplicaciones Ajax. Del lado del cliente, la API XML es muy limitada pero suficiente. En la mayoría de los casos, todas las operaciones necesarias se pueden realizar mediante XMLHttpRequest. Además, JavaScript se puede utilizar para analizar documentos XML y serializar árboles DOM en un navegador web. Del lado del servidor, hay muchas API y marcos disponibles para procesar documentos XML. Este artículo describe cómo realizar tareas básicas utilizando la API Java estándar para XML, que admite esquemas XML, XPath, DOM y muchos otros estándares.
A través de este artículo, podrá conocer las mejores técnicas y las últimas API para el intercambio de datos en aplicaciones Ajax. El código de muestra involucrado se encuentra en tres paquetes: util, model y feed. Las clases del paquete util proporcionan métodos para análisis XML, validación basada en esquemas, consultas basadas en XPath, serialización DOM y codificación JSON. El paquete de modelos contiene modelos de datos de muestra que pueden inicializarse a partir de documentos XML y luego convertirse al formato JSON. También hay un ejemplo de esquema en el directorio del modelo que se puede utilizar para la validación XML. Las clases del paquete de fuente se pueden utilizar para simular una fuente de datos, que recupera información a través de Ajax cada 5 segundos para actualizar la página web. Este artículo explica cómo evitar pérdidas de memoria en su navegador web finalizando las solicitudes Ajax pendientes y eliminando el objeto XMLHttpRequest cuando haya terminado de usarlo.
Se incluyen ejemplos de JSP y JavaScript en el directorio web. ajaxUtil.js contiene funciones de utilidad para enviar solicitudes Ajax, finalizar solicitudes y manejar errores HTTP. Este archivo también proporciona utilidades de JavaScript para codificación XML y URL, análisis XML y serialización DOM. El archivo ajaxCtrl.jsp actúa como un controlador Ajax, recibe cada solicitud de Ajax, reenvía parámetros al modelo de datos o proporciona procesamiento y luego devuelve una respuesta de Ajax. El resto de archivos web son ejemplos que demuestran cómo utilizar este método práctico.
La forma más sencillade construir una solicitud en el lado del cliente
para enviar datos a un servidor web es codificar la solicitud como una cadena de consulta, que puede agregarse a la URL o incluirse en el cuerpo de la solicitud, según el método HTTP utilizado. Si necesita enviar estructuras de datos complejas, una mejor solución es codificar la información en un documento XML. Cubriré ambos métodos en esta sección.
Codificar parámetros de solicitud. Al desarrollar aplicaciones web tradicionales, no necesita preocuparse por codificar los datos del formulario porque el navegador web lo hace automáticamente cuando el usuario envía los datos. Sin embargo, en una aplicación Ajax, usted mismo debe codificar los parámetros de solicitud. JavaScript proporciona una función muy útil escape(), que reemplaza cualquier carácter que no pueda ser parte de la URL con %HH (donde HH es el código hexadecimal). Por ejemplo, cualquier carácter de espacio en blanco se reemplaza por %20.
La descarga del código de muestra proporciona una función de utilidad, buildQueryString(), que concatena los parámetros recuperados de una matriz, separando el nombre y el valor de cada parámetro por = y colocando el carácter & entre cada par nombre-valor:
function buildQueryString(params) {
consulta var = "";
for (var i = 0; i < params.length; i++) {
consulta += (i > 0 ? "&" : "")
+ escape(params[i].nombre) + "="
+ escape(params[i].valor);
}
consulta de devolución;
}
Supongamos que desea codificar los siguientes parámetros:
var someParams = [
{ nombre: "nombre", valor: "John Smith" },
{ nombre: "correo electrónico", valor: " [email protected] " },
{ nombre: "teléfono", valor: "(123) 456 7890" }
];
Una llamada a buildQueryString(someParams) producirá resultados que contienen:
name=John%20Smith&[email protected]&phone=%28123%29%20456%207890
.
Si desea utilizar el método GET, debe agregar la consulta a la URL después del carácter? Cuando se utiliza POST, el encabezado Content-Type debe establecerse en application/x-www-form-urlencoded a través de setRequestHeader(), y la cadena de consulta debe pasarse al método send() de XMLHttpRequest, que enviará la solicitud HTTP a servidor.
Crea un documento XML. Usar cadenas para crear elementos a partir de sus atributos y datos es la forma más sencilla de crear documentos XML con JavaScript. Si adopta esta solución, necesitará un método de utilidad para escapar de los caracteres &, <, >, " y:
function escapeXML(content) {
si (contenido == indefinido)
devolver "";
si (!content.length || !content.charAt)
contenido = nueva cadena (contenido);
resultado var = "";
var longitud = contenido.longitud;
para (var i = 0; i < longitud; i++) {
var ch = contenido.charAt(i);
cambiar (canal) {
caso &:
resultado += "&";
romper;
caso <:
resultado += "< ";
romper;
caso >:
resultado += ">";
romper;
caso ":
resultado += """;
romper;
caso \:
resultado += "'";
romper;
por defecto:
resultado += cad;
}
}
resultado de devolución;
}
Para simplificar la tarea, se requieren algunos métodos de utilidad adicionales, como:
atributo de función (nombre, valor) {
return " " + nombre + "="" + escapeXML(valor) + """;
}
El siguiente ejemplo crea un documento XML a partir de una matriz de objetos con tres propiedades: símbolo, acciones y precio pagado:
función buildPortfolioDoc(acciones) {
var xml = "<cartera>";
for (var i = 0; i < existencias.longitud; i++) {
var acciones = acciones[i];
xml += "<valor";
xml += atributo("símbolo", stock.symbol);
xml += atributo("acciones", acciones.acciones);
xml += atributo("PrecioPagado", stock.PrecioPagado);
xml += "";
}
xml += "< /cartera>";
devolver xml;
}
Si prefiere trabajar con DOM, puede utilizar la API de su navegador web para analizar XML y serializar el árbol DOM. Con IE, puede crear un documento vacío con un nuevo ActiveXObject ("Microsoft.XMLDOM"). Luego, el XML se puede analizar a partir de una cadena o URL utilizando los métodos loadXML() o load() respectivamente. En el caso de IE, cada nodo tiene un atributo llamado xml que le permite obtener una representación XML del nodo y todos sus nodos secundarios. Por lo tanto, puede analizar una cadena XML, modificar el árbol DOM y luego serializar el DOM nuevamente a XML.
Los navegadores Firefox y Netscape le permiten crear un documento vacío usando document.implementation.createDocument(...). Luego, los nodos DOM se pueden crear usando createElement(), createTextNode(), createCDATASection(), etc. El navegador Mozilla también proporciona dos API llamadas DOMParser y XMLSerializer. La API DOMParser contiene los métodos parseFromStream() y parseFromString(). La clase XMLSerializer tiene métodos correspondientes para serializar el árbol DOM: serializeToStream() y serializeToString().
La siguiente función analiza una cadena XML y devuelve un documento DOM:
function parse(xml) {
vardom;
intentar{
dom = nuevo ActiveXObject("Microsoft.XMLDOM");
dom.async = falso;
dom.loadXML(xml);
} captura (error) {
intentar{
var analizador = nuevo DOMParser();
dom = parser.parseFromString(xml, "texto/xml");
eliminar analizador;
} captura (error2) {
si (depurar)
alert("No se admite el análisis XML.");
}
}
regresar dom;
}
La segunda función serializa un nodo DOM y todos sus nodos secundarios, devolviendo el XML como una cadena:
function serialize(dom) {
var xml = dom.xml;
si (xml == indefinido) {
intentar{
var serializador = nuevo XMLSerializer();
xml = serializador.serializeToString(dom);
eliminar serializador;
} captura (error) {
si (depurar)
alert("La serialización DOM no es compatible.");
}
}
devolver xml;
}
También puede utilizar XMLHttpRequest como analizador o serializador. Después de recibir una respuesta a una solicitud Ajax del servidor, la respuesta se analiza automáticamente. Se puede acceder a la versión de texto y al árbol DOM a través de las propiedades ResponseText y ResponseXML de XMLHttpRequest respectivamente. Además, el árbol DOM se serializa automáticamente cuando se pasa al método send().
Envía una solicitud. En un artículo anterior, presenté la API XMLHttpRequest y una función de utilidad, sendHttpRequest(), que puede encontrar en el archivo ajaxUtil.js en el ejemplo proporcionado para descargar. Esta función toma cuatro parámetros (método HTTP, URL, una matriz de parámetros y una devolución de llamada), crea un objeto XMLHttpRequest, establece sus propiedades y llama al método send(). Si se proporciona un parámetro de devolución de llamada, la solicitud se envía de forma asincrónica y se llama a la función de devolución de llamada después de recibir la respuesta. De lo contrario, la solicitud se envía sincrónicamente y puede manejar la respuesta tan pronto como regrese sendHttpRequest().
Como puede ver, debe tomar algunas decisiones importantes al utilizar XMLHttpRequest
. ¿Qué método HTTP se utilizará (GET o POST)?
El formato utilizado para codificar los parámetros de solicitud (la codificación XML y URL se analizó anteriormente en este artículo)
Si se debe realizar la llamada de forma sincrónica (esperando una respuesta) o asincrónica (usando una devolución de llamada) El formato de la respuesta, como XML, XHTML, HTML o notación de objetos JavaScript (JSON) (que se analiza más adelante en este artículo). Supongamos que desea obtener información sobre el precio de las acciones de una fuente de datos y actualizar la información periódicamente sin la intervención del usuario. En este caso, la solicitud HTTP debe enviarse de forma asincrónica para que la interfaz de usuario no se bloquee mientras se recupera la información. El parámetro de solicitud es una matriz de símbolos que se pueden codificar en la URL. Debido a que el servidor puede estar sobrecargado, no desea enviar documentos XML cuando se realizan solicitudes frecuentes. Dado que sólo está interesado en el último precio de las acciones, debe cancelar cualquier solicitud anterior pendiente:
var ctrlURL = "ajaxCtrl.jsp";
var feedRequest = nulo;
función sendInfoRequest (símbolos, devolución de llamada) {
si (solicitud de alimentación)
abortarSolicitud(solicitud de alimentación);
var parámetros = nueva matriz();
para (var i = 0; i < símbolos.longitud; i++)
parámetros[i] = {
nombre: "símbolo",
valor:símbolos[i]
};
feedRequest = enviarHttpRequest(
"GET", ctrlURL, parámetros, devolución de llamada);
}
Antes de llamar al método abort() del objeto de solicitud, la función abortRequest() (que se encuentra en el archivo ajaxUtil.js) establece la propiedad onreadystatechange en una devolución de llamada que no hace nada. Además, es fundamental eliminar el objeto de solicitud para evitar pérdidas de memoria:
function abortRequest(solicitud) {
función no hacer nada() {
}
request.onreadystatechange = no hacer nada;
solicitud.abortar();
eliminar solicitud de alimentación;
}
Consideremos otro caso: al transferir todos los datos del usuario para guardarlos en la base de datos, la solicitud debe enviarse sincrónicamente porque probablemente no desee que el usuario la modifique mientras se guardan estos datos. En este caso, se prefiere el formato XML porque suele ser más sencillo codificar el modelo de objetos en el documento que utilizar muchos parámetros de cadena. Además, las solicitudes para guardar datos son poco frecuentes y el servidor maneja la carga sin ningún problema. Un documento XML se puede codificar como parámetro para poder acceder a él en una página JSP utilizando la sintaxis EL (${param.xml}). Aquí está la función que envía datos del modelo codificados en un documento XML:
function sendSaveRequest(xml) {
var params = [ { nombre:"xml", valor:xml } ];
var saveRequest = sendHttpRequest("POST", ctrlURL, params);
si(guardarSolicitud)
eliminar saveRequest;
}
Si necesita restaurar el modelo de objetos, también puede enviar una solicitud sincrónicamente para recuperar datos del servidor. En este caso, el servidor debería devolver una respuesta JSON para que puedas convertirla fácilmente en un árbol de objetos JavaScript usando eval(loadRequest.responseText):
function sendLoadRequest() {
modelo var = nulo;
var loadRequest = sendHttpRequest("GET", ctrlURL);
si (cargaSolicitud) {
modelo = eval(loadRequest.responseText);
eliminar solicitud de carga;
}
modelo de devolución;
}
Las dos secciones siguientes describen las operaciones que normalmente se realizan en documentos XML en el servidor y cómo responder a las solicitudes de Ajax.
Manejo de solicitudes en el lado del servidor
El contenedor Servlet/JSP analiza cada solicitud HTTP y crea una instancia de ServletRequest, que le permite obtener los parámetros de la solicitud a través de getParameter() / getParameterValues() o el cuerpo de la solicitud a través de getInputStream(). En las páginas JSP, estos parámetros también se pueden obtener usando la sintaxis EL (${param...} y ${paramValues...}). Tenga en cuenta que pasar getParameter solo es posible si el cliente Ajax utiliza una función de utilidad como buildQueryString() para codificar los datos en el formato application/x-www-form-urlencoded (descrito en la sección anterior de este artículo () o $). {param...} para obtener los parámetros de la solicitud. Si pasa un documento XML o un árbol DOM al método send() de XMLHttpRequest en el lado del cliente, debe usar el método getInputStream() de ServletRequest en el lado del servidor.
Validación de datos. Una aplicación web típica realiza muchas operaciones de validación de datos. La mayoría de los errores posibles son bastante simples, como parámetros de solicitud faltantes, formatos de números incorrectos, etc. Estos errores generalmente se deben a que el usuario olvida ingresar un valor para un elemento del formulario o proporciona un valor no válido. Los marcos web como JSF y Oracle ADF Faces son muy buenos para manejar estos errores de usuario. En las aplicaciones Ajax, estos errores se pueden detectar y manejar en el lado del cliente mediante JavaScript. Por ejemplo, puede utilizar isNaN(nuevo número(valor)) para verificar que un valor numérico no es válido.
Por razones de seguridad y confiabilidad, los datos se deben volver a validar en el lado del servidor y no se debe asumir que las solicitudes XML están bien formateadas. Los esquemas XML son una herramienta útil para validar solicitudes complejas en el lado del servidor. La descarga del código de muestra incluye una clase llamada XMLUtil que proporciona métodos para cargar y usar documentos de esquema. El siguiente fragmento de código muestra cómo inicializar SchemaFactory:
import javax.xml.*;
importar javax.xml.validation.*;
...
SchemaFactory estático protegido SchemaFactory;
estático {
esquemaFactory = SchemaFactory.nuevaInstancia(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
esquemaFactory.setErrorHandler(newErrorHandler());
}
El método newErrorHandler() devuelve un controlador de errores SAX:
import org.xml.sax.*;
...
manejador de errores estático público newErrorHandler() {
devolver nuevo ErrorHandler() {
advertencia de anulación pública (SAXParseException e)
lanza SAXException {
Logger.global.warning(e.getMessage());
}
error de anulación pública (SAXParseException e)
lanza SAXException {
tirar e;
}
public void fatalError (SAXParseException e)
lanza SAXException {
tirar e;
}
};
}
Puede usar getResourceAsStream() para buscar y cargar un archivo XSD en un directorio o un JAR especificado en CLASSPATH:
public static InputStream getResourceAsStream(String name)
lanza IOException {
InputStream en = XMLUtil.class.getResourceAsStream(nombre);
si (en == nulo)
lanzar una nueva FileNotFoundException (nombre);
regresar en;
}
Luego, use la instancia de SchemaFactory para obtener el objeto Schema a través del método newSchema():
import javax.xml.validation.*;
...
Esquema estático público nuevoEsquema (nombre de cadena)
lanza IOException, SAXException {
Esquema de esquema;
InputStream en = getResourceAsStream(nombre);
intentar{
esquema = esquemaFactory.newSchema(new StreamSource(en));
}finalmente{
cercar();
}
esquema de retorno;
}
También puede crear un objeto Oracle XMLSchema usando:
import oracle.xml.parser.schema.XMLSchema;
importar oracle.xml.parser.schema.XSDBuilder;
...
XMLSchema estático público nuevoOracleSchema (nombre de cadena)
lanza IOException, SAXException {
esquema XMLSchema;
InputStream en = getResourceAsStream(nombre);
intentar{
Constructor XSDBuilder = nuevo XSDBuilder();
esquema = constructor.build(nueva fuente de entrada(en));
} captura (Excepción e){
lanzar nueva SAXException(e);
}finalmente{
cercar();
}
esquema de retorno;
}
A continuación, debe crear un DocumentBuilderFactory. Si se encuentra una implementación JAXP 1.1 en CLASSPATH, el método setSchema() definido por JAXP 1.2 puede generar una excepción UnsupportedOperationException, en cuyo caso la implementación JAXP 1.1 debe reemplazarse con la implementación JAXP 1.2 de Java SE 5.0. En este caso, aún puede crear un objeto de esquema usando newOracleSchema() y configurarlo mediante el método setAttribute():
import javax.xml.parsers.*;
importar oracle.xml.jaxp.JXDocumentBuilderFactory;
...
público estático DocumentBuilderFactory newParserFactory(
String nombreEsquema) arroja IOException, SAXException {
DocumentBuilderFactory analizadorFactory
= DocumentBuilderFactory.newInstance();
intentar{
parserFactory.setSchema(newSchema(nombreEsquema));
} captura (UnsupportedOperationException e) {
if (instancia parserFactory de JXDocumentBuilderFactory) {
parserFactory.setAttribute(
JXDocumentBuilderFactory.SCHEMA_OBJECT,
newOracleSchema(nombreEsquema));
}
}
devolver parserFactory;
}
Luego, cree un objeto DocumentBuilder y utilícelo para validar y analizar el documento XML:
import javax.xml.parsers.*;
...
public static DocumentBuilder newParser(
DocumentBuilderFactory parserFactory)
lanza ParserConfigurationException {
Analizador de DocumentBuilder = parserFactory.newDocumentBuilder();
parser.setErrorHandler(newErrorHandler());
analizador de retorno;
};
Supongamos que desea validar un documento XML con el ejemplo de esquema portfolio.xsd:
< xsd:schema xmlns:xsd=" http://www.w3.org/2001/XMLSchema ">
< xsd:element name="portfolio" type ="Tipo de cartera "
< xsd:complexType nombre="Tipo de cartera">
<xsd:secuencia>
< xsd: nombre del elemento = "valores"
minOccurs="0" maxOccurs="ilimitado">
<xsd:tipocomplejo>
< xsd: nombre del atributo = "símbolo"
tipo="xsd:cadena" uso="requerido"/>
< xsd: nombre del atributo = "acciones"
tipo="xsd:positivoInteger" uso="requerido"/>
< xsd: nombre del atributo = "Precio pagado"
tipo="xsd:decimal" uso="requerido"/>
< /xsd:tipocomplejo>
< /xsd:elemento>
< /xsd:secuencia>
< /xsd:complexType>
< /xsd:schema>
El método parsePortfolioDoc() de la clase DataModel utiliza XMLUtil para validar y analizar los parámetros xml y devolver un documento DOM:
cadena final estática privada SCHEMA_NAME
= "/ajaxapp/model/portfolio.xsd";
parserFactory estático privado DocumentBuilderFactory;
Documento estático privado parsePortfolioDoc (String xml)
lanza IOException, SAXException,
ParserConfigurationException {
sincronizado (DataModel.class) {
si (parserFactory == nulo)
parserFactory = XMLUtil.newParserFactory(SCHEMA_NAME);
}
Analizador de DocumentBuilder = XMLUtil.newParser(parserFactory);
Fuente de entrada en = nueva Fuente de entrada (nueva StringReader (xml));
devolver analizador.parse(en);
}
Ahora que tiene un árbol DOM, necesita obtener los datos necesarios para formar los nodos DOM.
Extraiga la información requerida. Puede utilizar la API DOM o un lenguaje de consulta (como XQuery o XPath) para explorar el árbol DOM. Java proporciona una API estándar para XPath, que se utilizará más adelante. La clase XMLUtil crea una XPathFactory con un método newXPath():
import javax.xml.xpath.*;
...
XPathFactory estático protegido xpathFactory;
estático {
xpathFactory = XPathFactory.newInstance();
}
XPath estático público newXPath() {
devolver xpathFactory.newXPath();
}
Los siguientes métodos evalúan una expresión XPath en un contexto determinado y devuelven el valor resultante:
import javax.xml.xpath.*;
importar org.w3c.dom.*;
...
cadena estática pública evalToString (expresión de cadena,
Contexto del objeto) arroja XPathExpressionException {
devolver (Cadena) newXPath().evaluar(expresión, contexto,
XPathConstants.STRING);
}
evalToBoolean booleano estático público (expresión de cadena,
Contexto del objeto) arroja XPathExpressionException {
return ((Boolean) newXPath().evaluate(expresión, contexto,
XPathConstants.BOOLEAN)).booleanValue();
}
pública estática doble evalToNumber (expresión de cadena,
Contexto del objeto) arroja XPathExpressionException {
return ((Double) newXPath().evaluate(expresión, contexto,
XPathConstants.NÚMERO)).doubleValue();
}
Nodo estático público evalToNode (expresión de cadena,
Contexto del objeto) arroja XPathExpressionException {
devolver (Nodo) newXPath().evaluate(expresión, contexto,
XPathConstants.NODE);
}
lista de nodos estática pública evalToNodeList (expresión de cadena,
Contexto del objeto) arroja XPathExpressionException {
devolver (NodeList) newXPath().evaluate(expresión, contexto,
XPathConstants.NODESET);
}
El método setData() del modelo de datos utiliza el método de resolución XPath para extraer información del documento XML combinado:
público sincronizado void setData(String xml)
lanza IOException, SAXException,
Excepción de configuración del analizador,
XPathExpressionException {
intentar{
Lista de matrices lista de valores
= nueva ListaArray();
Documento doc = parsePortfolioDoc(xml);
NodeList nodeList = XMLUtil.evalToNodeList(
"/cartera/acciones", doc);
para (int i = 0; i < nodeList.getLength(); i++) {
Nodo nodo = nodeList.item(i);
Stock de StockBean = nuevo StockBean();
valores.setSymbol(
XMLUtil.evalToString("@symbol", nodo));
acciones.setShares(
(int) XMLUtil.evalToNumber("@shares", nodo));
stock.setPrecioPagado(
XMLUtil.evalToNumber("@paidPrice", nodo));
listadevalores.add(valores);
}
this.stockList = lista de acciones;
} captura (Excepción e){
Logger.global.logp(Nivel.SEVERO, "DataModel", "setData",
e.getMessage(), e);
}
}
Una vez que los datos están disponibles en el modelo de datos del lado del servidor, se pueden procesar de acuerdo con los requisitos de la aplicación. Luego, debes responder a la solicitud de Ajax.
Generar la respuesta en el lado del servidor
Devolver HTML como respuesta a una solicitud de Ajax es la solución más simple porque puede crear el marcado usando la sintaxis JSP y el cliente Ajax simplemente usa el elemento <div> o <span>. La propiedad internalHTML inserta HTML. en algún lugar de la página. Sin embargo, es más eficaz devolver datos al cliente Ajax sin ningún marcado de presentación. Puede utilizar formato XML o JSON.
Generar respuesta XML. Java EE proporciona muchas opciones para crear documentos XML: generados mediante JSP, creados a partir de un árbol de objetos mediante JAXB o generados mediante javax.xml.transform. El transformador del siguiente ejemplo serializará un árbol DOM:
import javax.xml.transform.*;
importar javax.xml.transform.dom.*;
importar javax.xml.transform.stream.*;
...
serializerFactory pública estática TransformerFactory;
estático {
serializerFctory = TransformerFactory.newInstance();
}
serialización pública estática vacía (nodo nodo, salida OutputStream)
lanza TransformerException {
Serializador de transformador = serializerFctory.newTransformer();
Propiedades serializerProps = nuevas Propiedades();
serializerProps.put(OutputKeys.METHOD, "xml");
serializer.setOutputProperties(serializerProps);
Fuente fuente = new DOMSource(nodo);
Resultado resultado = nuevo StreamResult(out);
serializer.transform(fuente, resultado);
}
Hay tantas opciones estándar y marcos de desarrollo para generar XML en el lado del servidor que lo único que tienes que hacer es elegir el que funcione para ti. Sin embargo, en el cliente, la situación es muy diferente ya que XML sólo se puede analizar utilizando DOM. Algunos navegadores también admiten XPath y XSLT.
En artículos anteriores sobre Ajax, aprendió cómo generar XML a través de JSP y luego analizarlo en el cliente usando JavaScript y DOM. Otra solución es utilizar JSON en lugar de XML como formato de datos para responder a solicitudes de Ajax. Como se mencionó anteriormente, una cadena JSON se puede convertir en un árbol de objetos JavaScript usando la función eval(). Esto es más sencillo que usar JavaScript para extraer información del árbol DOM. Todo lo que necesitas es una buena clase de utilidad que genere JSON en el lado del servidor.
Codificación JSON. La clase JSONEncoder proporciona métodos para codificar literales, objetos y matrices. Los resultados se almacenan en java.lang.StringBuilder:
paquete ajaxapp.util
public class JSONEncoder {
buf privado StringBuilder;
JSONEncoder público() {
buf = nuevo StringBuilder();
}
...
}
El método caracter () codifica un solo carácter:
carácter público vacío (char ch) {
cambiar (canal) {
caso \:
caso \":
caso \ :
buf.append( \ );
buf.append(ch);
romper;
caso :
buf.append( \ );
buf.append();
romper;
caso
:
buf.append( \ );
buf.append(
);
romper;
caso
:
buf.append( \ );
buf.append(
);
romper;
por defecto:
si (canal >= 32 && canal < 128)
buf.append(ch);
demás{
buf.append( \ );
buf.append(u);
para (int j = 12; j >= 0; j-=4) {
intk = (((int)ch) >> j) & 0x0f;
int c = k < 10 ?
buf.append((char) c);
}
}
}
}
El método string() codifica la cadena completa:
public void string(String str) {
int longitud = str.longitud();
para (int i = 0; i < longitud; i++)
personaje(str.charAt(i));
}
El método literal() codifica el literal de JavaScript:
literal público vacío (valor del objeto) {
si (valor instancia de cadena) {
buf.append(");
cadena ((Cadena) valor);
buf.append(");
} else if (valor instancia del personaje) {
buf.append(\);
carácter(((Carácter) valor).charValue());
buf.append(\);
} demás
buf.append(value.toString());
}
El método coma() añade un carácter de coma:
private void comma() {
buf.append(,);
}
El método deleteLastComma() eliminará el último carácter de coma al final del búfer (si lo hay):
private void deleteLastComma() {
si (buf. longitud() > 0)
si (buf.charAt(buf.length()-1) ==,)
buf.deleteCharAt(buf.length()-1);
}
El método startObject() añade un carácter { para indicar el comienzo de un objeto JavaScript:
public void startObject() {
buf.append({);
}
El método propiedad() codifica propiedades de JavaScript:
propiedad pública vacía (nombre de cadena, valor de objeto) {
buf.append(nombre);
buf.append(:);
literal(valor);
coma();
}
El método endObject() añade un carácter } para indicar el final de un objeto JavaScript:
public void endObject() {
eliminarÚltimaComa();
buf.append(});
coma();
}
El método startArray() agrega un carácter [ para indicar el comienzo de una matriz de JavaScript:
public void startArray() {
buf.append([);
}
El método element() codifica los elementos de una matriz de JavaScript:
public void element(Valor del objeto) {
literal(valor);
coma();
}
El método endArray() añade un carácter ] para indicar el final de una matriz de JavaScript:
public void endArray() {
eliminarÚltimaComa();
buf.append();
coma();
}
El método toString() devuelve una cadena JSON:
public String toString() {
eliminarÚltimaComa();
devolver buf.toString();
}
El método clear() borra el búfer:
public void clear() {
buf.setLength(0);
}
DataModel utiliza la clase JSONEncoder para codificar los datos que mantiene:
público sincronizado String getData() {
JSONEncoder json = nuevo JSONEncoder();
json.startArray();
para (int i = 0; i < stockList.size(); i++) {
Acciones de StockBean = lista de acciones.get(i);
json.startObject();
json.property("símbolo", stock.getSymbol());
json.property("acciones", stock.getShares());
json.property("PrecioPagado", stock.getPrecioPagado());
json.endObject();
}
json.endArray();
devolver json.toString();
}
Si se proporcionan parámetros de solicitud xml, la página ajaxCtrl.jsp establecerá los datos del modelo. De lo contrario, la página utilizará la expresión EL ${dataModel.data} para generar la cadena JSON devuelta por getData():
< %@ taglib prefix="c" uri=" http://java.sun.com/jsp/ jstl /núcleo " %>
...
< jsp:useBean id="modelo de datos" alcance="sesión"
class="ajaxapp.model.DataModel" />
<c:elegir>
...
<c:cuando prueba="${!empty param.xml}">
<c:establecer objetivo="${modelo de datos}"
propiedad="datos"
valor="${param.xml}" />
< /c:cuándo>
<c:de lo contrario>
${modelodedatos.datos}
< /c:de lo contrario>
< /c:elegir>
Este trabajo no está completo porque el cliente Ajax debe procesar los datos JSON.
Procesamiento de respuestas en el lado del cliente
En una aplicación web típica, el contenido se genera en el lado del servidor mediante JSP, marcos web y bibliotecas de etiquetas. Las aplicaciones Ajax son ideales para esta situación porque los marcos web como JavaServer Faces y Oracle ADF Faces son muy útiles para crear aplicaciones Ajax. Sin embargo, todavía existen diferencias significativas entre las aplicaciones Ajax y las que no son Ajax. Cuando usa Ajax, debe procesar los datos en el lado del cliente y usar JavaScript para generar contenido dinámicamente para proporcionar los datos al usuario.
Si utiliza el formato JSON para la conversión de datos, es muy fácil convertir texto en un árbol de objetos utilizando la función eval() proporcionada por JavaScript. Si prefiere utilizar XML, hay muchas otras cosas que debe hacer, pero este formato también tiene sus propias ventajas. Por ejemplo, muchos tipos de clientes pueden utilizar XML, mientras que JSON sólo es fácil de analizar en un entorno JavaScript. Además, al utilizar XML, los errores se pueden encontrar y corregir más rápido, reduciendo así el tiempo de depuración.
Utilice JavaScript para acceder al árbol DOM. La API DOM de JavaScript es muy similar al paquete org.w3c.dom de Java. La principal diferencia es el acceso a las propiedades. En JavaScript puede acceder a las propiedades directamente, mientras que Java trata las propiedades como privadas y necesita acceder a ellas mediante los métodos get y set. Por ejemplo, puede obtener el elemento raíz de un documento a través de dom.documentElement.
El DOM es una API de bajo nivel que proporciona acceso a la estructura del documento analizado. Por ejemplo, en la mayoría de los casos desea ignorar los comentarios y es posible que no desee tener nodos de texto adyacentes. Considere el siguiente ejemplo sencillo:
var xml = "< elemento>da< !--comentario-->ta&"
+ "< ![CDATA[cdata< /elemento>";
Puede analizar la cadena XML anterior utilizando la función de utilidad presentada anteriormente:
var dom = parse(xml);
Puede encontrar el código de la función parse() en ajaxUtil.js; en este caso, la función devuelve un árbol DOM cuyo elemento raíz contiene un nodo de texto, seguido de un comentario, otro nodo de texto y un nodo de datos de caracteres. Si desea incluir texto sin comentarios, debe iterar sobre los elementos secundarios del elemento, concatenando los valores de los nodos de datos de texto y caracteres (que tienen tipos 3 y 4 respectivamente):
var elemento = dom.documentElement;
var childNodes = elemento.childNodes;
var texto = "";
para (var i = 0; i < childNodes.length; i++)
si (childNodes[i].nodeValue) {
var tipo = childNodes[i].nodeType;
si (tipo == 3 || tipo == 4)
texto += childNodes[i].nodeValue;
}
Cuando trabaje con el DOM, debe crear un pequeño conjunto de funciones de utilidad para evitar tener que lidiar con estos detalles de bajo nivel.
Utilice JavaScript para generar contenido dinámico. Los navegadores web le permiten acceder a la estructura DOM de una página web a través de objetos de documento. Por ejemplo, puedes usar document.getElementById(...) para encontrar un elemento muy fácilmente. También puede crear nuevos elementos y nodos de texto que se pueden insertar en documentos existentes. Sin embargo, es más sencillo crear HTML concatenando cadenas como se muestra a continuación:
function updateInfo(solicitud) {
var acciones = eval(request.responseText);
var tabla = "< borde de la tabla = 1 relleno de celda = 5>";
tabla += "<tr>";
tabla += "< th>Símbolo< /th>";
tabla += "< th>Tendencia< /th>";
table += "< th>Último precio< /th>";
tabla += "< /tr>";
for (var i = 0; i < acciones.longitud; i++) {
var compartir = acciones[i];
símbolo var = escapeXML(compartir.símbolo)
var tendencia = compartir.tendencia > 0 "+" : "-";
var últimoPrecio = new Número(compartir.últimoPrecio).toFixed(2);
tabla += "<tr>";
tabla += "< td>" + símbolo + "< /td>";
tabla += "< td>" + tendencia + "< /td>";
tabla += "< td>" + últimoPrecio + "< /td>";
tabla += "< /tr>";
}
tabla += "< /tabla>";
document.getElementById("tabla").innerHTML = tabla;
}
El HTML generado se puede insertar
en un elemento vacío configurando la propiedad InnerHTML del objeto devuelto por GetElementById (), por ejemplo:
<div ID = "Tabla">
< /div>
El ejemplo en este artículo utiliza la función UpdateInfo () como una devolución de llamada para manejar las respuestas a las solicitudes AJAX enviadas al servidor a través de SendInforeQuest en el archivo ajaxLogic.js. Si desea actualizar la información cada 5 segundos, puede usar la función SetInterval () de JavaScript:
VAR Symbols = [...];
setInterval ("sendInforequest (símbolos, updateInfo)", 5000);
Una clase llamada DataFeed simula una alimentación del lado del servidor. La página AJAXCTRL.JSP llama al método getData () de la feed, devolviendo la respuesta como una cadena JSON. En el lado del cliente, la función UpdateInfo () analiza la cadena JSON usando eval (request.esponsetext), como se muestra en el ejemplo del código anterior.