In effetti, la parola chiave è "ajax": JavaScript asincrono e XML . Tuttavia, negli ultimi anni è più che spesso JavaScript asincrono e JSON . Fondamentalmente, si consente a JS di eseguire una richiesta HTTP asincrona e aggiornare l'albero DOM HTML in base ai dati di risposta.
Dal momento che è un lavoro piuttosto noioso farlo funzionare su tutti i browser (specialmente Internet Explorer rispetto ad altri), ci sono molte librerie JavaScript che lo semplificano in singole funzioni e coprono il maggior numero possibile di bug / strane specifici del browser sotto i cofani , come jQuery , Prototype , Mootools . Poiché jQuery è più popolare in questi giorni, lo userò negli esempi seguenti.
Esempio di avvio che ritorna String
come testo normale
Crea un /some.jsp
like di seguito (nota: il codice non prevede che il file JSP venga inserito in una sottocartella, in tal caso, modifica di conseguenza l'URL del servlet):
<!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>
Crea un servlet con un doGet()
metodo simile al seguente:
@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.
}
Mappa questo servlet su un pattern URL di /someservlet
o /someservlet/*
come di seguito (ovviamente, il pattern URL è gratuito a tua scelta, ma dovresti modificare l' someservlet
URL negli esempi di codice JS in tutto il luogo di conseguenza):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Oppure, quando non sei ancora su un contenitore compatibile con Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, ecc. O più recenti), quindi mappaloweb.xml
alla vecchia maniera (vedi anche la nostra pagina wiki Servlet ):
<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>
Ora apri http: // localhost: 8080 / context / test.jsp nel browser e premi il pulsante. Vedrai che il contenuto del div viene aggiornato con la risposta servlet.
Tornando List<String>
come JSON
Con JSON invece del testo in chiaro come formato di risposta è possibile ottenere ulteriori passi. Consente una maggiore dinamica. Innanzitutto, ti piacerebbe avere uno strumento per convertire tra oggetti Java e stringhe JSON. Ce ne sono anche molti (vedi il fondo di questa pagina per una panoramica). Il mio preferito è Google Gson . Scarica e metti il suo file JAR nella /WEB-INF/lib
cartella della tua applicazione web.
Ecco un esempio che viene visualizzato List<String>
come <ul><li>
. Il 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);
}
Il codice 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>.
});
});
});
Si noti che jQuery analizza automaticamente la risposta come JSON e fornisce direttamente un oggetto JSON ( responseJson
) come argomento della funzione quando si imposta il tipo di contenuto della risposta su application/json
. Se dimentichi di impostarlo o fai affidamento su un valore predefinito di text/plain
o text/html
, allora l' responseJson
argomento non ti darebbe un oggetto JSON, ma una semplice stringa di vaniglia e avresti bisogno di armeggiare manualmente in JSON.parse()
seguito, il che è quindi totalmente inutile se tu imposta il tipo di contenuto al primo posto.
Tornando Map<String, String>
come JSON
Ecco un altro esempio che viene visualizzato Map<String, String>
come <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);
}
E il 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>.
});
});
});
con
<select id="someselect"></select>
Tornando List<Entity>
come JSON
Ecco un esempio che mostra List<Product>
in <table>
cui la Product
classe ha le proprietà Long id
, String name
e BigDecimal price
. Il 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);
}
Il codice 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>.
});
});
});
Restituendo List<Entity>
come XML
Ecco un esempio che funziona esattamente come nell'esempio precedente, ma poi con XML anziché JSON. Quando usi JSP come generatore di output XML, vedrai che è meno noioso codificare la tabella e tutto il resto. JSTL è molto più utile in quanto è possibile utilizzarlo per scorrere i risultati ed eseguire la formattazione dei dati lato server. Il 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);
}
Il codice JSP (nota: se si inserisce <table>
in a <jsp:include>
, potrebbe essere riutilizzabile altrove in una risposta 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>
Il codice 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".
});
});
Ormai probabilmente ti renderai conto del perché XML è molto più potente di JSON per il particolare scopo di aggiornare un documento HTML usando Ajax. JSON è divertente, ma dopo tutto generalmente utile solo per i cosiddetti "servizi web pubblici". I framework MVC come JSF usano XML sotto le copertine per la loro magia ajax.
Ajaxifying un modulo esistente
È possibile utilizzare jQuery $.serialize()
per ajaxificare facilmente i moduli POST esistenti senza armeggiare con la raccolta e il passaggio dei singoli parametri di input del modulo. Supponendo un modulo esistente che funzioni perfettamente senza JavaScript / jQuery (e quindi degrada con garbo quando l'utente finale ha JavaScript disabilitato):
<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>
Puoi migliorarlo progressivamente con ajax come di seguito:
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
Nel servlet puoi distinguere tra richieste normali e richieste Ajax come di seguito:
@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.
}
}
Il plug-in jQuery Form fa meno o più lo stesso dell'esempio jQuery sopra, ma ha un supporto trasparente aggiuntivo per i multipart/form-data
moduli come richiesto dai caricamenti di file.
Invio manuale dei parametri di richiesta al servlet
Se non si dispone di un modulo, ma si desidera solo interagire con il servlet "in background" per cui si desidera POST alcuni dati, è possibile utilizzare jQuery $.param()
per convertire facilmente un oggetto JSON in un codice codificato URL stringa della domanda.
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
Lo stesso doPost()
metodo mostrato qui sopra può essere riutilizzato. Nota che la sintassi sopra funziona anche con $.get()
jQuery e doGet()
in servlet.
Invio manuale dell'oggetto JSON al servlet
Se tuttavia si intende inviare l'oggetto JSON nel suo complesso anziché come singoli parametri di richiesta per qualche motivo, è necessario serializzarlo su una stringa utilizzando JSON.stringify()
(non parte di jQuery) e indicare a jQuery di impostare application/json
invece il tipo di contenuto della richiesta di (impostazione predefinita) application/x-www-form-urlencoded
. Questo non può essere fatto tramite la $.post()
funzione di convenienza, ma deve essere fatto $.ajax()
come di seguito.
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) {
// ...
}
});
Si noti che molti antipasti si mescolano contentType
con dataType
. Il contentType
rappresenta il tipo di corpo della richiesta . Il dataType
rappresenta il tipo (atteso) della risposta corpo, che di solito è superflua in quanto già jQuery autorileva è basato su di risposta Content-Type
di intestazione.
Quindi, al fine di elaborare l'oggetto JSON nel servlet che non viene inviato come singoli parametri di richiesta ma come una stringa JSON intera nel modo sopra, è necessario solo analizzare manualmente il corpo della richiesta utilizzando uno strumento JSON invece di utilizzare getParameter()
il solito modo. Vale a dire, i servlet non supportano application/json
richieste formattate, ma solo application/x-www-form-urlencoded
o multipart/form-data
richieste formattate. Gson supporta anche l'analisi di una stringa JSON in un oggetto 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();
// ...
Si noti che tutto questo è più goffo del semplice utilizzo $.param()
. Normalmente, si desidera utilizzare JSON.stringify()
solo se il servizio di destinazione è ad esempio un servizio JAX-RS (RESTful) che è per qualche motivo in grado di consumare stringhe JSON e non parametri di richiesta regolari.
Invio di un reindirizzamento dal servlet
È importante rendersi conto e comprendere che qualsiasi sendRedirect()
e forward()
chiamata da parte del servlet su una richiesta Ajax avrebbe solo inoltrato o reindirizzato la richiesta Ajax stessa e non il documento / finestra principale da cui proveniva la richiesta Ajax. JavaScript / jQuery in tal caso recupererebbe la risposta reindirizzata / inoltrata come responseText
variabile nella funzione di callback. Se rappresenta un'intera pagina HTML e non una risposta XML o JSON specifica per Ajax, tutto ciò che potresti fare è sostituire il documento corrente con esso.
document.open();
document.write(responseText);
document.close();
Nota che questo non cambia l'URL come vede l'utente finale nella barra degli indirizzi del browser. Quindi ci sono problemi con la segnalabilità. Pertanto, è molto meglio semplicemente restituire una "istruzione" per JavaScript / jQuery per eseguire un reindirizzamento invece di restituire l'intero contenuto della pagina reindirizzata. Ad esempio restituendo un valore booleano o un 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;
}
// ...
}
Guarda anche: