6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IE8 で form のデータを非同期で送信して json のレスポンスを受け取る

Last updated at Posted at 2015-11-08

form が持つデータを JavaScript から取得するための API として、 FormData というクラスが存在する。
これを利用すると、 Ajax でファイルをアップロードすることができるようになる。

ところが、この FormData は IE8, 9 では利用できない(IE10 以上なら利用できる)。

IE8 でも非同期で form のデータを送信したい場合は、 <iframe> タグを利用した(若干トリッキーな)方法が存在する。

環境

Web ブラウザ

IE11 (開発者モードで IE8 をエミュレートさせて確認)

AP サーバー

Payara 4.1.154

Java

JDK 1.8.0_60

仕組み的な

ie8_ajax_form.JPG

  • <form> タグに target 属性を設定する。
  • target には、同じ <html> 内に存在する <iframe>name を指定する。
  • <iframe> タグは、 display: none; などを使って見えないようにしておく。
  • <form> をサブミットすると、リクエストの結果は target で指定した <iframe> に出力される。
  • このため、画面遷移は起こらず Ajax で非同期通信したかのような動作になる。

実装的な

クライアント

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>sample</title>
    <style>
      .invisible {
        display: none;
      }
    </style>
  </head>
  <body>
    <form id="form" action="sample-servlet" method="POST" target="_frame">
      <input type="text" name="text" />
      <input type="submit" value="Submit" />
      
      <iframe id="frame" name="_frame" class="invisible"></iframe>
    </form>
    
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>
script.js
$(function() {
    $('#frame').on('load', function() {
        var json = $(this).contents().text();
        var response = $.parseJSON(json);
        alert('foo=' + response.foo + ', bar=' + response.bar);
    });
});

サーバー

SampleServlet.java
package jta_sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/sample-servlet")
public class SampleServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try (BufferedReader br = req.getReader();) {
            br.lines().forEach(System.out::println);
        }
        
        // ★ Content-Type に text/plain を指定
        resp.setHeader("Content-Type", "text/plain");
        
        try (PrintWriter pw = resp.getWriter();) {
            pw.println("{\"foo\": \"xxx\", \"bar\": true}");
        }
    }
}

動作確認

ie8_ajax_form.JPG

開発者モードで IE8 にして実行。

↓サブミット

ie8_ajax_form.JPG

サーバー側出力

2015-11-08T23:18:16.533+0900|情報: text=abcdefg

説明

レスポンスの受け取りには

script.js
    $('#frame').on('load', function() {
        var json = $(this).contents().text();
        var response = $.parseJSON(json);
        alert('foo=' + response.foo + ', bar=' + response.bar);
    });
  • <iframe>load イベントを監視することで、サーバーからのレスポンスを受け取れるようになる。
  • ステータスコードとかは参照できないので、レスポンスに成功かエラーか分かる識別子を渡す必要がありそう。

コンテンツタイプに text/plain を指定する

SampleServlet.java
    resp.setHeader("Content-Type", "text/plain");

json を返すんだから application/json にしたいところだが、そうすると IE はファイルのダウンロードを開始してしまう。

Content-Type を application/json にした場合

ie8_ajax_form.JPG

気持ち悪いけど、 text/plain にしてあげる必要があるっぽい。
(JAX-RS を使っていると、 JSON 変換のための MessageBodyWriter と連携するためにメソッドを @Produces(MediaType.APPLICATION_JSON) でアノテートしたくなるけど、それするとファイルダウンロードになってしまうので歯がゆい)

参考

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?