Thật vậy, từ khóa là "ajax": JavaScript và XML không đồng bộ . Tuy nhiên, năm ngoái, nó thường xuyên hơn JavaScript và JSON không đồng bộ . Về cơ bản, bạn để cho JS thực hiện một yêu cầu HTTP không đồng bộ và cập nhật cây DOM HTML dựa trên dữ liệu phản hồi.
Vì đây là một công việc khá tẻ nhạt để làm cho nó hoạt động trên tất cả các trình duyệt (đặc biệt là Internet Explorer so với các trình duyệt khác), có rất nhiều thư viện JavaScript đơn giản hóa điều này trong các chức năng đơn lẻ và bao gồm nhiều lỗi / quirks cụ thể của trình duyệt trong các trình duyệt , chẳng hạn như jQuery , Prototype , Mootools . Vì jQuery phổ biến nhất hiện nay, tôi sẽ sử dụng nó trong các ví dụ dưới đây.
Ví dụ khởi động trở lại String
dưới dạng văn bản thuần túy
Tạo một cái /some.jsp
giống như bên dưới (lưu ý: mã không mong đợi tệp JSP được đặt trong thư mục con, nếu bạn làm như vậy, hãy thay đổi URL servlet tương ứng):
<!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>
Tạo một servlet với một doGet()
phương thức giống như thế này:
@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.
}
Ánh xạ servlet này trên một mẫu URL /someservlet
hoặc /someservlet/*
như bên dưới (rõ ràng, mẫu URL là miễn phí cho sự lựa chọn của bạn, nhưng bạn cần thay đổi someservlet
URL trong các ví dụ về mã JS trên tất cả các vị trí tương ứng):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Hoặc, khi bạn chưa sử dụng bộ chứa tương thích Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, v.v. hoặc mới hơn), sau đó ánh xạ nó web.xml
theo cách thức cũ (xem thêm trang wiki Servlets của chúng tôi ):
<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>
Bây giờ hãy mở http: // localhost: 8080 / bối cảnh / test.jsp trong trình duyệt và nhấn nút. Bạn sẽ thấy rằng nội dung của div được cập nhật với phản hồi của servlet.
Trở lại List<String>
như JSON
Với JSON thay vì văn bản gốc như định dạng phản hồi, bạn thậm chí có thể có thêm một số bước nữa. Nó cho phép nhiều động lực hơn. Trước tiên, bạn muốn có một công cụ để chuyển đổi giữa các đối tượng Java và chuỗi JSON. Có rất nhiều trong số họ là tốt (xem dưới cùng của trang này để biết tổng quan). Yêu thích cá nhân của tôi là Google Gson . Tải xuống và đặt tệp JAR của nó trong /WEB-INF/lib
thư mục ứng dụng web của bạn.
Đây là một ví dụ hiển thị List<String>
như <ul><li>
. Các 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);
}
Mã 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>.
});
});
});
Xin lưu ý rằng jQuery tự động phân tích cú pháp phản hồi dưới dạng JSON và cung cấp cho bạn trực tiếp một đối tượng JSON ( responseJson
) làm đối số hàm khi bạn đặt loại nội dung phản hồi thành application/json
. Nếu bạn quên đặt nó hoặc dựa vào mặc định text/plain
hoặc text/html
, thì responseJson
đối số sẽ không cung cấp cho bạn một đối tượng JSON, nhưng một chuỗi vanilla đơn giản và bạn cần phải tự tìm hiểu xung quanh JSON.parse()
, điều này hoàn toàn không cần thiết nếu bạn đặt loại nội dung ngay ở vị trí đầu tiên.
Trở lại Map<String, String>
như JSON
Đây là một ví dụ khác hiển thị Map<String, String>
như <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);
}
Và bản tin:
$(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>.
});
});
});
với
<select id="someselect"></select>
Trở lại List<Entity>
như JSON
Dưới đây là một ví dụ mà hiển thị List<Product>
trong một <table>
nơi Product
lớp có các thuộc tính Long id
, String name
và BigDecimal price
. Các 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);
}
Mã 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>.
});
});
});
Trở lại List<Entity>
dưới dạng XML
Đây là một ví dụ có hiệu quả tương tự như ví dụ trước, nhưng sau đó với XML thay vì JSON. Khi sử dụng JSP làm trình tạo đầu ra XML, bạn sẽ thấy rằng việc mã hóa bảng và tất cả sẽ ít tẻ nhạt hơn. JSTL theo cách này hữu ích hơn nhiều khi bạn thực sự có thể sử dụng nó để lặp lại kết quả và thực hiện định dạng dữ liệu phía máy chủ. Các 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);
}
Mã JSP (lưu ý: nếu bạn đặt mã <table>
a <jsp:include>
, nó có thể được sử dụng lại ở nơi khác trong phản hồi không phải là 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>
Mã 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".
});
});
Bây giờ bạn có thể nhận ra tại sao XML mạnh hơn JSON nhiều cho mục đích cụ thể là cập nhật tài liệu HTML bằng Ajax. JSON là buồn cười, nhưng nói chung chỉ hữu ích cho cái gọi là "dịch vụ web công cộng". Các khung công tác MVC như JSF sử dụng XML dưới vỏ bọc cho phép thuật ajax của chúng.
Xác định một hình thức hiện có
Bạn có thể sử dụng jQuery $.serialize()
để dễ dàng xác định các biểu mẫu POST hiện có mà không phải loay hoay với việc thu thập và chuyển các tham số đầu vào của biểu mẫu riêng lẻ. Giả sử một biểu mẫu hiện có hoạt động hoàn toàn tốt mà không cần JavaScript / jQuery (và do đó làm giảm một cách duyên dáng khi enduser đã tắt 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>
Bạn có thể tăng cường dần dần với ajax như sau:
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
Bạn có thể trong servlet phân biệt giữa các yêu cầu thông thường và yêu cầu ajax như dưới đây:
@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.
}
}
Các plugin jQuery Mẫu làm ít hoặc nhiều tương tự như trên jQuery ví dụ, nhưng nó đã hỗ trợ trong suốt bổ sung cho multipart/form-data
các hình thức theo yêu cầu của tập tin tải lên.
Gửi thủ công các tham số yêu cầu đến servlet
Nếu bạn hoàn toàn không có biểu mẫu, nhưng chỉ muốn tương tác với servlet "trong nền", theo đó bạn muốn POST một số dữ liệu, thì bạn có thể sử dụng jQuery $.param()
để dễ dàng chuyển đổi một đối tượng JSON thành mã hóa URL chuỗi truy vấn.
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
doPost()
Phương pháp tương tự như được hiển thị ở trên có thể được sử dụng lại. Hãy lưu ý rằng cú pháp trên cũng hoạt động với $.get()
trong jQuery và doGet()
trong servlet.
Gửi thủ công đối tượng JSON đến servlet
Tuy nhiên, nếu bạn có ý định gửi toàn bộ đối tượng JSON thay vì tham số yêu cầu riêng lẻ vì một số lý do, thì bạn cần tuần tự hóa nó thành một chuỗi bằng cách sử dụng JSON.stringify()
(không phải là một phần của jQuery) và hướng dẫn jQuery đặt loại nội dung yêu cầu application/json
thay thế của (mặc định) application/x-www-form-urlencoded
. Điều này không thể được thực hiện thông qua $.post()
chức năng tiện lợi, nhưng cần phải được thực hiện thông qua $.ajax()
như dưới đây.
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) {
// ...
}
});
Hãy lưu ý rằng rất nhiều người mới bắt đầu trộn contentType
với dataType
. Các contentType
đại diện cho loại cơ thể yêu cầu . Kiểu dataType
đại diện (dự kiến) của phần phản hồi , thường không cần thiết vì jQuery đã tự động phát hiện nó dựa trên Content-Type
tiêu đề của phản hồi .
Sau đó, để xử lý đối tượng JSON trong servlet không được gửi dưới dạng tham số yêu cầu riêng lẻ mà là toàn bộ chuỗi JSON theo cách trên, bạn chỉ cần phân tích thủ công phần thân yêu cầu bằng cách sử dụng công cụ JSON thay vì sử dụng getParameter()
thông thường đường. Cụ thể, các servlet không hỗ trợ application/json
các yêu cầu được định dạng, mà chỉ application/x-www-form-urlencoded
hoặc multipart/form-data
các yêu cầu được định dạng. Gson cũng hỗ trợ phân tích chuỗi JSON thành đối tượng 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();
// ...
Hãy lưu ý rằng tất cả điều này là vụng về hơn là chỉ sử dụng $.param()
. Thông thường, bạn chỉ muốn sử dụng JSON.stringify()
nếu dịch vụ đích là dịch vụ JAX-RS (RESTful) vì một số lý do chỉ có khả năng tiêu thụ chuỗi JSON và không phải là tham số yêu cầu thông thường.
Gửi một chuyển hướng từ servlet
Điều quan trọng cần nhận ra và hiểu là bất kỳ sendRedirect()
và forward()
gọi bởi servlet theo yêu cầu ajax sẽ chỉ chuyển tiếp hoặc chuyển hướng yêu cầu ajax chứ không phải tài liệu / cửa sổ chính nơi yêu cầu ajax bắt nguồn. Trong trường hợp đó, JavaScript / jQuery chỉ lấy ra phản hồi được chuyển hướng / chuyển tiếp dưới dạng responseText
biến trong hàm gọi lại. Nếu nó đại diện cho toàn bộ trang HTML và không phải là phản hồi XML hoặc JSON dành riêng cho ajax, thì tất cả những gì bạn có thể làm là thay thế tài liệu hiện tại bằng nó.
document.open();
document.write(responseText);
document.close();
Lưu ý rằng điều này không thay đổi URL như enduser nhìn thấy trên thanh địa chỉ của trình duyệt. Vì vậy, có vấn đề với bookmarkability. Do đó, tốt hơn hết là bạn chỉ cần trả lại "hướng dẫn" cho JavaScript / jQuery để thực hiện chuyển hướng thay vì trả lại toàn bộ nội dung của trang được chuyển hướng. Ví dụ: bằng cách trả về boolean hoặc 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;
}
// ...
}
Xem thêm: