1. ホーム
  2. java

[解決済み] サーブレットとAjaxはどのように使い分ければよいのですか?

2022-03-16 21:45:10

質問

サーブレット内で何かを印刷し、それをウェブブラウザで呼び出すと、必ずそのテキストを含む新しいページが返されます。Ajaxを使用して、現在のページにテキストを印刷する方法はありますか?

<サブ 私はウェブアプリケーションとサーブレットについて非常に初心者です。

どのように解決するのですか?

確かに、キーワードは「Ajax」ですね。 非同期JavaScriptおよびXML . しかし、昨年はもっと頻繁に 非同期JavaScriptとJSON . 基本的には、JavaScriptに非同期のHTTPリクエストを実行させ、そのレスポンスデータに基づいてHTMLのDOMツリーを更新します。

かなり を動作させるのは面倒な作業です。 すべてのブラウザ (特に Internet Explorer とそれ以外) にまたがって、これを単一の関数で単純化し、ブラウザ固有のバグやクラックを可能な限りカバーする JavaScript ライブラリがたくさん出ています。 jQuery , プロトタイプ , ムートゥール . 最近はjQueryが人気なので、以下の例ではjQueryを使っています。

キックオフのリターン例 String プレーンテキストとして

を作成します。 /some.jsp 以下のように(注:この回答のコードスニペットは、JSPファイルがサブフォルダに配置されることを想定していません。 "someservlet" から "${pageContext.request.contextPath}/someservlet" 簡潔にするため、このコードでは省略しています)。

<!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() メソッドは次のようなものです。

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

のURLパターンでこのサーブレットをマッピングします。 /someservlet または /someservlet/* を変更する必要があります。 someservlet のURLは、それに応じて、すべての場所のJSコードの例で)。

package com.example;

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

また、その際に サーブレット 3.0 互換コンテナ ( トムキャット 7, GlassFish 3, JBoss AS 6 などの新しいバージョン) にマッピングし、それを web.xml は、昔ながらの方法です。 サーブレットウィキのページ ):

<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の内容が更新されるのがわかると思います。

戻る List<String> JSONとして

JSON レスポンスフォーマットとしてプレーンテキストではなく、さらに一歩進んだものを使用することができます。よりダイナミックな動きが可能になるのです。まず、JavaオブジェクトとJSON文字列の間で変換するツールが欲しいところです。それらもたくさんあります( このページ をご覧ください。) 個人的に気に入っているのは グーグルGson . ダウンロードし、そのJARファイルを /WEB-INF/lib フォルダを作成します。

を表示する例です。 List<String> として <ul><li> . サーブレットが

@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のコードです。

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

jQuery は自動的にレスポンスを JSON として解析し、直接 JSON オブジェクト ( responseJson を指定した場合は、関数の引数として application/json . この設定を忘れたり、デフォルトの text/plain または text/html を指定した場合、その後に responseJson の引数はJSONオブジェクトではなく、単なる文字列を与えるので、手動で JSON.parse() これは、コンテンツタイプを最初に正しく設定すれば、全く必要ないことです。

戻る Map<String, String> JSONとして

もう一つの例は、次のように表示します。 Map<String, String> として <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>

返品について List<Entity> JSONとして

を表示する例です。 List<Product> の中に <table> ここで Product クラスは、プロパティ Long id , String nameBigDecimal price . サーブレットが

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

返品について List<Entity> XMLとして

前の例と実質的に同じですが、JSONの代わりにXMLを使用した例を示します。JSPをXML出力ジェネレータとして使用する場合、テーブルやその他のコードを書くのがそれほど面倒でないことがわかるでしょう。JSTL を使えば、結果を繰り返し表示したり、サーバサイドでデータ整形を行ったりすることができるので、より便利です。サーブレットが

@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のコード(注意:もしあなたが <table> の中に <jsp:include> しかし、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>

JavaScriptのコードです。

$(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は面白いですが、結局のところ、一般的にいわゆる「公開Webサービス」に対してのみ有用です。MVCフレームワークのような JSF は、AjaxマジックのためにXMLを隠蔽して使用しています。

既存のフォームをAjax化する

jQueryを使用することができます。 $.serialize() を使えば、個々のフォーム入力パラメータの収集や受け渡しに煩わされることなく、既存のPOSTフォームを簡単にajax化することができます。JavaScript/jQuery なしで完全に動作する既存のフォームを想定しています (したがって、エンドユーザーが 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>

以下のようにAjaxで段階的に拡張することができます。

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

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

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

サーブレットでは、通常のリクエストとAjaxのリクエストを以下のように区別することができます。

@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 Form プラグイン は、上記の jQuery の例とほぼ同じですが、この例ではさらに multipart/form-data フォームを使用することができます。

サーブレットにリクエストパラメータを手動で送信する

フォームがなく、サーブレットとバックグラウンドで対話し、データを POST したい場合は、jQuery を使用することができます。 $.param() を使用すると、JSONオブジェクトをURLエンコードされたクエリ文字列に簡単に変換することができます。

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

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

同じく doPost() メソッドを再利用することができます。上記の構文は $.get() はjQueryで doGet() をサーブレットで使用します。

サーブレットにJSONオブジェクトを手動で送信する

しかし、何らかの理由で個々のリクエストパラメータとしてではなく、全体としてJSONオブジェクトを送信するつもりであるなら、それを JSON.stringify() (jQueryの一部ではありません)、jQueryにリクエストのcontent typeを application/json ではなく、(デフォルトの) application/x-www-form-urlencoded . これは $.post() コンビニエンス関数で行う必要があります。 $.ajax() を以下のように設定します。

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 の種類を表します。 リクエスト のボディを作成します。は、その dataType の(予想される)タイプを表します。 応答 ボディを自動検出するため、通常は不要です。 Content-Type ヘッダを作成します。

そして、個々のリクエストパラメータとしてではなく、上記の方法で全体のJSON文字列として送信されたサーブレット内のJSONオブジェクトを処理するためには、JSONツールを使ってリクエストボディを手動でパースするだけでよく、その代わりに getParameter() は、通常の方法です。すなわち、サーブレットは application/json 形式のリクエストにのみ対応し application/x-www-form-urlencoded または multipart/form-data 形式のリクエストに対応します。Gson はまた、JSON 文字列を 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();
// ...

を使用するよりも、より不格好になることに注意してください。 $.param() . 通常は JSON.stringify() は、ターゲットサービスが JAX-RS (RESTful) サービスであり、何らかの理由で通常のリクエストパラメータではなく JSON 文字列のみを消費することができる場合にのみ使用します。

サーブレットからリダイレクトを送信する

重要なことは、すべての sendRedirect()forward() は、ajax リクエストに対してサーブレットが呼び出すと、転送またはリダイレクトされるだけです。 Ajaxリクエストそのもの であり、Ajaxリクエストが発生したメインドキュメント/ウィンドウではありません。JavaScript/jQueryはこのような場合、リダイレクトされた/転送されたレスポンスを responseText をコールバック関数内で使用することができます。もしそれがAjax特有のXMLやJSONレスポンスではなく、HTMLページ全体を表しているなら、あなたができることは現在のドキュメントをそれに置き換えることだけです。

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

これは、エンドユーザーがブラウザのアドレスバーに表示されるURLを変更するものではないことに注意してください。そのため、ブックマーク性に問題がある。したがって、リダイレクト先のページの内容をすべて返すのではなく、JavaScript/jQueryにリダイレクトを実行させるための命令("instruction")を返すだけの方がずっとよいでしょう。例えば、ブール値を返すとか、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;
    }

    // ...
}

こちらもご覧ください。