WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

jQuery Ajax で xml 形式のデータ受け取り

by WebSurfer 2012年7月17日 21:37

jQuery Ajax で Web サービスを呼び出し、xml 形式のデータを受け取る場合の話です。

まず、Web サービスのメソッドの戻り値の型の形式として XML を指定するときは、ScriptMethodAttribute 属性 を追加し、ResponseFormat プロパティ を Xml に設定します。さらに、メソッドが XmlDocument オブジェクト を返すようにします。以下のような感じです。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlDocument GetXmlDocument()
{
    // Xml 形式の文字列を組み立て。
    string _xmlString = CreateXmlString();

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(_xmlString);
    return xmlDoc;
}

こうしておくと、XmlDocument オブジェクトはサーバーで Xml 形式の文字列にシリアル化され、このメソッドを呼んだクライアントに返されます。また、応答ヘッダには Content-Type: text/xml が含まれるようになります。

ScriptMethod 属性の ResponseFormat プロパティを Xml に設定しておかない場合、Web サービスのメソッドが返すデータの形式は、クライアントからの要求ヘッダの Content-Type の指定によるようです。

クライアントからサーバーへデータを送信する場合、要求ヘッダの Content-Type は、application/x-www-form-urlencoded または application/json のいずれかになるはずです(データを送信しない場合、Content-Type を指定しないこともありますが)。

前者は普通に form データをサーバーに POST する時のデータ形式、後者は Ajax でのデータ交換フォーマットのデファクトスタンダード(?)である JSON 形式です。

要求ヘッダの Content-Type が application/json となっている場合、ScriptMethod 属性の ResponseFormat プロパティを Xml に設定しておかないと、戻り値が Xml 形式にならない(といって、正しい Json 形式にもならない)ので注意してください。

要求ヘッダの Content-Type を設定しない、または application/x-www-form-urlencoded とした場合は、ResponseFormat プロパティの設定に関係なく、戻り値は Xml 形式になります。(ResponseFormat プロパティを Json 設定しても無視されて、戻り値は Xml 形式になります。)

クライアントで Web サービスからのデータを受け取るのは jQuery Ajax の success ハンドラです。jQuery Ajax は、サーバーからの応答ヘッダに含まれる Contetnt-Type を見て success ハンドラに渡すデータの形式を決めます。

jQuery API の解説ページ jQuery.ajax() に次の説明があります。

"The text and xml types return the data with no processing. The data is simply passed on to the success handler, either through the responseText or responseXML property of the jqXHR object, respectively."

上のサンプルに書いたコードの場合、Web サービスからの応答ヘッダに Contente-Type: text/xml が含まれますので、success ハンドラに渡される data は jqXHR.responseXML から取得されます。

jqXHR.responseXML は何かというと、MSDN ライブラリの responseXML property に書いてあるように、XML DOM オブジェクト(ただのシリアル化された文字列ではなく)になります。

シリアル化された文字列を表示したい場合は、IE であれば xml プロパティが使えます。詳しくは、MSDN ライブラリの IXMLDOMDocument/DOMDocument Members を参照してください。

ただし、xml プロパティは IE 専用なので、Mozilla 系のブラウザの場合は以下のように XMLSerializer を使用できます。

function getXmlDocument() {
  $.post("163-ScriptMethodAttribute.asmx/GetXmlDocument",
    function (data, textStatus, jqXHR) {
      $('#output').empty();
      $('#output').text(data.xml || 
        (new XMLSerializer()).serializeToString(data));
    }
  );
}

または、jqXHR.responseText から取得することも可能です。

Tags: , ,

AJAX

jQuery.ajax で form データを送信

by WebSurfer 2012年7月14日 16:43

jQuery.ajax で form データを送信する方法です。ちょっとハマったので忘れないように書いておきます。

Fiddler によるキャプチャ画面

例えば、"日本語" と "URL エンコードされるか?" という文字列を送信する場合は以下のようにします。

data: 'aaa=' + '<%=Server.UrlEncode("日本語")%>' + 
    '&bbb=' + '<%=Server.UrlEncode("URL エンコードされるか?")%>'

または、

data: { aaa: '日本語', bbb: 'URL エンコードされるか?' }

後者は、"日本語" および "URL エンコードされるか?" という文字列を自動的に UrlEncode して、前者と同様な form データを作って送信してくれるので、こちらの方がお勧めです。

上の画像は、以下の jQuery Ajax のコードでデータを送信した時、送信データを Fiddler でキャプチャしたものです。URL エンコードされているのが分かります。

$.post("152-jQueryAjaxXml.asmx/GetXml",
    { aaa: '日本語', bbb: 'URL エンコードされるか?' },
    function (data, textStatus, jqXHR) {
        $('#output').text(data.xml);
    }
);

Tags: ,

AJAX

jQuery AJAX と Web サービス

by WebSurfer 2011年6月4日 21:05

注意:
今頃になって何ですが、 jQuery Ajax の仕様が変わって、バージョン 1.5 以降では dataFilter に指定したメソッドで JSON 文字列を JavaScript オブジェクトにパースできなくなりました。詳しい説明はこの記事の下の方にある「2013/8/9 追記」を見てください。

先の記事 ASP.NET AJAX と Web サービス では、AJAX 対応 ASP.NET Web ページから Web サービスにアクセスする例を書きましたが、この記事では jQuery を使って AJAX 通信を行う例を書きます。

ASP.NET .aspx ファイルでない Web ページでは、ScriptManager コントロールを使用できません。従って、先の記事に書きましたプロキシクラスは利用できません。

この記事では、静的な html ページから、jQuery を使った AJAX 通信を行い、Web サービスからデータを JSON 形式で取得する方法について説明します。

基本的には、先の記事で紹介したページ Handling JSON Arrays ... に記載されていたサンプルとほぼ同じです。

ただし、d パラメータ有り(.NET 3.5 以降)/無し(.NET 2.0 以前)の両方に対応するためのコードと、ブラウザに実装されているネイティブの JSON パーサーを使えるように手を加えました。

それらの機能を追加しても、先の記事で書いた ScriptManager を利用してプロキシを自動生成する場合と同等以上に簡単と思いますがいかがでしょう。マイクロソフトが jQuery を Visual Studio 2010 に統合した理由が分かるような気がします。

以下のコードは、実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>jQUery AJAX and Web Service</title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
  function getCars() {
    $.ajax({
      type: "POST",
      url: "097_jQueryAjaxAndWebService.asmx/GetCarsByDoors",
      data: '{"doors":' + $("#ddlDoors").val() + '}',

      // JSON を期待する場合必須(下の追記参照)
      contentType: "application/json; charset=utf-8",

      // success ハンドラで d の有無を判定しないで済むように
      // dataFilter を使う。
      // その際、dataType: "json" は不用。
      // dataType は応答データをどのように deserialize する
      // かを jQuery に指示するもの。"json" が指定されてい
      // ると jQuery は内部的に eval メソッドを使って 
      // deserialize する。dataType: "json" が指定してある
      // と  dataFilter と二重に deserialize されてしまう。

      dataFilter: function (data) {
        // ブラウザの native JSON パーサがあればそれを
        // 使う(セキュリティ対策)  
        var msg = "";            
        if (typeof (JSON) !== 'undefined' &&
          typeof (JSON.parse) === 'function') {
          msg = JSON.parse(data);
        } else {
          msg = eval('(' + data + ')');
        }

        // .NET 3.5 で追加された d パラメータの処置。
        if (msg.hasOwnProperty('d')) {
          return msg.d;
        } else {
          return msg;
        }
      },
      success: function (cars) {
        $('#output').empty();
        $.each(cars, function (index, car) {
          $('#output').append(
            '<p><strong>' + car.Make + ' ' +
            car.Model + '</strong><br /> Year: ' +
            car.Year + '<br />Doors: ' +
            car.Doors + '<br />Colour: ' +
            car.Colour + '<br />Price: £' +
            car.Price + '</p>');
          });
        },
        // failure を error に訂正。下の追記参照。
        // failure: function (msg) {
        //   $('#output').text(msg);
        // }
        error: function (jqXHR, textStatus, errorThrown) {
          $('#output').text('textStatus: ' + textStatus + 
            ', errorThrown: ' + errorThrown);
        }
    });
  }
  //]]>
  </script>
</head>
<body>
  <div>
    Number of doors: 
    <select name="ddlDoors" id="ddlDoors">
	  <option value="2">2</option>
	  <option value="3">3</option>
	  <option value="4">4</option>
	  <option value="5">5</option>
    </select>   
  </div>
  <input type="button" 
    id="Button1" 
    value="Get Cars" 
    onclick="getCars();" /> 
  <div id="output"></div>
</body>
</html>

------ 2011/9/3 追記 ------

上記コードの $.ajax のオプション設定の中の failure は、参考にしたサイト Handling JSON Arrays returned from ASP.NET Web Services with jQuery のコードをコピペしたものですが、どうも間違っているようです。

