ハマりメモ:IE10でFormData Objectを使った$.ajax()が失敗


ハマった事象

IE10でJQuery.ajax()のdataパラメータにformDataオブジェクトを渡したら、
リクエスト内容が壊れてしまった。
具体的には以下のようなコードを書いた(細部は変えてある)

hoge.html
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>ほげほげ</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
      function onSubmit() {
          var form = $('#hogeForm').get(0);
          var formData = new FormData(form);

          $.ajax({
            url: '/hogeAction',
            type: 'POST',
            contentType: false,
            processData: false,
            data: formData,
            dataType: 'json',
          });
          return false;
      }
    </script>
  </head>
  <body>
    <form name="hogeForm" id="hogeForm" method="post" action="/hogeAction" enctype="multipart/form-data">

    <select id="selection" name="selection">
      <option value="1">選択1</option>
      <option value="2">選択2</option>
      <option value="3">選択3</option>
    </select>
    <div>区分
      <input type="radio" name="kubun" value="1" checked="checked"><input type="radio" name="teikyouKbn" value="0"></div>
    <div>タイプ選択1
      <input type="radio" name="type" value="1" checked="checked" id="fileType1">タイプ1
      <input type="radio" name="type" value="2" id="fileType2">タイプ2
    </div>
    <input type="file" name="file" size="70" value="" style="font-size:9pt;">
    <div>タイプ選択2
      <input type="radio" name="type2" value="1" checked="checked" id="fileType1">タイプ1
      <input type="radio" name="type2" value="2" id="fileType2">タイプ2
    </div>
    <input type="button" id="submitButton" onClick="onSubmit()" name="submitButton" value="submit" />
    </form>
  </body>
</html>

submitボタンを押すと、onSubmit()メソッド内でnew FormData(form)して、
作成されたFormDataオブジェクトを$.ajax()でPOSTする。
これを動かすと、以下のように、リクエストの最後に謎のContent-Dispositionヘッダが付与されてしまい、
POST受信側でリクエストをパースできない状況になった。

Request
POST http://xxxxxxxxxx/hogeAction HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: multipart/form-data; boundary=---------------------------7de1df1690910
Referer: http://xxxxxxxxxx/
Accept-Language: ja-JP
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
Host: xxxxxxxxxxx
Content-Length: 659
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
Cookie: xxxxxxxxx

-----------------------------7de1df1690910
Content-Disposition: form-data; name="selection"

1
-----------------------------7de1df1690910
Content-Disposition: form-data; name="kubun"

1
-----------------------------7de1df1690910
Content-Disposition: form-data; name="type"

1
-----------------------------7de1df1690910
Content-Disposition: form-data; name="file"; filename=""
Content-Type: application/octet-stream


-----------------------------7de1df1690910
Content-Disposition: form-data; name="type2"

1
-----------------------------7de1df1690910
Content-Disposition: form-data; name="
-----------------------------7de1df1690910--

回避策

頑張ってググったところ、ずばりこの問題を扱っているサイトを見つけた。
http://blog.yorkxin.org/posts/2014/02/06/ajax-with-formdata-is-broken-on-ie10-ie11
上記サイトの検証によると、Formの最後にあるinput要素(button/submitを除く)が「チェックされていないラジオボタンorチェックボックス」の場合、この現象が起きるようだ。
上記サイトの例に倣って、ダミーのinput hiddenを追加する。

hoge.html
  ...
  <body>
    <form name="hogeForm" id="hogeForm" method="post" action="/hogeAction" enctype="multipart/form-data">

    <select id="selection" name="selection">
      <option value="1">選択1</option>
      <option value="2">選択2</option>
      <option value="3">選択3</option>
    </select>
    <div>区分
      <input type="radio" name="kubun" value="1" checked="checked"><input type="radio" name="teikyouKbn" value="0"></div>
    <div>タイプ選択1
      <input type="radio" name="type" value="1" checked="checked" id="fileType1">タイプ1
      <input type="radio" name="type" value="2" id="fileType2">タイプ2
    </div>
    <input type="file" name="file" size="70" value="" style="font-size:9pt;">
    <div>タイプ選択2
      <input type="radio" name="type2" value="1" checked="checked" id="fileType1">タイプ1
      <input type="radio" name="type2" value="2" id="fileType2">タイプ2
    </div>
    <input type="hidden" name="dummy" value="dummy">
    <input type="button" id="submitButton" onClick="onSubmit()" name="submitButton" value="submit" />
    </form>
  </body>
</html>

再度リクエスト内容を確認する。

Request
POST http://xxxxxxxxxx/hogeAction HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: multipart/form-data; boundary=---------------------------7de37a3190910
Referer: http://xxxxxxxxxx/
Accept-Language: ja-JP
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
Host: xxxxxxxxxxx
Content-Length: 674
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
Cookie: xxxxxxxxx

-----------------------------7de37a3190910
Content-Disposition: form-data; name="selection"

1
-----------------------------7de37a3190910
Content-Disposition: form-data; name="kubun"

1
-----------------------------7de37a3190910
Content-Disposition: form-data; name="type"

1
-----------------------------7de37a3190910
Content-Disposition: form-data; name="file"; filename=""
Content-Type: application/octet-stream


-----------------------------7de37a3190910
Content-Disposition: form-data; name="type2"

1
-----------------------------7de37a3190910
Content-Disposition: form-data; name="dummy"

dummy
-----------------------------7de37a3190910--

正しいリクエストになったようだ。やれやれ。