¿Cómo usar Servlets y Ajax?

334

Soy muy nuevo en aplicaciones web y Servlets y tengo la siguiente pregunta:

Cada vez que imprimo algo dentro del servlet y lo llamo por el navegador web, devuelve una nueva página que contiene ese texto. ¿Hay alguna manera de imprimir el texto en la página actual usando Ajax?

Amir Rachum
fuente

Respuestas:

561

De hecho, la palabra clave es "ajax": JavaScript asíncrono y XML . Sin embargo, en los últimos años es más frecuente que JavaScript asíncrono y JSON . Básicamente, permite que JS ejecute una solicitud HTTP asincrónica y actualice el árbol DOM de HTML en función de los datos de respuesta.

Dado que es un trabajo bastante tedioso hacer que funcione en todos los navegadores (especialmente Internet Explorer versus otros), hay muchas bibliotecas de JavaScript que simplifican esto en funciones individuales y cubre la mayor cantidad posible de errores / peculiaridades específicas del navegador. , como jQuery , Prototype , Mootools . Como jQuery es más popular en estos días, lo usaré en los ejemplos a continuación.

Ejemplo de inicio regresando Stringcomo texto sin formato

Cree un me /some.jspgusta a continuación (nota: el código no espera que el archivo JSP se coloque en una subcarpeta; si lo hace, modifique la URL del servlet en consecuencia):

<!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>

Cree un servlet con un doGet()método que se vea así:

@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.
}

