En effet, le mot clé est "ajax" : JavaScript asynchrone et XML . Cependant, ces dernières années, c'est plus que souvent JavaScript asynchrone et JSON . En gros, vous laissez JS exécuter une requête HTTP asynchrone et mettre à jour l'arbre DOM HTML en fonction des données de la réponse.
Puisque c'est plutôt un fastidieux pour le faire fonctionner sur tous les navigateurs (en particulier Internet Explorer par rapport à d'autres), il existe de nombreuses bibliothèques JavaScript qui simplifient cette tâche en une seule fonction et couvrent autant que possible les bogues et les problèmes spécifiques aux navigateurs, tels que jQuery , Prototype , Mootools . Puisque jQuery est le plus populaire de nos jours, je vais l'utiliser dans les exemples ci-dessous.
Exemple de retour de coup d'envoi String
en texte brut
Créer un /some.jsp
comme ci-dessous (note : les extraits de code dans cette réponse ne s'attendent pas à ce que le fichier JSP soit placé dans un sous-dossier, si c'est le cas, modifiez l'URL de la servlet en conséquence à partir de "someservlet"
a "${pageContext.request.contextPath}/someservlet"
(il est simplement omis dans les extraits de code pour des raisons de brièveté) :
<!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>
Créez une servlet avec un doGet()
qui ressemblent à ceci :
@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.
}
Mapper cette servlet sur un modèle d'URL de /someservlet
o /someservlet/*
comme ci-dessous (évidemment, le modèle d'URL est libre de votre choix, mais vous devrez modifier l'adresse de l'utilisateur. someservlet
URL dans les exemples de code JS sur toute la place en conséquence) :
package com.example;
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Ou, si vous n'êtes pas encore sur un conteneur compatible avec Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc. ou plus récent), vous pouvez le mettre en correspondance dans le fichier web.xml
à l'ancienne (voir aussi notre page wiki sur les servlets ):
<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>
Ouvrez maintenant le site http://localhost:8080/context/test.jsp dans le navigateur et appuyez sur le bouton. Vous verrez que le contenu du div est mis à jour avec la réponse du servlet.
Retour à List<String>
en JSON
Avec JSON au lieu du texte en clair comme format de réponse, vous pouvez même aller un peu plus loin. Cela permet plus de dynamique. Tout d'abord, vous aimeriez disposer d'un outil permettant de convertir les objets Java en chaînes JSON. Il en existe de nombreux (voir le bas de la page cette page pour un aperçu). Mon préféré est Google Gson . Téléchargez et placez son fichier JAR dans /WEB-INF/lib
de votre application web.
Voici un exemple qui affiche List<String>
como <ul><li>
. Le 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);
}
Le code 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>.
});
});
});
Notez que jQuery analyse automatiquement la réponse en tant que JSON et vous donne directement un objet JSON ( responseJson
) comme argument de fonction lorsque vous définissez le type de contenu de la réponse à application/json
. Si vous oubliez de le définir ou si vous vous fiez à un défaut de text/plain
o text/html
alors le responseJson
ne vous donnera pas un objet JSON, mais une simple chaîne de caractères et vous devrez manipuler manuellement les paramètres suivants JSON.parse()
par la suite, ce qui est donc totalement inutile si vous définissez correctement le type de contenu en premier lieu.
Retour à Map<String, String>
en JSON
Voici un autre exemple qui affiche Map<String, String>
como <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);
}
Et le 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>.
});
});
});
avec
<select id="someselect"></select>
Retour à List<Entity>
en JSON
Voici un exemple qui affiche List<Product>
dans un <table>
où le Product
a les propriétés suivantes Long id
, String name
y BigDecimal price
. Le 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);
}
Le code 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>.
});
});
});
Retour à List<Entity>
en tant que XML
Voici un exemple qui fait effectivement la même chose que l'exemple précédent, mais avec XML au lieu de JSON. En utilisant JSP comme générateur de sortie XML, vous verrez qu'il est moins fastidieux de coder la table et tout le reste. JSTL est de cette façon beaucoup plus utile car vous pouvez l'utiliser pour itérer sur les résultats et effectuer le formatage des données côté serveur. La 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);
}
Le code JSP (note : si vous mettez le code <table>
dans un <jsp:include>
il peut être réutilisé ailleurs dans une réponse non-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>
Le code 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".
});
});
Vous avez maintenant probablement compris pourquoi XML est tellement plus puissant que JSON dans le but particulier de mettre à jour un document HTML en utilisant Ajax. JSON est amusant, mais après tout, il n'est généralement utile que pour les "services web publics". Les frameworks MVC comme JSF utilisent XML sous la couverture de leur magie ajax.
Ajaxifier un formulaire existant
Vous pouvez utiliser jQuery $.serialize()
pour ajaxifier facilement les formulaires POST existants sans avoir à se préoccuper de la collecte et du passage des paramètres de saisie des formulaires individuels. En partant d'un formulaire existant qui fonctionne parfaitement bien sans JavaScript/jQuery (et qui se dégrade donc gracieusement lorsque l'utilisateur final a désactivé 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>
Vous pouvez l'améliorer progressivement avec ajax comme ci-dessous :
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
Dans la servlet, vous pouvez faire la distinction entre les requêtes normales et les requêtes ajax comme ci-dessous :
@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.
}
}
El plugin jQuery Form fait plus ou moins la même chose que l'exemple jQuery ci-dessus, mais il a un support transparent supplémentaire pour multipart/form-data
comme l'exige le téléchargement de fichiers.
Envoi manuel des paramètres de la demande au servlet
Si vous n'avez pas de formulaire, mais que vous souhaitez simplement interagir avec le servlet "en arrière-plan" en envoyant des données, vous pouvez utiliser jQuery. $.param()
pour convertir facilement un objet JSON en une chaîne de requête codée en URL.
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
Le même doPost()
comme indiqué ci-dessus peut être réutilisé. Notez que la syntaxe ci-dessus fonctionne également avec $.get()
dans jQuery et doGet()
dans la servlet.
Envoi manuel d'un objet JSON à la servlet
Si, pour une raison quelconque, vous avez l'intention d'envoyer l'objet JSON dans son ensemble plutôt que sous la forme de paramètres de requête individuels, vous devrez le sérialiser en une chaîne de caractères à l'aide de la commande JSON.stringify()
(qui ne fait pas partie de jQuery) et demander à jQuery de définir le type de contenu de la requête comme étant application/json
au lieu de (par défaut) application/x-www-form-urlencoded
. Cela ne peut pas être fait via $.post()
mais elle doit être réalisée via $.ajax()
comme ci-dessous.
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) {
// ...
}
});
Notez que beaucoup de starters mélangent contentType
con dataType
. Le site contentType
représente le type de la demande corps. Le site dataType
représente le type (attendu) de la réponse ce qui n'est généralement pas nécessaire puisque jQuery le détecte déjà automatiquement en se basant sur l'élément de réponse Content-Type
en-tête.
Ensuite, pour traiter l'objet JSON dans la servlet qui n'est pas envoyé en tant que paramètres de requête individuels mais en tant que chaîne JSON entière de la manière décrite ci-dessus, il suffit d'analyser manuellement le corps de la requête à l'aide d'un outil JSON au lieu d'utiliser getParameter()
de la manière habituelle. A savoir, les servlets ne supportent pas application/json
des demandes formatées, mais seulement application/x-www-form-urlencoded
o multipart/form-data
demandes formatées. Gson prend également en charge l'analyse d'une chaîne JSON en un objet 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();
// ...
Notez que tout ceci est plus maladroit que d'utiliser simplement $.param()
. Normalement, vous voulez utiliser JSON.stringify()
uniquement si le service cible est, par exemple, un service JAX-RS (RESTful) qui, pour une raison quelconque, ne peut consommer que des chaînes JSON et non des paramètres de requête ordinaires.
Envoi d'une redirection à partir d'une servlet
Il est important de réaliser et de comprendre que toute sendRedirect()
y forward()
par la servlet sur une requête ajax ne ferait que transférer ou rediriger la requête ajax elle-même et non pas le document/fenêtre principal d'où provient la requête ajax. Dans ce cas, JavaScript/jQuery ne récupère que la réponse redirigée/transférée en tant que responseText
dans la fonction de rappel. Si elle représente une page HTML entière et non une réponse XML ou JSON spécifique à ajax, alors tout ce que vous pouvez faire est de remplacer le document actuel par celle-ci.
document.open();
document.write(responseText);
document.close();
Notez que cela ne change pas l'URL que l'utilisateur final voit dans la barre d'adresse du navigateur. Il y a donc des problèmes avec la possibilité d'ajouter des signets. Par conséquent, il est préférable de renvoyer une "instruction" à JavaScript/jQuery pour effectuer une redirection plutôt que de renvoyer le contenu complet de la page redirigée. Par exemple, en renvoyant un booléen ou une 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;
}
// ...
}
Voir aussi :