详细讲解在Java Web项目中,JavaScript如何通过现代Fetch API与原生XMLHttpRequest向Servlet及JSP接口发起AJAX请求并获取JSON数据,涵盖后端接口编写、前端调用、中文乱码处理与常见错误排查。
JavaScript怎么通过AJAX请求JSP或Servlet接口获取数据
时间:2026-10-30
浏览:429
文章目录
在 Java Web 项目中,客户端网页获取服务端数据最普遍的方式是使用 JavaScript 发起 AJAX 异步请求。其核心运行路径十分直接:前端通过 fetch() 或原生的 XMLHttpRequest 向服务端指定的 URL 路由发送 HTTP 请求;后端的 Servlet(或 JSP)接收请求后执行业务逻辑,设置响应格式为 application/json;charset=UTF-8 并将数据通过输出流写回;浏览器接收到返回的文本后将其解析为 JavaScript 对象,进而局部更新页面,无需重载整个网页。
一、核心通信架构与协作机制
理解 AJAX 与后端交互的关键在于明确职责边界。在传统的 MVC 架构下,Servlet 作为控制器负责接收前端请求并组织数据,JSP 则多用于视图层展示。当采用前后端分离或局部异步更新时,后端直接退化为“数据接口”,返回标准格式的纯数据(如 JSON),不再由服务端渲染整页 HTML。
虽然 JSP 本身也可以通过内置对象 out 输出 JSON 字符串,但由于 JSP 页面极易包含多余的换行、空格或 HTML 骨架标签,往往会导致前端反序列化失败。因此在实际工程中,强烈建议优先使用 Servlet(或基于 Servlet 封装的 Controller)作为数据接口,JSP 仅在维护老旧项目时作为轻量过渡手段。
二、服务端接口实现:输出标准 JSON 数据
要让前端能够成功解析数据,服务端必须满足两个基本要求:一是准确设置响应标头 Content-Type 为 application/json;charset=UTF-8;二是通过响应字符输出流写入格式合法的 JSON 字符串。
1. 编写 Servlet 接口(标准做法)
在 Servlet 3.0+ 中,可以使用 @WebServlet 注解快速绑定 URL 路径。以下为一个提供用户信息的 Servlet 示例:
package com.example.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/api/user")
public class UserServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 设置请求与响应编码
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
// 2. 获取客户端传递的参数
String userId = request.getParameter("id");
// 3. 组织返回的 JSON 数据(实际工程中可使用 Jackson、Gson 或 Fastjson)
String jsonResult;
if ("101".equals(userId)) {
jsonResult = "{\"code\":200,\"message\":\"success\",\"data\":{\"id\":101,\"name\":\"张三\",\"role\":\"管理员\"}}";
} else {
jsonResult = "{\"code\":404,\"message\":\"用户不存在\",\"data\":null}";
}
// 4. 将 JSON 文本写入响应流
PrintWriter out = response.getWriter();
out.write(jsonResult);
out.flush();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
2. 编写 JSP 接口(特定过渡场景)
如果业务限制必须通过 JSP 提供接口,必须在页面顶部使用指令明确媒体类型,并彻底移除所有 HTML 标签、<!DOCTYPE> 以及标签外的空行和空格:
<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<%
// 设置跨域标头(如需要)与编码
request.setCharacterEncoding("UTF-8");
String userId = request.getParameter("id");
String jsonResponse = "{\"code\":200,\"name\":\"李四\",\"dept\":\"技术部\"}";
out.print(jsonResponse);
out.flush();
%>
trimDirectiveWhitespaces="true" 非常关键。若不加上该属性,JSP 指令留下的换行符会被原样输出到响应最前部,可能导致某些前端解析器抛出语法错误。
三、前端 JavaScript 请求的两种实现方式
在前端实现层面,目前主流方式是使用原生 fetch() API;若需兼容 Internet Explorer 或更早期的系统,则使用 XMLHttpRequest。两种方式都可以顺畅对接 Servlet 与 JSP 接口。
方式一:现代标准的 Fetch API(推荐)
fetch() 基于 Promise 设计,代码简洁直观。下面演示发起 GET 请求获取数据,以及通过 POST 提交表单数据:
// 1. GET 请求示例
function loadUserData(userId) {
const url = `/api/user?id=${encodeURIComponent(userId)}`;
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(response => {
// 校验 HTTP 状态码(200-299)
if (!response.ok) {
throw new Error(`网络响应错误,状态码: ${response.status}`);
}
return response.json(); // 自动调用 JSON.parse
})
.then(result => {
console.log('获取到的服务端数据:', result);
if (result.code === 200) {
renderUser(result.data);
} else {
alert('获取失败: ' + result.message);
}
})
.catch(error => {
console.error('AJAX 请求异常:', error);
});
}
// 2. POST 请求示例(向 Servlet 提交参数)
async function submitUserUpdate(userData) {
try {
const response = await fetch('/api/user', {
method: 'POST',
headers: {
// 若以标准表单形式提交
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: new URLSearchParams(userData).toString()
});
if (!response.ok) {
throw new Error(`HTTP 异常: ${response.status}`);
}
const data = await response.json();
console.log('更新成功:', data);
} catch (err) {
console.error('提交失败:', err);
}
}
方式二:经典兼容的 XMLHttpRequest 原生方式
在不引入第三方库且需要严格兼容老旧运行环境时,XMLHttpRequest 是最稳妥的底层方案:
function loadUserDataXHR(userId) {
// 创建 XHR 实例
var xhr = new XMLHttpRequest();
var url = '/api/user?id=' + encodeURIComponent(userId);
// 初始化请求:配置方式、URL与是否异步
xhr.open('GET', url, true);
// 设置期望的数据格式
xhr.setRequestHeader('Accept', 'application/json');
// 监听状态变化
xhr.onreadystatechange = function () {
// readyState 4 表示请求完成
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
// 手动将 JSON 文本反序列化为 JS 对象
var responseData = JSON.parse(xhr.responseText);
console.log('XHR 请求成功:', responseData);
renderUser(responseData.data);
} catch (e) {
console.error('JSON 解析失败,返回内容可能非合法格式:', xhr.responseText);
}
} else {
console.error('请求失败,服务端返回状态码:', xhr.status);
}
}
};
// 触发网络发送
xhr.send(null);
}
四、页面动态呈现完整代码示例
为了让读者直观了解从点击按钮到数据显示的完整联动过程,下面给出一个无需刷新页面的 HTML + JavaScript 完整片段:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户查询面板</title>
<style>
.box { border: 1px solid #ccc; padding: 16px; width: 320px; }
.item { margin-top: 8px; }
</style>
</head>
<body>
<div class="box">
<label for="uid">用户ID:</label>
<input type="text" id="uid" value="101" />
<button id="queryBtn">查询</button>
<div id="resultBox" class="item">请输入 ID 并点击查询</div>
</div>
<script>
document.getElementById('queryBtn').addEventListener('click', function () {
const uid = document.getElementById('uid').value.trim();
const resultBox = document.getElementById('resultBox');
if (!uid) {
alert('请输入用户ID');
return;
}
resultBox.innerHTML = '正在加载中...';
// 发起 AJAX 请求
fetch('/api/user?id=' + encodeURIComponent(uid))
.then(res => {
if (!res.ok) throw new Error('HTTP 状态异常 ' + res.status);
return res.json();
})
.then(resData => {
if (resData.code === 200 && resData.data) {
const user = resData.data;
resultBox.innerHTML = `
<p><strong>姓名:</strong>${user.name}</p>
<p><strong>身份:</strong>${user.role}</p>
`;
} else {
resultBox.innerHTML = `<span style="color:red;">${resData.message || '无此数据'}</span>`;
}
})
.catch(err => {
resultBox.innerHTML = `<span style="color:red;">请求出错: ${err.message}</span>`;
});
});
</script>
</body>
</html>
五、常见问题排查与关键检查清单
在实际联调过程中,开发者经常遇到接口请求不通或解析报错的情况。以下是四种最典型的问题及其解决办法:
| 常见故障现象 | 核心原因剖析 | 具体排查与修复方法 |
|---|---|---|
| 404 Not Found | 请求 URL 路径与 Servlet 映射不匹配,或缺失了 Web 项目的应用上下文根(Context Path)。 | 检查 @WebServlet("/api/user") 或 web.xml 中 <url-pattern> 的配置;如果部署在非根目录下,URL 需补齐工程名,例如 /myproject/api/user。 |
| 中文乱码(??? 或乱码字符) | 响应未声明 UTF-8 字符集,或读取请求参数前未设置编码。 | 在 response.getWriter() 调用之前,必须先执行 response.setContentType("application/json;charset=UTF-8");针对 POST 参数设置 request.setCharacterEncoding("UTF-8")。 |
| JSON.parse 报 Unexpected token < | 前端期待返回 JSON,但后端返回了 HTML(通常是 500 异常页面、404 报错页面或 JSP 带有 HTML 标签)。 | 在浏览器的 Network(网络)标签中查看该请求的 Response 原始文本;若返回的是报错 HTML,先排查后端 Java 代码的空指针或 SQL 报错。 |
| CORS 跨域阻断 | 前端运行在不同端口(如前端运行在 http://localhost:3000,后端 Tomcat 在 8080),触发浏览器同源安全策略。 |
在 Servlet 的 doGet/doPost 中补充响应标头:response.setHeader("Access-Control-Allow-Origin", "*"),或通过后端过滤器(Filter)统一放行。 |
总结
JavaScript 向 JSP 或 Servlet 接口请求数据的流程清晰而标准:在服务端,优先使用 Servlet 作为数据端点,确保在输出内容前设置正确的字符集与 application/json 类型;在客户端,优先使用现代 fetch() API 或传统的 XMLHttpRequest 发起异步请求,并在回调中对 HTTP 状态码与 JSON 数据进行妥善处理与 DOM 渲染。只要理清路由映射、规避中文乱码并确保返回数据的纯净度,便能稳定高效地完成前后端异步数据交互。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。