Asigne este servlet en un patrón de URL de /someservleto /someservlet/*como se muestra a continuación (obviamente, el patrón de URL es libre de su elección, pero deberá modificar la someservletURL en ejemplos de código JS en todos los lugares según corresponda):

@WebServlet("/someservlet/*")
public class SomeServlet extends HttpServlet {
    // ...
}

O bien, cuando todavía no se encuentra en un contenedor compatible con Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc. o más reciente), luego mapeelo a web.xmlla antigua usanza (consulte también nuestra 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>

Ahora abra el http: // localhost: 8080 / context / test.jsp en el navegador y presione el botón. Verá que el contenido del div se actualiza con la respuesta del servlet.

Volviendo List<String>como JSON

Con JSON en lugar de texto sin formato como formato de respuesta, incluso puede avanzar algunos pasos más. Permite más dinámica. Primero, le gustaría tener una herramienta para convertir entre objetos Java y cadenas JSON. También hay muchos de ellos (consulte la parte inferior de esta página para obtener una descripción general). Mi favorito personal es Google Gson . Descargue y coloque su archivo JAR en la /WEB-INF/libcarpeta de su aplicación web.

Aquí hay un ejemplo que se muestra List<String>como <ul><li>. El 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);
}

El 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>.
        });
    });
});

Tenga en cuenta que jQuery analiza automáticamente la respuesta como JSON y le proporciona directamente un objeto JSON ( responseJson) como argumento de función cuando establece el tipo de contenido de respuesta en application/json. Si olvida establecerlo o confía en un valor predeterminado de text/plaino text/html, entonces el responseJsonargumento no le daría un objeto JSON, sino una cadena de vainilla simple y luego tendría que manipular manualmente JSON.parse(), lo que es totalmente innecesario si usted establecer el tipo de contenido en primer lugar.

Volviendo Map<String, String>como JSON

Aquí hay otro ejemplo que se muestra 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);
}

Y el 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>

Volviendo List<Entity>como JSON

Aquí hay un ejemplo que se muestra List<Product>en un lugar <table>donde la Productclase tiene las propiedades Long id, String namey BigDecimal price. El 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);
}

El 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>.
        });
    });
});

Volviendo List<Entity>como XML

Aquí hay un ejemplo que efectivamente hace lo mismo que el ejemplo anterior, pero luego con XML en lugar de JSON. Cuando use JSP como generador de salida XML, verá que es menos tedioso codificar la tabla y todo. JSTL es mucho más útil de esta manera, ya que puede usarlo para iterar sobre los resultados y realizar el formateo de datos del lado del servidor. El 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);
}

El código JSP (nota: si coloca el <table>en a <jsp:include>, puede ser reutilizable en otro lugar en una respuesta que no sea 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>

El 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".
    });
});

Probablemente ya se dará cuenta de por qué XML es mucho más poderoso que JSON para el propósito particular de actualizar un documento HTML usando Ajax. JSON es divertido, pero después de todo, generalmente solo es útil para los llamados "servicios web públicos". Los marcos MVC como JSF usan XML bajo las cubiertas para su magia ajax.

Ajaxificar un formulario existente

Puede usar jQuery $.serialize()para ajaxificar fácilmente los formularios POST existentes sin perder el tiempo recogiendo y pasando los parámetros de entrada de formularios individuales. Asumiendo un formulario existente que funciona perfectamente bien sin JavaScript / jQuery (y por lo tanto se degrada con gracia cuando el usuario final tiene JavaScript deshabilitado):

<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>

Puede mejorarlo progresivamente con ajax de la siguiente manera:

$(document).on("submit", "#someform", function(event) {
    var $form = $(this);

    $.post($form.attr("action"), $form.serialize(), function(response) {
        // ...
    });

    event.preventDefault(); // Important! Prevents submitting the form.
});

En el servlet puede distinguir entre solicitudes normales y solicitudes ajax como se muestra a continuación:

@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 complemento jQuery Form hace menos o más lo mismo que el ejemplo anterior de jQuery, pero tiene soporte transparente adicional para multipart/form-dataformularios según lo requerido por la carga de archivos.

Envío manual de parámetros de solicitud al servlet

Si no tiene un formulario, pero solo desea interactuar con el servlet "en segundo plano" mediante el cual desea PUBLICAR algunos datos, puede usar jQuery $.param()para convertir fácilmente un objeto JSON en un URL codificado cadena de consulta

var params = {
    foo: "fooValue",
    bar: "barValue",
    baz: "bazValue"
};

$.post("someservlet", $.param(params), function(response) {
    // ...
});

El mismo doPost()método que se muestra aquí arriba puede reutilizarse. Tenga en cuenta que la sintaxis anterior también funciona con $.get()jQuery y doGet()en servlet.

Envío manual de objetos JSON al servlet

Sin embargo, si tiene la intención de enviar el objeto JSON como un todo en lugar de como parámetros de solicitud individuales por alguna razón, entonces deberá serializarlo en una cadena usando JSON.stringify()(no parte de jQuery) e indicar a jQuery que establezca el tipo de contenido de la solicitud en su application/jsonlugar de (predeterminado) application/x-www-form-urlencoded. Esto no se puede hacer a través de la $.post()función de conveniencia, pero se debe hacer de la $.ajax()siguiente manera.

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) {
        // ...
    }
});

Tenga en cuenta que muchos entrantes se mezclan contentTypecon dataType. El contentTyperepresenta el tipo del cuerpo de la solicitud . El dataTyperepresenta el tipo de la (esperado) respuesta cuerpo, que es generalmente innecesario, ya que jQuery ya detecta automáticamente que basado en la respuesta Content-Typede cabecera.

Luego, para procesar el objeto JSON en el servlet que no se envía como parámetros de solicitud individuales, sino como una cadena JSON completa de la manera anterior, solo necesita analizar manualmente el cuerpo de la solicitud usando una herramienta JSON en lugar de usar getParameter()el habitual camino. A saber, los servlets no son compatibles con application/jsonlas solicitudes con formato, pero sólo application/x-www-form-urlencodedo multipart/form-datasolicitudes con formato. Gson también admite el análisis de una cadena JSON en un 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();
// ...

Tenga en cuenta que todo esto es más torpe que solo usarlo $.param(). Normalmente, desea usar JSON.stringify()solo si el servicio de destino es, por ejemplo, un servicio JAX-RS (RESTful) que, por alguna razón, solo puede consumir cadenas JSON y no parámetros de solicitud regulares.

Enviar una redirección desde servlet

Es importante darse cuenta y comprender que cualquiera sendRedirect()y forward()llamar por el servlet en una solicitud ajax solo reenviaría o redirigiría la solicitud ajax en sí y no el documento / ventana principal donde se originó la solicitud ajax. JavaScript / jQuery en ese caso solo recuperaría la respuesta redirigida / reenviada como responseTextvariable en la función de devolución de llamada. Si representa una página HTML completa y no una respuesta XML o JSON específica de ajax, entonces todo lo que puede hacer es reemplazar el documento actual con ella.

document.open();
document.write(responseText);
document.close();

Tenga en cuenta que esto no cambia la URL como ve el usuario final en la barra de direcciones del navegador. Por lo tanto, hay problemas con la posibilidad de marcar. Por lo tanto, es mucho mejor simplemente devolver una "instrucción" para que JavaScript / jQuery realice una redirección en lugar de devolver todo el contenido de la página redirigida. Por ejemplo, devolviendo un booleano o una 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;
    }

    // ...
}

Ver también:

BalusC
fuente
necesita analizar el json en el último ejemplo.
shinzou
44
@kuhaku: no. Si lees la publicación de arriba a abajo, aprenderás por qué.
BalusC
1
Esta respuesta ha sido mi salvavidas durante el último mes más o menos jajaja. Aprendiendo un montón de eso. ME ENCANTA el ejemplo XML. ¡Gracias por armar esto! Una pregunta novata si tienes tiempo. ¿Hay alguna razón para poner la carpeta xml en WEB-INF?
Jonathan Laliberte
1
@JonathanLaliberte: Entonces los usuarios no pueden descargarlos.
BalusC
@BalusC, tu ejemplo XML es genial, gracias. Pero obtengo "No se puede" reemplazar "la propiedad de referencia indefinida o nula" para $("#somediv").html($(responseXml).find("data").html())esta línea. También dice "Número incorrecto de argumentos o asignación de propiedad no válida". También puedo ver que mi XML está lleno de datos cuando lo depuro. Algunas ideas ?
629
14

La forma correcta de actualizar la página que se muestra actualmente en el navegador del usuario (sin volver a cargarla) es hacer que algún código que se ejecute en el navegador actualice el DOM de la página.

Ese código es típicamente javascript que está incrustado o vinculado desde la página HTML, de ahí la sugerencia de AJAX. (De hecho, si suponemos que el texto actualizado proviene del servidor a través de una solicitud HTTP, este es el clásico AJAX).

También es posible implementar este tipo de cosas utilizando algún complemento o complemento del navegador, aunque puede ser complicado para un complemento acceder a las estructuras de datos del navegador para actualizar el DOM. (Los complementos de código nativo normalmente escriben en algunos marcos gráficos que están incrustados en la página).

Stephen C
fuente
13

Te mostraré un ejemplo completo de servlet y cómo llamar a ajax.

Aquí, vamos a crear el ejemplo simple para crear el formulario de inicio de sesión con servlet.

index.html

<form>  
   Name:<input type="text" name="username"/><br/><br/>  
   Password:<input type="password" name="userpass"/><br/><br/>  
   <input type="button" value="login"/>  
</form>  

Aquí está la muestra ajax

       $.ajax
        ({
            type: "POST",           
            data: 'LoginServlet='+name+'&name='+type+'&pass='+password,
            url: url,
        success:function(content)
        {
                $('#center').html(content);           
            }           
        });

Código de servlet de LoginServlet: -

    package abc.servlet;

import java.io.File;


public class AuthenticationServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {   
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        try{
        HttpSession session = request.getSession();
        String username = request.getParameter("name");
        String password = request.getParameter("pass");

                /// Your Code
out.println("sucess / failer")
        } catch (Exception ex) {
            // System.err.println("Initial SessionFactory creation failed.");
            ex.printStackTrace();
            System.exit(0);
        } 
    }
}
Mitul Maheshwari
fuente
8
$.ajax({
type: "POST",
url: "url to hit on servelet",
data:   JSON.stringify(json),
dataType: "json",
success: function(response){
    // we have the response
    if(response.status == "SUCCESS"){
        $('#info').html("Info  has been added to the list successfully.<br>"+
        "The  Details are as follws : <br> Name : ");

    }else{
        $('#info').html("Sorry, there is some thing wrong with the data provided.");
    }
},
 error: function(e){
   alert('Error: ' + e);
 }
});
SUBZ
fuente
7

Ajax (también AJAX), un acrónimo de Asynchronous JavaScript y XML) es un grupo de técnicas de desarrollo web interrelacionadas que se utilizan en el lado del cliente para crear aplicaciones web asincrónicas. Con Ajax, las aplicaciones web pueden enviar datos y recuperar datos de un servidor de forma asíncrona. A continuación se muestra un código de ejemplo:

Función de script java de la página Jsp para enviar datos al servlet con dos variables firstName y lastName:

function onChangeSubmitCallWebServiceAJAX()
    {
      createXmlHttpRequest();
      var firstName=document.getElementById("firstName").value;
      var lastName=document.getElementById("lastName").value;
      xmlHttp.open("GET","/AJAXServletCallSample/AjaxServlet?firstName="
      +firstName+"&lastName="+lastName,true)
      xmlHttp.onreadystatechange=handleStateChange;
      xmlHttp.send(null);

    }

Servlet para leer el envío de datos a jsp en formato xml (también puede usar texto. Solo necesita cambiar el contenido de la respuesta al texto y representar los datos en la función javascript).

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    String firstName = request.getParameter("firstName");
    String lastName = request.getParameter("lastName");

    response.setContentType("text/xml");
    response.setHeader("Cache-Control", "no-cache");
    response.getWriter().write("<details>");
    response.getWriter().write("<firstName>"+firstName+"</firstName>");
    response.getWriter().write("<lastName>"+lastName+"</lastName>");
    response.getWriter().write("</details>");
}
usuario3468976
fuente
5

Normalmente no puede actualizar una página desde un servlet. El cliente (navegador) tiene que solicitar una actualización. El cliente Eiter carga una página completamente nueva o solicita una actualización de una parte de una página existente. Esta técnica se llama Ajax.

Peter Knego
fuente
4

Usando bootstrap multi select

Ajax

function() { $.ajax({
    type : "get",
    url : "OperatorController",
    data : "input=" + $('#province').val(),
    success : function(msg) {
    var arrayOfObjects = eval(msg); 
    $("#operators").multiselect('dataprovider',
    arrayOfObjects);
    // $('#output').append(obj);
    },
    dataType : 'text'
    });}
}

En servlet

request.getParameter("input")
Thakhani Tharage
fuente