Rzeczywiście, słowem kluczowym jest „ajax”: asynchroniczny JavaScript i XML . Jednak w ostatnich latach jest to bardziej niż często Asynchroniczny JavaScript i JSON . Zasadniczo pozwalasz JS wykonać asynchroniczne żądanie HTTP i aktualizujesz drzewo DOM HTML na podstawie danych odpowiedzi.
Ponieważ praca we wszystkich przeglądarkach (szczególnie Internet Explorer w porównaniu z innymi) jest dość żmudna , istnieje wiele bibliotek JavaScript, które upraszczają to w pojedynczych funkcjach i obejmują jak najwięcej błędów / dziwactw związanych z przeglądarką pod maskami , takich jak jQuery , Prototype , Mootools . Ponieważ jQuery jest obecnie najbardziej popularny, użyję go w poniższych przykładach.
Przykład rozpoczęcia powracający String
jako zwykły tekst
Utwórz /some.jsp
podobny sposób poniżej (uwaga: kod nie oczekuje umieszczenia pliku JSP w podfolderze, jeśli to zrobisz, odpowiednio zmień adres URL serwletu):
<!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>
Utwórz serwlet za pomocą doGet()
metody, która wygląda następująco:
@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.
}
Odwzoruj ten serwlet na wzorzec adresu URL /someservlet
lub /someservlet/*
niższy (oczywiście wzorzec adresu URL jest dowolny, ale musisz odpowiednio zmienić someservlet
adres URL w przykładach kodu JS):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Lub, jeśli nie jesteś jeszcze w kontenerze kompatybilnym z Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6 itp. Lub nowszym), to zamapuj go w web.xml
staromodny sposób (zobacz także naszą stronę wiki Servletów ):
<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>
Teraz otwórz http: // localhost: 8080 / context / test.jsp w przeglądarce i naciśnij przycisk. Zobaczysz, że zawartość div zostanie zaktualizowana o odpowiedź serwletu.
Wracając List<String>
jako JSON
Dzięki JSON zamiast zwykłego tekstu jako formatu odpowiedzi możesz nawet zrobić kilka kroków dalej. Pozwala na większą dynamikę. Po pierwsze, chcesz mieć narzędzie do konwersji między obiektami Java i łańcuchami JSON. Jest ich także dużo (przegląd na dole tej strony ). Moim ulubionym jest Google Gson . Pobierz i umieść plik JAR w /WEB-INF/lib
folderze swojej aplikacji internetowej.
Oto przykład, który wyświetla się List<String>
jako <ul><li>
. Serwlet:
@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);
}
Kod 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>.
});
});
});
Zauważ, że jQuery automatycznie analizuje odpowiedź jako JSON i daje ci bezpośrednio obiekt JSON ( responseJson
) jako argument funkcji, gdy ustawisz typ zawartości odpowiedzi na application/json
. Jeśli zapomnisz ustawić go lub polegać na domyślnym ustawieniu text/plain
lub text/html
, wówczas responseJson
argument nie dałby ci obiektu JSON, ale zwykły ciąg waniliowy i musiałbyś ręcznie manipulować nim JSON.parse()
później, co jest całkowicie niepotrzebne, jeśli ustaw typ zawartości na pierwszym miejscu.
Wracając Map<String, String>
jako JSON
Oto kolejny przykład, który wyświetla się Map<String, String>
jako <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);
}
I 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>.
});
});
});
z
<select id="someselect"></select>
Wracając List<Entity>
jako JSON
Oto przykład, który wyświetla się List<Product>
w miejscu, w <table>
którym Product
klasa ma właściwości Long id
, String name
oraz BigDecimal price
. Serwlet:
@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);
}
Kod 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>.
});
});
});
Zwracanie List<Entity>
jako XML
Oto przykład, który działa tak samo jak poprzedni przykład, ale następnie z XML zamiast JSON. Gdy używasz JSP jako generatora danych wyjściowych XML, zobaczysz, że mniej uciążliwe jest kodowanie tabeli i wszystkich innych. JSTL jest w ten sposób o wiele bardziej pomocny, ponieważ można go używać do iteracji wyników i formatowania danych po stronie serwera. Serwlet:
@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);
}
Kod JSP (uwaga: jeśli umieścisz go <table>
w <jsp:include>
, może on zostać użyty w innym miejscu w odpowiedzi innej niż 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>
Kod 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".
});
});
Prawdopodobnie już zdasz sobie sprawę, dlaczego XML jest o wiele potężniejszy niż JSON, szczególnie w celu aktualizacji dokumentu HTML za pomocą Ajax. JSON jest zabawny, ale ogólnie przydatny tylko w przypadku tak zwanych „publicznych usług sieciowych”. Frameworki MVC, takie jak JSF, używają XML pod przykryciem do swojej magii ajax.
Ajaxifying istniejącego formularza
Możesz użyć jQuery $.serialize()
do łatwego przechwytywania istniejących formularzy POST bez zbędnego kręcenia się przy zbieraniu i przekazywaniu poszczególnych parametrów wejściowych formularza. Przyjmując istniejącą formę, która działa idealnie bez JavaScript / jQuery (i tym samym łagodnie się psuje, gdy użytkownik ma wyłączoną obsługę 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>
Możesz go stopniowo ulepszać za pomocą ajax, jak poniżej:
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
W serwletu możesz rozróżnić zwykłe żądania i żądania ajax, jak poniżej:
@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.
}
}
Plugin jQuery Formularz robi mniej lub więcej takie same jak powyżej przykład jQuery, ale ma dodatkową przezroczystą obsługę multipart/form-data
formularzy wymaganych przez przesłanych plików.
Ręczne wysyłanie parametrów żądania do serwletu
Jeśli w ogóle nie masz formularza, a chcesz po prostu wchodzić w interakcję z serwletem „w tle”, w którym chcesz wysłać dane, możesz użyć jQuery, $.param()
aby łatwo przekonwertować obiekt JSON na kodowany adres URL ciąg zapytania.
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
Tę samą doPost()
metodę, jak pokazano powyżej, można ponownie wykorzystać. Zauważ, że powyższa składnia działa również z $.get()
jQuery i doGet()
serwletem.
Ręczne wysyłanie obiektu JSON do serwletu
Jeśli jednak z jakiegoś powodu zamierzasz wysłać obiekt JSON jako całość zamiast jako indywidualne parametry żądania, musisz serializować go do ciągu za pomocą JSON.stringify()
(nie stanowiącego części jQuery) i poinstruować jQuery, aby application/json
zamiast tego ustawić typ zawartości żądania z (domyślnie) application/x-www-form-urlencoded
. Nie można tego zrobić za pomocą $.post()
funkcji wygody, ale należy to zrobić $.ajax()
w następujący sposób.
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) {
// ...
}
});
Zauważ, że wiele przystawek łączy się contentType
z dataType
. contentType
Reprezentuje typ żądania ciała. dataType
Reprezentuje (expected) typ reakcji organizmu, który jest zwykle konieczne, ponieważ jQuery już automatycznie wykrywa go w oparciu o odpowiedź za Content-Type
cel.
Następnie, aby przetworzyć obiekt JSON w serwlecie, który nie jest wysyłany jako indywidualne parametry żądania, ale jako cały ciąg JSON w powyższy sposób, wystarczy ręcznie przeanalizować treść żądania za pomocą narzędzia JSON zamiast getParameter()
zwykłego droga. Mianowicie, serwlety nie obsługują application/json
sformatowanych żądań, ale tylko application/x-www-form-urlencoded
lub multipart/form-data
sformatowanych żądań. Gson obsługuje również parsowanie łańcucha JSON w obiekcie 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();
// ...
Pamiętaj, że to wszystko jest bardziej niezdarne niż zwykłe używanie $.param()
. Zwykle chcesz używać JSON.stringify()
tylko wtedy, gdy usługą docelową jest np. Usługa JAX-RS (RESTful), która z jakiegoś powodu jest w stanie zużywać tylko ciągi JSON, a nie parametry regularnego żądania.
Wysyłanie przekierowania z serwletu
Ważne jest, aby zrozumieć i zrozumieć, że dowolne sendRedirect()
i forward()
wywołanie przez serwlet na żądanie ajax spowoduje tylko przekazanie lub przekierowanie samego żądania ajax, a nie główny dokument / okno, z którego pochodzi zapytanie ajax. JavaScript / jQuery w takim przypadku pobierałby tylko przekierowaną / przekazaną odpowiedź jako responseText
zmienną w funkcji wywołania zwrotnego. Jeśli reprezentuje całą stronę HTML, a nie odpowiedź w formacie XML lub JSON specyficzną dla ajax, to wszystko, co możesz zrobić, to zastąpić go bieżącym dokumentem.
document.open();
document.write(responseText);
document.close();
Pamiętaj, że nie zmienia to adresu URL, tak jak użytkownik widzi w pasku adresu przeglądarki. Są więc problemy z bukmacherstwem. Dlatego o wiele lepiej jest po prostu zwrócić „instrukcję” dla JavaScript / jQuery, aby wykonać przekierowanie, zamiast zwracać całą treść przekierowanej strony. Np. Przez zwrócenie wartości logicznej lub adresu 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;
}
// ...
}
Zobacz też: