每当我在 servlet 中打印某些内容并由 web 浏览器调用它时,它都会返回一个包含该文本的新页面。有没有办法使用 Ajax 打印当前页面中的文本?
我对 Web 应用程序和 servlet 很陌生。
实际上,关键字是“Ajax”: 异步 JavaScript 和 XML 。然而,去年它比 异步 JavaScript 和 JSON 更常见。基本上,您让 JavaScript 执行异步 HTTP 请求并根据响应数据更新 HTML DOM 树。
由于让它在所有浏览器(尤其是 Internet Explorer 与其他浏览器)上工作是一项相当繁琐的工作,因此有大量 JavaScript 库可以将其简化为单个函数并涵盖尽可能多的浏览器特定的错误/怪癖,如jQuery、Prototype、Mootools。由于 jQuery 现在最流行,我将在下面的示例中使用它。
String
在下面创建一个/some.jsp类似的(注意:此答案中的代码片段不希望将 JSP 文件放置在子文件夹中,如果这样做,请相应地将 servlet URL 从 更改"someservlet"为"${pageContext.request.contextPath}/someservlet";为简洁起见,它只是从代码片段中省略):
/some.jsp
"someservlet"
"${pageContext.request.contextPath}/someservlet"
<!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()使用如下所示的方法创建一个 servlet :
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 模式,但您需要相应地更改someservletJS 代码示例中的 URL):
/someservlet
/someservlet/*
someservlet
package com.example; @WebServlet("/someservlet/*") public class SomeServlet extends HttpServlet { // ... }
或者,当您尚未使用Servlet 3.0 兼容容器(Tomcat 7、GlassFish 3、JBoss AS 6 等或更新版本)时,web.xml请以老式方式映射它(另请参阅我们的 Servlet wiki 页面):
web.xml
<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 并按下按钮。您将看到 div 的内容随着 servlet 响应而更新。
List<String>
使用JSON而不是纯文本作为响应格式,您甚至可以更进一步。它允许更多的动态。首先,您需要一个工具来在 Java 对象和 JSON 字符串之间进行转换。它们也有很多(请参阅本页底部的概述)。我个人最喜欢的是Google Gson。下载并将其 JAR 文件放在/WEB-INF/lib您的 Web 应用程序的文件夹中。
/WEB-INF/lib
这是一个显示List<String>为的示例<ul><li>。小服务程序:
<ul><li>
@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); }
JavaScript 代码:
$(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>. }); }); });
请注意,responseJson当您将响应内容类型设置为application/json. 如果您忘记设置它或依赖默认值text/plainor text/html,那么该responseJson参数不会给您一个 JSON 对象,而是一个普通的香草字符串,JSON.parse()之后您需要手动摆弄,因此如果您完全没有必要这样做首先设置内容类型。
responseJson
application/json
text/plain
text/html
JSON.parse()
Map<String, String>
这是另一个显示Map<String, String>为的示例<option>:
<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>
这是一个示例,它显示List<Product>在类具有属性和的<table>地方。小服务程序:Product``Long id``String name``BigDecimal price
List<Product>
<table>
Product``Long id``String name``BigDecimal price
@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 而不是 JSON。当使用 JSP 作为 XML 输出生成器时,您会发现对表格和所有内容进行编码变得不那么乏味了。JSTL 这种方式更有帮助,因为您实际上可以使用它来迭代结果并执行服务器端数据格式化。小服务程序:
@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 代码(注意:如果您将<table>a放入<jsp:include>,它可能可以在非 Ajax 响应的其他地方重用):
<jsp:include>
<?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>
$(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 很有趣,但毕竟一般只对所谓的“公共网络服务”有用。像JSF这样的MVC 框架在其 ajax 魔法的背后使用 XML。
您可以使用 jQuery$.serialize()轻松地 ajaxify 现有的 POST 表单,而无需费力收集和传递单个表单输入参数。假设一个现有的表单在没有 JavaScript/jQuery 的情况下工作得很好(因此当最终用户禁用 JavaScript 时会优雅地降级):
$.serialize()
<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 表单插件与上面的jQuery 示例大致相同,但它对multipart/form-data文件上传所需的表单提供了额外的透明支持。
multipart/form-data
如果您根本没有表单,而只是想与“在后台”的 servlet 进行交互,您想发布一些数据,那么您可以使用 jQuery$.param()轻松地将 JSON 对象转换为 URL 编码的对象请求参数。
$.param()
var params = { foo: "fooValue", bar: "barValue", baz: "bazValue" }; $.post("someservlet", $.param(params), function(response) { // ... });
doPost()可以重复使用上面显示的相同方法。请注意,上述语法也适用$.get()于 jQuery 和doGet()servlet。
doPost()
$.get()
但是,如果您出于某种原因打算将 JSON 对象作为一个整体而不是作为单个请求参数发送,那么您需要使用JSON.stringify()(不是 jQuery 的一部分)将其序列化为字符串并指示 jQuery 将请求内容类型设置为application/json的(默认)application/x-www- form-urlencoded。这不能通过$.post()便利功能完成,但需要通过$.ajax()以下方式完成。
JSON.stringify()
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. contentType表示 请求* 正文的类型。 表示响应 主体的dataType(预期)类型,这通常是不必要的,因为 jQuery 已经根据响应的标头自动检测它。 *Content-Type
contentType
dataType
Content-Type
然后,为了处理 servlet 中的 JSON 对象,它不是作为单个请求参数发送,而是作为整个 JSON 字符串以上述方式发送,您只需要使用 JSON 工具手动解析请求正文,而不是使用getParameter()通常的大大地。也就是说,servlet 不支持application/json格式化请求,而只application/x-www-form- urlencoded支持格式化multipart/form-data请求。Gson 还支持将 JSON 字符串解析为 JSON 对象。
getParameter()
application/x-www-form- urlencoded
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 对 ajax 请求的任何sendRedirect()和forward()调用只会转发或重定向 Ajax 请求本身 ,而不是 Ajax 请求发起的主文档/窗口。JavaScript/jQuery 在这种情况下只会检索重定向/转发的响应作为responseText回调函数中的变量。如果它代表整个 HTML 页面而不是 Ajax 特定的 XML 或 JSON 响应,那么您所能做的就是用它替换当前文档。
sendRedirect()
forward()
responseText
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; } // ... }