jQuery の本家のサイトの jQuery.ajax() の説明を見ると、オプション設定の中に failure というのはなく、error が正しいはずです。実際に試してみましたが、失敗しても制御は failure に設定した function には飛ばず、error に設定した function に飛びました。

という訳で、訂正しました。やはり、何も考えずにコピペして、しかも検証もしないというのはダメですね。(汗)

----- 2012/7/16 追記(2014/3/24 一部訂正) -----

jQuery Ajax で contentType: "application/json; charset=utf-8" を設定しないと、Web サービスのメソッドから返されるデータは xml 形式になってしまいます。結果、パースエラーになり error に設定したメソッドに制御が飛びます。

jQuery のサイトの API Documentation の説明によると、jQuery.ajax() の contentType のデフォルトは 'application/x-www-form-urlencoded; charset=UTF-8' とのことですが、何も設定しないと要求ヘッダには Content-Type: そのものが設定されません(1.4.1 でしか試してませんが)。その場合でも、Web サービスのメソッドが返すデータ合は Xml 形式になります。

なお、要求ヘッダが Content-Type: application/x-www-form-urlencoded もしくは何も指定しない場合は ScriptMethodAttribute クラスResponseFormat プロパティ を Json に指定しても無視されます。

jQuery.post() や jQuery.get() では contentType が指定できないので注意してください。

----- 2012/12/23 追記 -----

POST する JSON 文字列が {doors:5} となっており、正しく {"doors":5} となってなかったので以下のように訂正しました。

data: "{doors: " + $('#ddlDoors').val() + "}",
 ↓↓↓
data: '{"doors":' + $("#ddlDoors").val() + '}',

以前の形式でも問題なく Web サービスのメソッドは呼び出され、応答は正しく帰ってきましたが、気分の問題ということで直しました。(笑)

------ 2013/8/9 追記 ------

jQuery バージョン 1.4(多分それ以前のバージョンも)では、dataFilter のメソッドの戻り値が string 型以外のときは、そのまま結果を success のメソッドの引数に渡します(string 型の時に限り戻り値をパースしてから success のメソッドに渡します)。

なので、上のサンプルコードのように、dataFilter のメソッドで JSON をパースして JavaScript オブジェクトに変換すると、string 型ではないので、JavaScript オブジェクトがそのまま success のメソッドの引数 cars に渡され、問題なく結果が表示されます。

ところが、バージョン 1.5 以降では、上のサンプルコードのように dataFilter のメソッドで JSON 文字列を JavaScript オブジェクトにパースすると、 success のメソッドの引数 cars に渡されるのは null になり、結果スクリプトエラーとなってしまいます。その理由は以下の通りです。

  1. サーバーからの応答の Content-Type が application/json の場合、dataFilter の結果を parseJSON: function(data){...} で定義されているメソッドの引数 data に渡す。
  2. parseJSON: function(data){...} メソッドは、引数として受け取った data をパースして JavaScript オブジェクトを戻り地として返すようになっているが、typeof data !== "string" の場合は、パース等何もしないで null を返す。
  3. dataFilter のメソッドで JSON 文字列をパースして JavaScript オブジェクトに変換してしまうと、data は string 型ではないので null が返される。
  4. 結果、success のメソッドの引数 cars に null が渡され、スクリプトエラーとなる。

従って、バージョン 1.5 以降では、パースは jQuery に任せて(dataFilter のメソッドは記述しないで)、d パラメータの処置のみを success のメソッドで行うことになります。上記のサンプルコードの function getCars() は以下のように修正してください。

function getCars() {
  $.ajax({
    type: "POST",

    url: "097_jQueryAjaxAndWebService.asmx/GetCarsByDoors",

    data: '{"doors":' + $("#ddlDoors").val() + '}',

    contentType: "application/json; charset=utf-8",

    success: function (cars) {
      if (cars.hasOwnProperty('d')) {
          cars = cars.d;
      }
      $('#output').empty();
      $.each(cars, function (index, car) {
        $('#output').append(
          '<p><strong>' + car.Make + ' ' +
          car.Model + '</strong><br /> Year: ' +
          car.Year + '<br />Doors: ' +
          car.Doors + '<br />Colour: ' +
          car.Colour + '<br />Price: £' +
          car.Price + '</p>');
        });
      },

      error: function (jqXHR, textStatus, errorThrown) {
        $('#output').text('textStatus: ' + textStatus + 
          ', errorThrown: ' + errorThrown);
      }
  });
}

Tags: ,

AJAX

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar