我应该如何使用 servlet 和 Ajax? [英] How should I use servlets and Ajax?

查看:19
本文介绍了我应该如何使用 servlet 和 Ajax?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当我在 servlet 中打印某些内容并由网络浏览器调用它时,它都会返回一个包含该文本的新页面.有没有办法使用ajax打印当前页面中的文本?

Whenever I print something inside the servlet and call it by the webbrowser, it returns a new page containing that text. Is there a way to print the text in the current page using Ajax?

我对 Web 应用程序和 servlet 非常陌生.

推荐答案

确实,关键字是Ajax":异步 JavaScript 和 XML.然而,在去年,异步 JavaScript 和 JSON 更为常见.基本上,您让 JavaScript 执行异步 HTTP 请求并根据响应数据更新 HTML DOM 树.

Indeed, the keyword is "Ajax": Asynchronous JavaScript and XML. However, last years it's more than often Asynchronous JavaScript and JSON. Basically, you let JavaScript execute an asynchronous HTTP request and update the HTML DOM tree based on the response data.

因为它非常繁琐的工作所有浏览器(尤其是 Internet Explorer 与其他浏览器),有大量 JavaScript 库在单个函数中简化了这一点,并涵盖了尽可能多的浏览器特定的错误/怪癖,例如 jQuery原型Mootools.由于 jQuery 目前最流行,我将在下面的示例中使用它.

Since it's pretty tedious work to make it to work across all browsers (especially Internet Explorer versus others), there are plenty of JavaScript libraries out which simplifies this in single functions and covers as many as possible browser-specific bugs/quirks under the hoods, such as jQuery, Prototype, Mootools. Since jQuery is most popular these days, I'll use it in the below examples.

创建一个 /some.jsp 如下(注意:此答案中的代码片段不希望将 JSP 文件放置在子文件夹中,如果这样做,请相应地更改 servlet URL"someservlet""${pageContext.request.contextPath}/someservlet";为了简洁起见,它只是从代码片段中省略了):

Create a /some.jsp like below (note: the code snippets in this answer doesn't expect the JSP file being placed in a subfolder, if you do so, alter servlet URL accordingly from "someservlet" to "${pageContext.request.contextPath}/someservlet"; it's merely omitted from the code snippets for brevity):

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

使用 doGet() 方法创建一个 servlet,如下所示:

Create a servlet with a doGet() method which look like this:

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

将此 servlet 映射到 /someservlet/someservlet/* 的 URL 模式上,如下所示(显然,URL 模式可以由您自由选择,但您必须需要相应地更改 JS 代码示例中的 someservlet URL):

Map this servlet on an URL pattern of /someservlet or /someservlet/* as below (obviously, the URL pattern is free to your choice, but you'd need to alter the someservlet URL in JS code examples over all place accordingly):

package com.example;

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

或者,当您还没有使用 Servlet 3.0 兼容容器时(Tomcat 7、GlassFish 3、JBoss AS 6 等.或更新版本),然后以旧式方式将其映射到 web.xml 中(另请参阅我们的 Servlets wiki 页面):

Or, when you're not on a Servlet 3.0 compatible container yet (Tomcat 7, GlassFish 3, JBoss AS 6, etc. or newer), then map it in web.xml the old fashioned way (see also our Servlets wiki page):

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

现在在浏览器中打开 http://localhost:8080/context/test.jsp 并按下按钮.您将看到 div 的内容随 servlet 响应而更新.

Now open the http://localhost:8080/context/test.jsp in the browser and press the button. You'll see that the content of the div get updated with the servlet response.

使用 JSON 而不是纯文本作为响应格式,您甚至可以更进一步.它允许更多的动态.首先,您需要一个工具来在 Java 对象和 JSON 字符串之间进行转换.还有很多(请参阅此页面的底部以了解概述).我个人最喜欢的是 Google Gson.下载并将其 JAR 文件放在您的 Web 应用程序的 /WEB-INF/lib 文件夹中.

With JSON instead of plaintext as response format you can even get some steps further. It allows for more dynamics. First, you'd like to have a tool to convert between Java objects and JSON strings. There are plenty of them as well (see the bottom of this page for an overview). My personal favourite is Google Gson. Download and put its JAR file in /WEB-INF/lib folder of your web application.

这是一个将 List 显示为

  • 的示例.小服务程序:

    Here's an example which displays List<String> as <ul><li>. The 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);
    }
    

    JavaScript 代码:

    The JavaScript code:

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

    请注意,当您将响应内容类型设置为 application/jsonresponseJson) 作为函数参数>.如果您忘记设置它或依赖 text/plaintext/html 的默认值,则 responseJson 参数不会给您一个 JSON 对象,但一个普通的普通字符串,你需要手动摆弄 JSON.parse() 之后,因此如果您首先将内容类型设置正确,则完全没有必要.

    Do note that jQuery automatically parses the response as JSON and gives you directly a JSON object (responseJson) as function argument when you set the response content type to application/json. If you forget to set it or rely on a default of text/plain or text/html, then the responseJson argument wouldn't give you a JSON object, but a plain vanilla string and you'd need to manually fiddle around with JSON.parse() afterwards, which is thus totally unnecessary if you set the content type right in first place.

    这是将 Map 显示为 的另一个示例:

    Here's another example which displays Map<String, String> as <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);
    }
    

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

    <select id="someselect"></select>
    

    以 JSON 形式返回 List

    这是一个在

    中显示 List 的示例,其中 Product 类具有属性 LongidString nameBigDecimal price.小服务程序:

    Returning List<Entity> as JSON

    Here's an example which displays List<Product> in a <table> where the Product class has the properties Long id, String name and BigDecimal price. The 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);
    }
    

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

    以 XML 形式返回 List

    这是一个示例,它的作用与前一个示例相同,但使用的是 XML 而不是 JSON.当使用 JSP 作为 XML 输出生成器时,您会发现对表格和所有内容进行编码变得不那么乏味了.JSTL 这种方式更有帮助,因为您实际上可以使用它来迭代结果并执行服务器端数据格式化.小服务程序:

    Returning List<Entity> as XML

    Here's an example which does effectively the same as previous example, but then with XML instead of JSON. When using JSP as XML output generator you'll see that it's less tedious to code the table and all. JSTL is this way much more helpful as you can actually use it to iterate over the results and perform server side data formatting. The 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);
    }
    

    JSP 代码(注意:如果将

    放在 中,它可能可以在非 Ajax 的其他地方重用回复):

    The JSP code (note: if you put the <table> in a <jsp:include>, it may be reusable elsewhere in a non-Ajax response):

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

    JavaScript 代码:

    The JavaScript code:

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

    您现在可能会意识到为什么在使用 Ajax 更新 HTML 文档的特定目的方面,XML 比 JSON 强大得多.JSON 很有趣,但毕竟通常只对所谓的公共网络服务"有用.JSF 等 MVC 框架在幕后使用 XML 来实现其 ajax 魔法.

    You'll by now probably realize why XML is so much more powerful than JSON for the particular purpose of updating a HTML document using Ajax. JSON is funny, but after all generally only useful for so-called "public web services". MVC frameworks like JSF use XML under the covers for their ajax magic.

    您可以使用 jQuery $.serialize() 来轻松ajaxify 现有的 POST 表单,而无需摆弄收集和传递单个表单输入参数.假设现有表单在没有 JavaScript/jQuery 的情况下也能正常工作(因此当最终用户禁用 JavaScript 时会优雅地降级):

    You can use jQuery $.serialize() to easily ajaxify existing POST forms without fiddling around with collecting and passing the individual form input parameters. Assuming an existing form which works perfectly fine without JavaScript/jQuery (and thus degrades gracefully when the end user has JavaScript disabled):

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

    您可以使用 Ajax 逐步增强它,如下所示:

    You can progressively enhance it with Ajax as below:

    $(document).on("submit", "#someform", function(event) {
        var $form = $(this);
    
        $.post($form.attr("action"), $form.serialize(), function(response) {
            // ...
        });
    
        event.preventDefault(); // Important! Prevents submitting the form.
    });
    

    您可以在servlet中区分普通请求和Ajax请求如下:

    You can in the servlet distinguish between normal requests and Ajax requests as below:

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

    jQuery 表单插件 与上面的 jQuery 示例或多或少相同,但它有根据文件上传的要求,对 multipart/form-data 表单的额外透明支持.

    The jQuery Form plugin does less or more the same as above jQuery example, but it has additional transparent support for multipart/form-data forms as required by file uploads.

    如果您根本没有表单,而只是想在后台"与 servlet 交互,请执行以下操作:如果你想发布一些数据,那么你可以使用 jQuery $.param() 可以轻松地将 JSON 对象转换为 URL 编码的查询字符串.

    If you don't have a form at all, but just wanted to interact with the servlet "in the background" whereby you'd like to POST some data, then you can use jQuery $.param() to easily convert a JSON object to an URL-encoded query string.

    var params = {
        foo: "fooValue",
        bar: "barValue",
        baz: "bazValue"
    };
    
    $.post("someservlet", $.param(params), function(response) {
        // ...
    });
    

    可以重复使用与上面显示的相同的 doPost() 方法.请注意,上述语法也适用于 jQuery 中的 $.get() 和 servlet 中的 doGet().

    The same doPost() method as shown here above can be reused. Do note that above syntax also works with $.get() in jQuery and doGet() in servlet.

    但是,如果您出于某种原因打算将 JSON 对象作为一个整体发送而不是作为单独的请求参数发送,那么您需要使用 JSON.stringify()(不是 jQuery 的一部分)和指示 jQuery 将请求内容类型设置为 application/json 而不是(默认)application/x-www-form-urlencoded.这不能通过 $.post() 便利函数完成,但需要通过 $.ajax() 完成,如下所示.

    If you however intend to send the JSON object as a whole instead of as individual request parameters for some reason, then you'd need to serialize it to a string using JSON.stringify() (not part of jQuery) and instruct jQuery to set request content type to application/json instead of (default) application/x-www-form-urlencoded. This can't be done via $.post() convenience function, but needs to be done via $.ajax() as below.

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

    请注意,许多初学者将 contentTypedataType 混合使用.contentType 表示 request 正文的类型.dataType 表示 response 主体的(预期)类型,这通常是不必要的,因为 jQuery 已经根据响应的 Content-Type 标头自动检测到它.

    Do note that a lot of starters mix contentType with dataType. The contentType represents the type of the request body. The dataType represents the (expected) type of the response body, which is usually unnecessary as jQuery already autodetects it based on response's Content-Type header.

    然后,为了处理 servlet 中的 JSON 对象,该对象不是作为单独的请求参数发送,而是作为一个完整的 JSON 字符串以上述方式发送,您只需要使用 JSON 工具手动解析请求正文,而不是使用 getParameter() 通常的方式.也就是说,servlet 不支持 application/json 格式的请求,而只支持 application/x-www-form-urlencodedmultipart/form-data> 格式化请求.Gson 还支持将 JSON 字符串解析为 JSON 对象.

    Then, in order to process the JSON object in the servlet which isn't being sent as individual request parameters but as a whole JSON string the above way, you only need to manually parse the request body using a JSON tool instead of using getParameter() the usual way. Namely, servlets don't support application/json formatted requests, but only application/x-www-form-urlencoded or multipart/form-data formatted requests. Gson also supports parsing a JSON string into a JSON object.

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

    请注意,这比仅使用 $.param() 更笨拙.通常,仅当目标服务是例如时才希望使用 JSON.stringify()一个 JAX-RS (RESTful) 服务,出于某种原因只能使用 JSON 字符串,而不能使用常规请求参数.

    Do note that this all is more clumsy than just using $.param(). Normally, you want to use JSON.stringify() only if the target service is e.g. a JAX-RS (RESTful) service which is for some reason only capable of consuming JSON strings and not regular request parameters.

    需要意识到和理解的重要一点是,servlet 对 ajax 请求的任何 sendRedirect()forward() 调用只会转发或重定向 Ajax请求本身,而不是发起 Ajax 请求的主文档/窗口.在这种情况下,JavaScript/jQuery 只会在回调函数中将重定向/转发的响应作为 responseText 变量检索.如果它代表整个 HTML 页面,而不是特定于 Ajax 的 XML 或 JSON 响应,那么您所能做的就是用它替换当前文档.

    Important to realize and understand is that any sendRedirect() and forward() call by the servlet on an ajax request would only forward or redirect the Ajax request itself and not the main document/window where the Ajax request originated. JavaScript/jQuery would in such case only retrieve the redirected/forwarded response as responseText variable in the callback function. If it represents a whole HTML page and not an Ajax-specific XML or JSON response, then all you could do is to replace the current document with it.

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

    请注意,这不会更改最终用户在浏览器地址栏中看到的 URL.所以存在可书签性的问题.因此,只返回一个指令"要好得多.让 JavaScript/jQuery 执行重定向而不是返回重定向页面的全部内容.例如,通过返回布尔值或 URL.

    Note that this doesn't change the URL as end user sees in browser's address bar. So there are issues with bookmarkability. Therefore, it's much better to just return an "instruction" for JavaScript/jQuery to perform a redirect instead of returning the whole content of the redirected page. E.g., by returning a boolean, or a 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;
        }
    
        // ...
    }
    

    另见: