AJAX XML 实例
AJAX XML 实例(中文讲解)
本教程展示如何使用 AJAX 与服务器交互,处理 XML 格式的响应数据,结合 Node.js 和 Express 构建后端,MongoDB 存储数据。实例实现用户数据的查询(GET 请求)和添加(POST 请求),服务器返回 XML 响应,客户端使用 XMLHttpRequest
和 fetch
解析 XML 数据并显示。本例简洁实用,突出 XML 的处理,适合理解 AJAX 与 XML 的交互。
1. 实例概述
- 目标:通过 AJAX 实现用户管理功能:
- GET 请求:查询 MongoDB 用户数据,返回 XML 格式,显示在页面。
- POST 请求:提交新用户数据,存入 MongoDB,返回 XML 确认。
- 技术栈:
- 客户端:HTML + JavaScript(
XMLHttpRequest
和fetch
)。 - 服务器:Node.js + Express,生成 XML 响应。
- 数据库:MongoDB,存储用户数据。
- 数据格式:XML(服务器响应),JSON(POST 请求体)。
2. 环境准备
确保以下工具已安装:
- Node.js:LTS 版本(如 v20.x),运行
node -v
检查。 - MongoDB:本地运行或使用 MongoDB Atlas。
- npm 依赖:Express、MongoDB 驱动和
xmlbuilder2
(用于生成 XML)。
安装依赖:
mkdir ajax-xml-example
cd ajax-xml-example
npm init -y
npm install express mongodb xmlbuilder2
MongoDB 数据准备:
use myDatabase
db.users.insertMany([
{ name: "张三", age: 25, hobbies: ["读书", "旅行"] },
{ name: "李四", age: 30, hobbies: ["编程", "运动"] }
])
3. 服务器端代码(Node.js + Express + MongoDB)
创建 server.js
,查询 MongoDB 数据并返回 XML 格式响应。
const express = require('express');
const { MongoClient } = require('mongodb');
const { create } = require('xmlbuilder2');
const app = express();
const uri = 'mongodb://localhost:27017/myDatabase';
const client = new MongoClient(uri);
app.use(express.json());
app.use(express.static('public'));
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST');
next();
});
// 查询用户(返回 XML)
app.get('/users', async (req, res) => {
try {
await client.connect();
const db = client.db('myDatabase');
const collection = db.collection('users');
const users = await collection.find({}).toArray();
// 构建 XML
const xml = create({ version: '1.0' })
.ele('users')
.dtd()
users.forEach(user => {
const userNode = xml.ele('user');
userNode.ele('name').txt(user.name).up();
userNode.ele('age').txt(user.age).up();
const hobbiesNode = userNode.ele('hobbies');
user.hobbies.forEach(hobby => {
hobbiesNode.ele('hobby').txt(hobby).up();
});
});
const xmlString = xml.end({ prettyPrint: true });
res.set('Content-Type', 'text/xml');
res.send(xmlString);
} catch (error) {
console.error('查询失败:', error);
res.status(500).send('<error>服务器错误</error>');
} finally {
await client.close();
}
});
// 添加用户(接收 JSON,返回 XML)
app.post('/users', async (req, res) => {
try {
await client.connect();
const db = client.db('myDatabase');
const collection = db.collection('users');
const user = req.body;
if (!user.name || !user.age) {
const xml = create({ version: '1.0' })
.ele('error')
.txt('姓名和年龄必填')
.end({ prettyPrint: true });
res.status(400).set('Content-Type', 'text/xml').send(xml);
return;
}
const result = await collection.insertOne(user);
// 构建 XML 响应
const xml = create({ version: '1.0' })
.ele('response')
.ele('message').txt('添加成功').up()
.ele('id').txt(result.insertedId.toString()).up()
.end({ prettyPrint: true });
res.set('Content-Type', 'text/xml');
res.send(xml);
} catch (error) {
console.error('添加失败:', error);
res.status(500).send('<error>服务器错误</error>');
} finally {
await client.close();
}
});
app.listen(3000, () => console.log('服务器运行在 http://localhost:3000'));
- 说明:
- 使用
xmlbuilder2
生成 XML 响应。 /users
(GET):返回用户列表,格式为 XML。/users
(POST):接收 JSON 数据,插入用户,返回 XML 确认。- 设置
Content-Type: text/xml
。 - CORS 允许跨域请求。
XML 响应示例:
- GET
/users
:
<?xml version="1.0"?>
<!DOCTYPE users>
<users>
<user>
<name>张三</name>
<age>25</age>
<hobbies>
<hobby>读书</hobby>
<hobby>旅行</hobby>
</hobbies>
</user>
<user>
<name>李四</name>
<age>30</age>
<hobbies>
<hobby>编程</hobby>
<hobby>运动</hobby>
</hobbies>
</user>
</users>
- POST
/users
:
<?xml version="1.0"?>
<response>
<message>添加成功</message>
<id>someObjectId</id>
</response>
4. 客户端代码(HTML + AJAX)
创建 public/index.html
,使用 XMLHttpRequest
和 fetch
处理 XML 响应,显示用户数据。
<!DOCTYPE html>
<html>
<head>
<title>AJAX XML 示例</title>
<style>
table { border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
button, input { padding: 10px; margin: 5px; }
</style>
</head>
<body>
<h1>用户管理</h1>
<div>
<input id="name" placeholder="姓名">
<input id="age" type="number" placeholder="年龄">
<button onclick="addUser()">添加用户(XMLHttpRequest)</button>
<button onclick="addUserFetch()">添加用户(Fetch)</button>
</div>
<button onclick="loadUsers()">加载用户</button>
<table id="userTable">
<thead>
<tr><th>姓名</th><th>年龄</th><th>爱好</th></tr>
</thead>
<tbody id="userList"></tbody>
</table>
<script>
// XMLHttpRequest 加载用户
function loadUsers() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000/users', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const xml = xhr.responseXML;
if (!xml) throw new Error('无效 XML');
const users = parseUsersXML(xml);
displayUsers(users);
} catch (error) {
document.getElementById('userList').innerHTML = `<tr><td colspan="3">错误:${error.message}</td></tr>`;
}
} else {
document.getElementById('userList').innerHTML = `<tr><td colspan="3">错误:${xhr.statusText} (${xhr.status})</td></tr>`;
}
}
};
xhr.onerror = function () {
document.getElementById('userList').innerHTML = '<tr><td colspan="3">错误:网络错误</td></tr>';
};
xhr.send();
}
// XMLHttpRequest 添加用户
function addUser() {
const name = document.getElementById('name').value;
const age = parseInt(document.getElementById('age').value);
if (!name || !age) {
alert('请输入姓名和年龄');
return;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/users', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const xml = xhr.responseXML;
if (!xml) throw new Error('无效 XML');
const message = xml.querySelector('message')?.textContent || '未知';
alert(message);
loadUsers();
} catch (error) {
alert(`错误:${error.message}`);
}
} else {
alert(`添加失败:${xhr.statusText}`);
}
}
};
xhr.onerror = function () {
alert('网络错误');
};
xhr.send(JSON.stringify({ name, age, hobbies: ['未知'] }));
}
// Fetch 添加用户
async function addUserFetch() {
const name = document.getElementById('name').value;
const age = parseInt(document.getElementById('age').value);
if (!name || !age) {
alert('请输入姓名和年龄');
return;
}
try {
const response = await fetch('http://localhost:3000/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, age, hobbies: ['未知'] })
});
if (!response.ok) throw new Error(`服务器错误:${response.status}`);
const text = await response.text();
const parser = new DOMParser();
const xml = parser.parseFromString(text, 'text/xml');
if (xml.querySelector('parsererror')) throw new Error('无效 XML');
const message = xml.querySelector('message')?.textContent || '未知';
alert(message);
loadUsers();
} catch (error) {
alert(`添加失败:${error.message}`);
}
}
// 解析 XML 用户数据
function parseUsersXML(xml) {
const users = [];
const userNodes = xml.getElementsByTagName('user');
for (let userNode of userNodes) {
const name = userNode.getElementsByTagName('name')[0]?.textContent || '';
const age = parseInt(userNode.getElementsByTagName('age')[0]?.textContent || '0');
const hobbyNodes = userNode.getElementsByTagName('hobby');
const hobbies = Array.from(hobbyNodes).map(hobby => hobby.textContent);
users.push({ name, age, hobbies });
}
return users;
}
// 显示用户列表
function displayUsers(users) {
const userList = document.getElementById('userList');
userList.innerHTML = '';
if (users.length === 0) {
userList.innerHTML = '<tr><td colspan="3">无用户数据</td></tr>';
return;
}
users.forEach(user => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${user.name}</td>
<td>${user.age}</td>
<td>${user.hobbies.join(', ')}</td>
`;
userList.appendChild(row);
});
}
</script>
</body>
</html>
- 说明:
- 使用
<table>
显示用户数据,解析 XML 响应。 loadUsers()
:通过xhr.responseXML
或DOMParser
解析 XML,提取用户数据。addUser()
和addUserFetch()
:发送 JSON 数据,接收 XML 响应,显示结果。parseUsersXML()
:解析 XML,转换为 JavaScript 对象。- 处理错误(如无效 XML、网络问题)。
5. 运行和测试
- 启动服务器:
node server.js
- 访问客户端:
- 打开浏览器,访问
http://localhost:3000
。 - 点击“加载用户”,显示用户表格:
姓名 年龄 爱好 张三 25 读书, 旅行 李四 30 编程, 运动
- 输入姓名和年龄,点击“添加用户(XMLHttpRequest)”或“添加用户(Fetch)”,添加用户并刷新表格。
- 响应解析:
XMLHttpRequest
:直接使用xhr.responseXML
获取 DOM 对象。fetch
:通过response.text()
获取 XML 字符串,使用DOMParser
解析。
6. XML 在实例中的作用
- 服务器:使用
xmlbuilder2
生成 XML 响应,设置Content-Type: text/xml
。 - 客户端:
XMLHttpRequest
:通过xhr.responseXML
获取解析后的 DOM 对象。fetch
:获取文本后使用DOMParser
解析为 DOM。- 使用 DOM 方法(如
getElementsByTagName
)提取数据。 - 与 JSON 对比:
- XML 更结构化,适合复杂嵌套数据,但解析较繁琐。
- JSON 更轻量,现代主流,解析更简单(
response.json()
)。
7. 注意事项与最佳实践
- XML 解析:
- 检查
xhr.responseXML
或xml.querySelector('parsererror')
确保有效 XML:javascript if (!xml || xml.querySelector('parsererror')) throw new Error('无效 XML');
- 跨域处理:
- 本例使用 CORS(
Access-Control-Allow-Origin: *
)。 - 生产环境限制域名:
javascript res.header('Access-Control-Allow-Origin', 'http://trusted-domain.com');
- 错误处理:
XMLHttpRequest
:检查xhr.status
和onerror
。fetch
:检查response.ok
和try-catch
。- 服务器验证输入,防止无效数据。
- 超时设置:
XMLHttpRequest
:javascript xhr.timeout = 5000; xhr.ontimeout = () => alert('请求超时');
fetch
:javascript const controller = new AbortController(); setTimeout(() => controller.abort(), 5000); fetch('http://localhost:3000/users', { signal: controller.signal });
- 推荐 JSON:
- XML 解析复杂,现代开发推荐 JSON(
fetch
+response.json()
)。 - XML 适用于遗留系统或特定需求(如 SOAP)。
- 调试:
- 使用浏览器开发者工具(Network 面板)检查 XML 响应。
- 验证 XML 格式(在线 XML 验证工具)。
8. 总结
- 实例功能:通过 AJAX 查询和添加 MongoDB 用户,处理 XML 响应,渲染表格。
- XML 角色:服务器返回 XML,客户端通过
responseXML
或DOMParser
解析。 - 技术要点:
XMLHttpRequest
:使用onreadystatechange
和responseXML
。fetch
:结合DOMParser
解析 XML。- 最佳实践:检查 XML 有效性、处理错误、设置超时,考虑 JSON 替代。
如果你需要其他格式(如 JSON+XML 混合)、更复杂功能(如分页、搜索),或想深入某部分,请告诉我,我会提供详细代码或指导!
const express = require('express');
const { MongoClient } = require('mongodb');
const { create } = require('xmlbuilder2');
const app = express();
const uri = 'mongodb://localhost:27017/myDatabase';
const client = new MongoClient(uri);
app.use(express.json());
app.use(express.static('public'));
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST');
next();
});
app.get('/users', async (req, res) => {
try {
await client.connect();
const db = client.db('myDatabase');
const collection = db.collection('users');
const users = await collection.find({}).toArray();
const xml = create({ version: '1.0' })
.ele('users')
.dtd()
users.forEach(user => {
const userNode = xml.ele('user');
userNode.ele('name').txt(user.name).up();
userNode.ele('age').txt(user.age).up();
const hobbiesNode = userNode.ele('hobbies');
user.hobbies.forEach(hobby => {
hobbiesNode.ele('hobby').txt(hobby).up();
});
});
const xmlString = xml.end({ prettyPrint: true });
res.set('Content-Type', 'text/xml');
res.send(xmlString);
} catch (error) {
console.error('查询失败:', error);
res.status(500).send('<error>服务器错误</error>');
} finally {
await client.close();
}
});
app.post('/users', async (req, res) => {
try {
await client.connect();
const db = client.db('myDatabase');
const collection = db.collection('users');
const user = req.body;
if (!user.name || !user.age) {
const xml = create({ version: '1.0' })
.ele('error')
.txt('姓名和年龄必填')
.end({ prettyPrint: true });
res.status(400).set('Content-Type', 'text/xml').send(xml);
return;
}
const result = await collection.insertOne(user);
const xml = create({ version: '1.0' })
.ele('response')
.ele('message').txt('添加成功').up()
.ele('id').txt(result.insertedId.toString()).up()
.end({ prettyPrint: true });
res.set('Content-Type', 'text/xml');
res.send(xml);
} catch (error) {
console.error('添加失败:', error);
res.status(500).send('<error>服务器错误</error>');
} finally {
await client.close();
}
});
app.listen(3000, () => console.log('服务器运行在 http://localhost:3000'));
<!DOCTYPE html>
<html>
<head>
<title>AJAX XML 示例</title>
<style>
table { border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
button, input { padding: 10px; margin: 5px; }
</style>
</head>
<body>
<h1>用户管理</h1>
<div>
<input id="name" placeholder="姓名">
<input id="age" type="number" placeholder="年龄">
<button onclick="addUser()">添加用户(XMLHttpRequest)</button>
<button onclick="addUserFetch()">添加用户(Fetch)</button>
</div>
<button onclick="loadUsers()">加载用户</button>
<table id="userTable">
<thead>
<tr><th>姓名</th><th>年龄</th><th>爱好</th></tr>
</thead>
<tbody id="userList"></tbody>
</table>
<script>
function loadUsers() {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000/users', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const xml = xhr.responseXML;
if (!xml) throw new Error('无效 XML');
const users = parseUsersXML(xml);
displayUsers(users);
} catch (error) {
document.getElementById('userList').innerHTML = `<tr><td colspan="3">错误:${error.message}</td></tr>`;
}
} else {
document.getElementById('userList').innerHTML = `<tr><td colspan="3">错误:${xhr.statusText} (${xhr.status})</td></tr>`;
}
}
};
xhr.onerror = function () {
document.getElementById('userList').innerHTML = '<tr><td colspan="3">错误:网络错误</td></tr>';
};
xhr.send();
}
function addUser() {
const name = document.getElementById('name').value;
const age = parseInt(document.getElementById('age').value);
if (!name || !age) {
alert('请输入姓名和年龄');
return;
}
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/users', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const xml = xhr.responseXML;
if (!xml) throw new Error('无效 XML');
const message = xml.querySelector('message')?.textContent || '未知';
alert(message);
loadUsers();
} catch (error) {
alert(`错误:${error.message}`);
}
} else {
alert(`添加失败:${xhr.statusText}`);
}
}
};
xhr.onerror = function () {
alert('网络错误');
};
xhr.send(JSON.stringify({ name, age, hobbies: ['未知'] }));
}
async function addUserFetch() {
const name = document.getElementById('name').value;
const age = parseInt(document.getElementById('age').value);
if (!name || !age) {
alert('请输入姓名和年龄');
return;
}
try {
const response = await fetch('http://localhost:3000/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, age, hobbies: ['未知'] })
});
if (!response.ok) throw new Error(`服务器错误:${response.status}`);
const text = await response.text();
const parser = new DOMParser();
const xml = parser.parseFromString(text, 'text/xml');
if (xml.querySelector('parsererror')) throw new Error('无效 XML');
const message = xml.querySelector('message')?.textContent || '未知';
alert(message);
loadUsers();
} catch (error) {
alert(`添加失败:${error.message}`);
}
}
function parseUsersXML(xml) {
const users = [];
const userNodes = xml.getElementsByTagName('user');
for (let userNode of userNodes) {
const name = userNode.getElementsByTagName('name')[0]?.textContent || '';
const age = parseInt(userNode.getElementsByTagName('age')[0]?.textContent || '0');
const hobbyNodes = userNode.getElementsByTagName('hobby');
const hobbies = Array.from(hobbyNodes).map(hobby => hobby.textContent);
users.push({ name, age, hobbies });
}
return users;
}
function displayUsers(users) {
const userList = document.getElementById('userList');
userList.innerHTML = '';
if (users.length === 0) {
userList.innerHTML = '<tr><td colspan="3">无用户数据</td></tr>';
return;
}
users.forEach(user => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${user.name}</td>
<td>${user.age}</td>
<td>${user.hobbies.join(', ')}</td>
`;
userList.appendChild(row);
});
}
</script>
</body>
</html>