实际上,关键字是“ajax”:
异步JavaScript和XML
. 但是,过去的几年里
异步JavaScript和JSON
. 基本上,您可以让JS执行一个异步HTTP请求,并根据响应数据更新HTMLDOM树。
因为它很漂亮
tedious
为了使它能够跨所有浏览器工作(尤其是Internet Explorer与其他浏览器),有大量的javascript库可以在单个函数中简化这一过程,并尽可能多地覆盖引擎盖下特定于浏览器的错误/怪癖,例如
jQuery
,
Prototype
,
Mootools
. 由于jquery最近最流行,我将在下面的示例中使用它。
启动示例返回
String
纯文本格式
创建
/some.jsp
如下所示(注意:代码不希望JSP文件被放置在子文件夹中,如果这样做,请相应地更改servlet url):
<!DOCTYPE html>
<html lang="en">
<head>
<title>SO question 4112686</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).on("click", "#somebutton", function() { // When HTML DOM "click" event is invoked on element with ID "somebutton", execute the following function...
$.get("someservlet", function(responseText) { // Execute Ajax GET request on URL of "someservlet" and execute the following function with Ajax response text...
$("#somediv").text(responseText); // Locate HTML DOM element with ID "somediv" and set its text content with the response text.
});
});
</script>
</head>
<body>
<button id="somebutton">press here</button>
<div id="somediv"></div>
</body>
</html>
使用
doGet()
方法如下:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String text = "some text";
response.setContentType("text/plain"); // Set content type of the response so that jQuery knows what it can expect.
response.setCharacterEncoding("UTF-8"); // You want world domination, huh?
response.getWriter().write(text); // Write response body.
}
将此servlet映射到URL模式
/someservlet
或
/someservlet/*
如下所示(显然,URL模式由您自由选择,但您需要更改
someservlet
JS代码示例中的url将相应地覆盖所有位置):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
或者,当您还没有使用与servlet 3.0兼容的容器(Tomcat 7、Glassfish 3、JBoss as 6等或更高版本)时,将其映射到
web.xml
传统方式(另见
our Servlets wiki page
):
<servlet>
<servlet-name>someservlet</servlet-name>
<servlet-class>com.example.SomeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>someservlet</servlet-name>
<url-pattern>/someservlet/*</url-pattern>
</servlet-mapping>
现在打开
http://localhost:8080/context/test.jsp
在浏览器中按按钮。您将看到,使用servlet响应更新了DIV的内容。
返回
List<String>
作为JSON
用
JSON
您甚至可以进一步了解一些步骤,而不是将纯文本作为响应格式。它允许更多的动力。首先,您希望有一个在Java对象和JSON字符串之间转换的工具。它们也有很多(见
this page
概述)。我个人最喜欢的是
Google Gson
. 下载并放入jar文件
/WEB-INF/lib
Web应用程序的文件夹。
下面是一个显示
列表<字符串>
作为
<ul><li>
. servlet:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<String> list = new ArrayList<>();
list.add("item1");
list.add("item2");
list.add("item3");
String json = new Gson().toJson(list);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
JS代码:
$(document).on("click", "#somebutton", function() { // When HTML DOM "click" event is invoked on element with ID "somebutton", execute the following function...
$.get("someservlet", function(responseJson) { // Execute Ajax GET request on URL of "someservlet" and execute the following function with Ajax response JSON...
var $ul = $("<ul>").appendTo($("#somediv")); // Create HTML <ul> element and append it to HTML DOM element with ID "somediv".
$.each(responseJson, function(index, item) { // Iterate over the JSON array.
$("<li>").text(item).appendTo($ul); // Create HTML <li> element, set its text content with currently iterated item and append it to the <ul>.
});
});
});
请注意,jquery自动将响应解析为json,并直接为您提供一个json对象(
responseJson
)当您将响应内容类型设置为
application/json
. 如果您忘记设置或依赖默认值
text/plain
或
text/html
,然后
响应
参数不会给您一个JSON对象,而是一个普通的字符串,您需要手动处理
JSON.parse()
然后,如果您首先正确设置内容类型,这是完全不必要的。
返回
Map<String, String>
作为JSON
下面是另一个显示
映射字符串,字符串
作为
<option>
:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String> options = new LinkedHashMap<>();
options.put("value1", "label1");
options.put("value2", "label2");
options.put("value3", "label3");
String json = new Gson().toJson(options);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
以及JSP:
$(document).on("click", "#somebutton", function() { // When HTML DOM "click" event is invoked on element with ID "somebutton", execute the following function...
$.get("someservlet", function(responseJson) { // Execute Ajax GET request on URL of "someservlet" and execute the following function with Ajax response JSON...
var $select = $("#someselect"); // Locate HTML DOM element with ID "someselect".
$select.find("option").remove(); // Find all child elements with tag name "option" and remove them (just to prevent duplicate options when button is pressed again).
$.each(responseJson, function(key, value) { // Iterate over the JSON object.
$("<option>").val(key).text(value).appendTo($select); // Create HTML <option> element, set its value with currently iterated key and its text content with currently iterated item and finally append it to the <select>.
});
});
});
具有
<select id="someselect"></select>
返回
List<Entity>
作为JSON
下面是一个显示
List<Product>
在一个
<table>
在哪里
Product
类具有属性
Long id
,
String name
和
BigDecimal price
. servlet:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Product> products = someProductService.list();
String json = new Gson().toJson(products);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
JS代码:
$(document).on("click", "#somebutton", function() { // When HTML DOM "click" event is invoked on element with ID "somebutton", execute the following function...
$.get("someservlet", function(responseJson) { // Execute Ajax GET request on URL of "someservlet" and execute the following function with Ajax response JSON...
var $table = $("<table>").appendTo($("#somediv")); // Create HTML <table> element and append it to HTML DOM element with ID "somediv".
$.each(responseJson, function(index, product) { // Iterate over the JSON array.
$("<tr>").appendTo($table) // Create HTML <tr> element, set its text content with currently iterated item and append it to the <table>.
.append($("<td>").text(product.id)) // Create HTML <td> element, set its text content with id of currently iterated product and append it to the <tr>.
.append($("<td>").text(product.name)) // Create HTML <td> element, set its text content with name of currently iterated product and append it to the <tr>.
.append($("<td>").text(product.price)); // Create HTML <td> element, set its text content with price of currently iterated product and append it to the <tr>.
});
});
});
返回
列出<实体>
作为XML
这里有一个例子,它与前面的例子实际上是相同的,但是使用XML而不是JSON。当使用JSP作为XML输出生成器时,您会发现编写表和所有内容的代码就不那么繁琐了。JSTL这样做更有帮助,因为您可以实际使用它来迭代结果并执行服务器端数据格式化。servlet:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Product> products = someProductService.list();
request.setAttribute("products", products);
request.getRequestDispatcher("/WEB-INF/xml/products.jsp").forward(request, response);
}
JSP代码(注意:如果
<表>
在一个
<jsp:include>
,它可以在非Ajax响应中的其他地方重用):
<?xml version="1.0" encoding="UTF-8"?>
<%@page contentType="application/xml" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<data>
<table>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.id}</td>
<td><c:out value="${product.name}" /></td>
<td><fmt:formatNumber value="${product.price}" type="currency" currencyCode="USD" /></td>
</tr>
</c:forEach>
</table>
</data>
JS代码:
$(document).on("click", "#somebutton", function() { // When HTML DOM "click" event is invoked on element with ID "somebutton", execute the following function...
$.get("someservlet", function(responseXml) { // Execute Ajax GET request on URL of "someservlet" and execute the following function with Ajax response XML...
$("#somediv").html($(responseXml).find("data").html()); // Parse XML, find <data> element and append its HTML to HTML DOM element with ID "somediv".
});
});
到目前为止,您可能已经认识到,为了使用Ajax更新HTML文档的特定目的,XML为什么比JSON强大得多。JSON很有趣,但毕竟通常只对所谓的“公共Web服务”有用。MVC框架
JSF
在其Ajax魔力的封面下使用XML。
一种现有的形式
您可以使用jquery
$.serialize()
在不影响收集和传递单个表单输入参数的情况下,可以轻松地半开现有的post表单。假设现有表单在没有javascript/jquery的情况下运行良好(因此当最终用户禁用了javascript时会有良好的降级):
<form id="someform" action="someservlet" method="post">
<input type="text" name="foo" />
<input type="text" name="bar" />
<input type="text" name="baz" />
<input type="submit" name="submit" value="Submit" />
</form>
您可以使用Ajax逐步增强它,如下所示:
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
您可以在servlet中区分正常请求和Ajax请求,如下所示:
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String foo = request.getParameter("foo");
String bar = request.getParameter("bar");
String baz = request.getParameter("baz");
boolean ajax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
// ...
if (ajax) {
// Handle ajax (JSON or XML) response.
} else {
// Handle regular (JSP) response.
}
}
这个
jQuery Form plugin
与上面的jquery示例相同,但它对
multipart/form-data
文件上载所需的表单。
手动向servlet发送请求参数
如果您根本没有表单,但只想与servlet“在后台”交互,以便发布一些数据,那么可以使用jquery
$.param()
将JSON对象轻松转换为URL编码的查询字符串。
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
相同的
doPost()
上面所示的方法可以重用。请注意,上面的语法也适用于
$.get()
在jquery和
小狗()
在servlet中。
手动将JSON对象发送到servlet
但是,如果出于某种原因,您打算将JSON对象作为一个整体而不是作为单个请求参数发送,那么您需要使用
JSON.stringify()
(不是jquery的一部分)并指示jquery将请求内容类型设置为
应用程序/JSON
代替(默认)
application/x-www-form-urlencoded
. 这不能通过
$.post()
方便功能,但需要通过
$.ajax()
如下所示。
var data = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.ajax({
type: "POST",
url: "someservlet",
contentType: "application/json", // NOT dataType!
data: JSON.stringify(data),
success: function(response) {
// ...
}
});
一定要注意,很多启动混合
contentType
具有
dataType
. 这个
内容类型
表示的类型
请求
身体。这个
数据库类型
表示
响应
body,这通常是不必要的,因为jquery已经根据响应自动检测它。
Content-Type
标题。
然后,为了处理servlet中的JSON对象,该对象不是作为单独的请求参数发送的,而是作为一个整体的JSON字符串,您只需要使用JSON工具而不是使用
getParameter()
通常的方法。也就是说,servlet不支持
应用程序/JSON
格式化请求,但仅限于
应用程序/X-WWW-FORM-URLENCODED
或
多部分/表单数据
格式化请求。GSON还支持将JSON字符串解析为JSON对象。
JsonObject data = new Gson().fromJson(request.getReader(), JsonObject.class);
String foo = data.get("foo").getAsString();
String bar = data.get("bar").getAsString();
String baz = data.get("baz").getAsString();
// ...
请注意,这一切都比仅仅使用
.param()美元
. 正常情况下,你想用
json.stringify()。
只有当目标服务是JAX-RS(RESTful)服务时,由于某种原因,该服务只能使用JSON字符串,而不能使用常规的请求参数。
从servlet发送重定向
重要的是要认识和理解
sendRedirect()
和
forward()
servlet对Ajax请求的调用只能转发或重定向
Ajax请求本身
而不是产生Ajax请求的主文档/窗口。在这种情况下,javascript/jquery只能检索重定向/转发的响应
responseText
回调函数中的变量。如果它代表整个HTML页面,而不是Ajax特定的XML或JSON响应,那么您所能做的就是用它替换当前文档。
document.open();
document.write(responseText);
document.close();
请注意,这不会改变最终用户在浏览器地址栏中看到的URL。因此,书签功能存在问题。因此,最好只返回一条“指令”,让javascript/jquery执行重定向,而不是返回重定向页面的全部内容。例如,通过返回布尔值或URL。
String redirectURL = "http://example.com";
Map<String, String> data = new HashMap<>();
data.put("redirect", redirectURL);
String json = new Gson().toJson(data);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
function(responseJson) {
if (responseJson.redirect) {
window.location = responseJson.redirect;
return;
}
// ...
}
另请参见: