Дійсно, ключове слово "ajax": Асинхронний JavaScript та XML . Однак останніми роками це більш ніж часто асинхронний JavaScript та JSON . В основному, ви дозволяєте JS виконувати асинхронний запит HTTP та оновлювати дерево HTML DOM на основі даних відповіді.
Оскільки це досить копітка робота, щоб змусити її працювати у всіх браузерах (особливо Internet Explorer порівняно з іншими), існує безліч бібліотек JavaScript, що спрощує це в окремих функціях і охоплює якомога більше помилок / хитрощів, пов’язаних із браузером, під кришками , таких як jQuery , Prototype , Mootools . Оскільки jQuery є найпопулярнішим в наші дні, я буду використовувати його в наведених нижче прикладах.
Приклад Kickoff, що повертається String
як звичайний текст
Створіть /some.jsp
подібне нижче (зверніть увагу: код не очікує, що файл JSP буде розміщений у підпапці, якщо ви це зробите відповідно, змініть 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.
}
Позначте цей сервлет на шаблоні URL-адреси /someservlet
або /someservlet/*
нижче (очевидно, що шаблон URL-адреси на ваш вибір є вільним, але вам потрібно буде змінити someservlet
URL-адресу в прикладах коду JS для всіх місць відповідно):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Або якщо ви ще не використовуєте контейнер, сумісний із Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6 тощо, або новіший), то позначайте його 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 оновлюється у відповіді сервлета.
Повертаючись List<String>
як JSON
З JSON замість простого тексту як формату відповіді ви навіть можете зробити кілька кроків далі. Це дозволяє отримати більше динаміки. По-перше, ви хочете мати інструмент для перетворення між об'єктами Java та рядками JSON. Їх також багато ( огляд див у нижній частині цієї сторінки ). Мій особистий фаворит - Google Gson . Завантажте і покладіть його файл JAR у /WEB-INF/lib
папку вашої веб-програми.
Ось приклад, який відображається List<String>
як <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);
}
Код 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
, то responseJson
аргумент не дасть вам JSON об'єкт, але звичайний рядок ванілі і вам потрібно вручну возитися зJSON.parse()
потім, що , таким чином , абсолютно НЕ потрібно , якщо вам встановіть тип вмісту на перше місце.
Повернення Map<String, String>
як JSON
Ось ще один приклад, який відображається Map<String, String>
як <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
. Сервлет:
@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>.
});
});
});
Повернення List<Entity>
як XML
Ось приклад, який фактично робить те саме, що і попередній приклад, але тоді з 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 відповіді):
<?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".
});
});
Ви вже напевно зрозумієте, чому XML настільки потужніший, ніж JSON, для конкретної мети оновлення документа HTML за допомогою Ajax. JSON - це смішно, але в цілому корисно лише для так званих "публічних веб-сервісів". Структури MVC типу JSF використовують XML під обкладинками для своєї магії ajax.
Аяксифікація існуючої форми
Ви можете використовувати jQuery $.serialize()
для легкої аяксифікації існуючих форм POST, не обмінюючись збиранням та передачею вхідних параметрів окремих форм. Припускаючи існуючу форму, яка прекрасно працює без JavaScript / jQuery (і таким чином витончено погіршується, коли у enduser відключений 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.
});
Ви можете в сервлеті розрізняти звичайні запити та запити 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
форм відповідно з вимогами завантаження файлів.
Вручну надсилання параметрів запиту на сервлет
Якщо у вас взагалі немає форми, а ви просто хотіли взаємодіяти з сервлетом "у фоновому режимі", за допомогою якого ви хочете розмістити деякі дані, тоді ви можете використовувати jQuery, $.param()
щоб легко перетворити об'єкт JSON в закодований URL рядок запиту
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
Той же doPost()
метод, що показаний тут, можна повторно використовувати. Зауважте, що вище синтаксис також працює з $.get()
jQuery та doGet()
сервлетом.
Відправлення JSON-об’єкта вручну в сервлет
Якщо ви хочете з якоїсь причини надіслати об'єкт JSON в цілому замість окремих параметрів запиту, вам знадобиться серіалізувати його до рядка, використовуючи JSON.stringify()
(не частина jQuery), і доручити jQuery встановити тип вмісту запиту application/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
. contentType
Являє тип запиту органу. dataType
Являє (очікуваний) тип реакції тіла, яке, як правило , немає необхідності , оскільки JQuery вже автоматично визначає його на основі відповідних роківContent-Type
заголовку.
Потім, щоб обробити об'єкт JSON в сервлеті, який не надсилається як індивідуальні параметри запиту, але як цілий рядок JSON вище, вам потрібно лише вручну проаналізувати тіло запиту за допомогою інструмента JSON, а не використовувати getParameter()
звичайний шлях. А саме сервлети не підтримують application/json
форматовані запити, а лише application/x-www-form-urlencoded
або multipart/form-data
відформатовані запити. 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, а не регулярні параметри запиту.
Надсилання переадресації з сервлета
Важливо усвідомити і зрозуміти, що будь-який sendRedirect()
і forward()
виклик сервлета на запит 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;
}
// ...
}
Дивись також: