WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

jQuery AJAX と Web サービス

by WebSurfer 4. June 2011 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月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  November 2019  >>
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar