De fato, a palavra-chave é "ajax": JavaScript assíncrono e XML . No entanto, nos últimos anos, é mais do que frequentemente JavaScript assíncrono e JSON . Basicamente, você permite que o JS execute uma solicitação HTTP assíncrona e atualize a árvore DOM HTML com base nos dados da resposta.
Como é um trabalho tedioso fazê-lo funcionar em todos os navegadores (especialmente no Internet Explorer e em outros), existem muitas bibliotecas JavaScript que simplificam isso em funções únicas e abrangem o máximo possível de bugs / peculiaridades específicos do navegador, ocultos , como jQuery , Prototype , Mootools . Como o jQuery é o mais popular atualmente, usarei nos exemplos abaixo.
Exemplo de kickoff retornando String
como texto simples
Crie /some.jsp
como abaixo (nota: o código não espera que o arquivo JSP seja colocado em uma subpasta, se você o fizer, altere o URL do servlet de acordo):
<!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>
Crie um servlet com um doGet()
método parecido com este:
@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.
}
Mapeie este servlet em um padrão de URL igual /someservlet
ou /someservlet/*
abaixo (obviamente, o padrão de URL é livre para sua escolha, mas você precisará alterar o someservlet
URL nos exemplos de código JS em todos os lugares adequadamente):
@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
// ...
}
Ou, quando você ainda não estiver em um contêiner compatível com Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc ou mais recente), mapeie- web.xml
o da maneira antiga (consulte também a página wiki de 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>
Agora abra o http: // localhost: 8080 / context / test.jsp no navegador e pressione o botão Você verá que o conteúdo da div é atualizado com a resposta do servlet.
Retornando List<String>
como JSON
Com o JSON, em vez de texto sem formatação, como formato de resposta, você pode ainda dar mais alguns passos. Permite mais dinâmica. Primeiro, você gostaria de ter uma ferramenta para converter entre objetos Java e sequências JSON. Também existem muitos deles (consulte a parte inferior desta página para uma visão geral). Meu favorito pessoal é o Google Gson . Faça o download e coloque o arquivo JAR na /WEB-INF/lib
pasta do seu aplicativo da web.
Aqui está um exemplo que é exibido List<String>
como <ul><li>
. O 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);
}
O código 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>.
});
});
});
Observe que o jQuery analisa automaticamente a resposta como JSON e fornece diretamente um objeto JSON ( responseJson
) como argumento de função quando você define o tipo de conteúdo da resposta como application/json
. Se você esquecer de configurá-lo ou confiar em um padrão de text/plain
or text/html
, o responseJson
argumento não fornecerá um objeto JSON, mas uma sequência simples de baunilha e você precisará mexer manualmente comJSON.parse()
posteriormente, o que é totalmente desnecessário se você defina o tipo de conteúdo em primeiro lugar.
Retornando Map<String, String>
como JSON
Aqui está outro exemplo que é exibido 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);
}
E o 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>.
});
});
});
com
<select id="someselect"></select>
Retornando List<Entity>
como JSON
Aqui está um exemplo que exibe List<Product>
em um <table>
onde a Product
classe tem as propriedades Long id
, String name
e BigDecimal price
. O 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);
}
O código 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>.
});
});
});
Retornando List<Entity>
como XML
Aqui está um exemplo que efetivamente é igual ao exemplo anterior, mas depois com XML em vez de JSON. Ao usar JSP como gerador de saída XML, você verá que é menos tedioso codificar a tabela e tudo. O JSTL é muito mais útil, pois você pode usá-lo para iterar sobre os resultados e executar a formatação de dados do lado do servidor. O 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);
}
O código JSP (nota: se você inserir o <table>
em a <jsp:include>
, pode ser reutilizável em outro lugar em uma resposta não-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>
O código 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".
});
});
Você provavelmente já perceberá por que o XML é muito mais poderoso que o JSON com o objetivo específico de atualizar um documento HTML usando o Ajax. O JSON é engraçado, mas, afinal, geralmente só é útil para os chamados "serviços públicos da web". Estruturas MVC como JSF usam XML nos bastidores para sua mágica no ajax.
Ajaxificando um formulário existente
Você pode usar o jQuery $.serialize()
para facilitar ajaxificar os formulários POST existentes sem se preocupar em coletar e passar os parâmetros de entrada de formulários individuais. Supondo que um formulário existente funcione perfeitamente sem JavaScript / jQuery (e, portanto, seja degradado normalmente quando o usuário final estiver com o JavaScript desativado):
<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>
Você pode aprimorá-lo progressivamente com ajax, como abaixo:
$(document).on("submit", "#someform", function(event) {
var $form = $(this);
$.post($form.attr("action"), $form.serialize(), function(response) {
// ...
});
event.preventDefault(); // Important! Prevents submitting the form.
});
É possível distinguir no servlet entre solicitações normais e solicitações de ajax, conforme abaixo:
@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.
}
}
O plugin jQuery Form faz menos ou mais o mesmo exemplo acima do jQuery, mas possui suporte transparente adicional paramultipart/form-data
formulários, conforme exigido pelos uploads de arquivos.
Envio manual de parâmetros de solicitação ao servlet
Se você não possui um formulário, mas apenas deseja interagir com o servlet "em segundo plano" no qual deseja postar alguns dados, pode usar o jQuery $.param()
para converter facilmente um objeto JSON em um codificado em URL string de consulta.
var params = {
foo: "fooValue",
bar: "barValue",
baz: "bazValue"
};
$.post("someservlet", $.param(params), function(response) {
// ...
});
O mesmo doPost()
método mostrado aqui acima pode ser reutilizado. Observe que a sintaxe acima também funciona $.get()
no jQuery e doGet()
no servlet.
Enviando Objeto JSON Manualmente para Servlet
Se você no entanto a intenção de enviar o objeto JSON como um todo em vez de como parâmetros de solicitação individuais, por algum motivo, então você precisa para serializar-lo para uma string usando JSON.stringify()
(não faz parte da jQuery) e instruir jQuery ao pedido conjunto de tipo de conteúdo a application/json
vez de (padrão) application/x-www-form-urlencoded
. Isso não pode ser feito através da $.post()
função de conveniência, mas precisa ser feito $.ajax()
conforme abaixo.
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) {
// ...
}
});
Note que um monte de entradas misturar contentType
com dataType
. O contentType
representa o tipo do corpo da solicitação . O dataType
representa o tipo (esperado) do corpo da resposta , o que geralmente é desnecessário, já que o jQuery já o detecta automaticamente com base no Content-Type
cabeçalho da resposta .
Em seguida, para processar o objeto JSON no servlet que não está sendo enviado como parâmetros de solicitação individuais, mas como uma string JSON inteira da maneira acima, você só precisa analisar manualmente o corpo da solicitação usando uma ferramenta JSON em vez de usar getParameter()
o usual caminho. Ou seja, servlets não suportam application/json
solicitações formatadas, mas apenas application/x-www-form-urlencoded
ou multipart/form-data
solicitações formatadas. O Gson também suporta a análise de uma string JSON em um objeto 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();
// ...
Observe que tudo isso é mais desajeitado do que apenas usar $.param()
. Normalmente, você deseja usar JSON.stringify()
apenas se o serviço de destino for, por exemplo, um serviço JAX-RS (RESTful) que, por algum motivo, possa apenas consumir cadeias JSON e não parâmetros de solicitação regulares.
Enviando um redirecionamento do servlet
É importante perceber e entender que qualquer chamada sendRedirect()
e forward()
pelo servlet em uma solicitação ajax encaminharia ou redirecionaria apenas a solicitação ajax em si e não o documento / janela principal em que a solicitação ajax se originou. Nesse caso, o JavaScript / jQuery recuperaria apenas a resposta redirecionada / encaminhada como responseText
variável na função de retorno de chamada. Se ele representa uma página HTML inteira e não uma resposta XML ou JSON específica do ajax, tudo o que você pode fazer é substituir o documento atual por ele.
document.open();
document.write(responseText);
document.close();
Observe que isso não altera o URL como o usuário vê na barra de endereços do navegador. Portanto, há problemas com a capacidade de marcação. Portanto, é muito melhor retornar uma "instrução" para JavaScript / jQuery para executar um redirecionamento em vez de retornar todo o conteúdo da página redirecionada. Por exemplo, retornando um booleano ou uma 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;
}
// ...
}
Veja também: