WebSurfer's Home

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

Ajax Control Toolkit デモ

by WebSurfer 2017年12月16日 20:25

Ajax Control Toolkit の最新版(この記事を書いた時点では v17.1.1.0)のデモを自分の開発環境で動くようにする手順を備忘録として書いておきます。

Ajax Control Toolkit デモ

DevExpress のサイト ASP.NET AJAX Control Toolkit Demos にデモは公開されていますが、自分の開発環境に同じものをインストールし、ソースコードを見たりデバッグ実行したいということがあると思います。

そのためには、まず、GitHub のサイト DevExpress/AjaxControlToolkit から AjaxControlToolkit-master.zip をダウンロードします。

ダウンロードしたファイル AjaxControlToolkit-master.zip の中に AjaxControlToolkit.SampleSite というフォルダがあるので、それを丸ごと解凍して適当な場所にコピーします。

v7.x までは、それを Visual Studio で開いて[デバッグ(D)]⇒[デバッグなしで開始(H)]で実行すれば動いたのですが、v17.1.1.0 では AjaxControlToolkit が見つからないというエラーになります。

基本的には NuGet で必要なパッケージをインストールして web.config を一部修正すれば動くようになるのですが、かなり多くのパッケージをインストールしなければならず、エラーメッセージも意味不明なものがあり、動くようになるまで 2 日ほどハマってしまいました。(笑)

NuGet

自分が NuGet からインストールしたパッケージおよびそのバージョンは以下の通りです。

  1. AjaxControlToolkit v17.1.1
  2. Microsoft.AspNet.Web.Optimization v1.1.3
  3. Microsoft.AspNet.Web.Optimization.WebForms v1.1.3
  4. AjaxControlToolkit.StaticResources v17.1.1
  5. WebGrease v1.6.0
  6. AjaxControlToolkit.HtmlEditor.Sanitizer v17.1.1
  7. HtmlAgilityPack v1.6.7
  8. Antlr v3.5.0.2
  9. Newtonsoft.Json v10.0.3
  10. Microsoft.Web.Infrastructure v1.0.0

web.config の修正箇所は以下の通りです。

  1. <trust level="Medium" /> を削除(理由不明ですが Visual Studio 2015 で IIS Express 64-bt で動かした場合はこれがあるとダメ)
  2. siteMap / providers 要素に <remove name="MySqlSiteMapProvider" /> を追加(これは MySQL の Connector/NET をインストールした自分の環境固有の問題)

上に書いた NuGet でのパッケージのインストールが完了するとアプリケーションの bin フォルダは以下のようになるはずです。

bin フォルダ

以下に自分的に注意すべきと思う点を書いておきます。

WebGrease, HtmlAgilityPack など[ソリューションの NuGet パッケージの管理]ではインストール済みと表示されるものがあります。その場合、新しいバージョンがリリースされていたら更新することでインストールできます。

Microsoft.Web.Infrastructure は新しいバージョンがない(1.0.0 しかない)ので[ソリューションの NuGet パッケージの管理]ではインストールできません。その場合は[パッケージマネージャーコンソール(O)]で以下のようにしてインストールできます。

Update-Package -reinstall Microsoft.Web.Infrastructure

HtmlEditorEntender には AjaxControlToolkit.HtmlEditor.Sanitizer が必要です。これなしで起動しようとすると以下のように "値を Null にすることはできません。パラメータ名:Type" という意味不明なエラーになります。これの原因が分からなくてハマりました。(汗)

エラーメッセージ

加えて、HtmlAgilityPack も必要です。もういい加減にカンベンしてって感じだったのですが、一応ここまででとりあえず動くようにはなりました。(笑)

Tags: ,

AJAX

日付時刻と JSON 文字列

by WebSurfer 2017年8月27日 15:01

JavaScript の Date オブジェクトや .NET Framework の DataTime オブジェクトの JSON 文字列への変換について調べましたので備忘録として書いておきます。

ASP.NET ajax によるパース結果

JSON の紹介よると、その記事に書いてある value に直接設定できるのは string, number, object, array, true, false, null だけということですので、JavaScript の Date オブジェクトや .NET Framework の DataTime オブジェクトは string に変換して JSON 文字列に設定せざるを得ません。

変換は自力で行わなくても、例えば、JavaScript なら JSON.stringify() メソッド、.NET Framework なら DataContractJsonSerializer クラスを利用すれば自動的に変換してくれます。

その他、JavaScriptSerializer クラス、ASP.NET Web サービス、WCF、ASP.NET Web API でも DateTime 型を自動的に文字列に変換してくれますが、それぞれどのような結果になるかを以下にまとめておきます。

No. 方法 結果
(1) JSON.stringify() 2017-02-01T03:15:45.000Z
(2) DataContractJsonSerializer \/Date(1503727168573+0900)\/
(3) WCF 上記 (2) の結果と同じ
(4) JavaScriptSerializer \/Date(1030806000000)\/
(5) ASP.NET Web サービス 上記 (4) の結果と同じ
(6) ASP.NET Web API 2017-08-26T15:39:32.6330349+09:00

以下に上記 (1) ~ (6) のそれぞれの検証方法や備忘録として残しておいた方がよさそうな情報を書いておきます。

(1) JSON.stringify() メソッド

ECMAScript 2017 Language Specification (ECMA-262, 8th edition, June 2017) の 20.3.1.16 Date Time String Format のセクションによると、ISO 8601 Extended Format 即ち:

YYYY-MM-DDTHH:mm:ss.sssZ

という形式に変換することになっているそうです。

全てのブラウザで確認したわけではありませんが、IE11, Chrome 60.0.3112.113, Firefox 55.0.3 では確かにその通りになるのを確認しました。

なお、JavaScript の Data オブジェクトはローカルタイムとして扱われまずが、JSON.stringify() メソッドで文字列に変換すると UTC になるので注意してください(YYYY-MM-DDTHH:mm:ss.sssZ の末尾の Z は UTC であることを意味します)。

例えば、以下のコードを実行すると、

var dateTime = new Date(2017, 1, 1, 12, 15, 45);
var dateObject = { DateTime: dateTime };
var dateJson = JSON.stringify(dateObject);

dateJson は {"DateTime":"2017-02-01T03:15:45.000Z"} という JSON 文字列になります。

Date コンストラクタの引数で月を表す整数値は 0 (1月) から 11 (12月) なので、1 ⇒ 02 という結果になっています。時間 12 が 03 になるのは東京ローカルタイムが UTC に変換されるためです。

(2) DataContractJsonSerializer クラス

DataContractJsonSerializer クラスを使うと、別の記事「w2ui Grid」に書きましたように、.NET Framework の DateTime 型は:

\/Date(1503727168573+0900)\/

のような文字列に変換されます。

この例の最初の数字 1503727168573 は UTC 1970 年 1 月 1 日午前 0 時からカウントしたミリ秒、+0900 はオプションで、ローカルタイムの UTC からの時差だそうです。

詳しくは以下の MSDN ライブラリの記事「スタンドアロン JSON のシリアル化」の「高度な情報 / DateTime ワイヤ形式」のセクションの説明を見てください。

(3) WCF

先の記事「WCF と jQuery AJAX」の CarService.svc.cs のコードに以下のメソッドを追加して試してみました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

namespace WebApplication1
{
    // web.config で aspNetCompatibilityEnabled="true" と設定され
    // ているので [AspNetCompatibilityRequirements(...)] が必要
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = 
        AspNetCompatibilityRequirementsMode.Allowed)]
    public class CarService
    {
        // ・・・中略・・・

        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Wrapped)]
        public DateTimeJsonTest GetToday()
        {
            DateTimeJsonTest today = new DateTimeJsonTest();
            today.Today = DateTime.Now;
            return today;
        }
    }

    // ・・・中略・・・

    [DataContract]
    public class DateTimeJsonTest
    {
        [DataMember]
        public DateTime Today { set; get; }
    }
}

ブラウザから上記コードの GetToday を要求すると以下のような JSON 文字列が返されます:

{"GetTodayResult":{"Today":"\/Date(1503727168573+0900)\/"}}

すなわち、上記「(2) DataContractJsonSerializer クラス」の場合と同じになります。(内部的に DataContractJsonSerializer クラスを使って変換しているのであろうと思います。未確認ですが・・・)

(4) JavaScriptSerializer クラス

ASP.NET MVC の Controller クラスの Json メソッドの内部で .NET Framework のオブジェクトを JSON 文字列にシリアライズするために JavaScriptSerializer クラスが用いられているそうです。

先の記事「jQuery.ajax で JSONP」に JavaScriptSerializer クラスを使ってシリアル化を行っている例がありますので、これに少し手を加えて検証してみました。

その記事のコードを見てください。Controller の Jsonp アクションメソッドでは Address テーブルから AddressID, AddressLine1, City フィールドのデータを取得して匿名クラスのオブジェクトを作り、それを JavaScriptSerializer クラスの Serialize メソッドで JSON 文字列にシリアライズしています。

Address テーブルには ModifiedDate という datetime 型のフィールドがありますのでそれを匿名クラス address に追加してシリアライズしてみました。

Serialize(address) の結果は以下のような文字列となります。

{"AddressID":25,"AddressLine1":"9178 Jumping St.","City":"Dallas","ModifiedDate":"\/Date(1030806000000)\/"}

即ち、DateTime 型の変換結果は上記「(2) DataContractJsonSerializer クラス」とほぼ同じで、オプションのローカルタイムの UTC からの時差(上の例では +0900)が省略された形になっています。

(5) ASP.NET Web サービス

先の記事「ASP.NET AJAX と Web サービス」のコードに以下のメソッドを追加して試してみました。

<%@ WebService Language="C#" Class="CarService" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;
using System.Collections.Generic;
using System.Linq;

// ・・・中略・・・

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class CarService : WebService
{
    // ・・・中略・・・

    [WebMethod]
    public DateTimeJsonTest GetToday()
    {
        DateTimeJsonTest today = new DateTimeJsonTest();
        today.Today = DateTime.Now;
        return today;
    }
}

public class DateTimeJsonTest
{
    public DateTime Today { set; get; }
}

上記 GetToday() メソッドを、要求ヘッダに Content-Type: application/json を追加して POST 要求すると(注)以下のような JSON 文字列が返されます:

{"d":{"__type":"DateTimeJsonTest","Today":"\/Date(1503712714770)\/"}}

(注)ASP.NET Web サービスの制約上 POST 要求しないとエラーになります。Content-Type を指定しない、または application/x-www-form-urlencoded とすると XML 形式で返されます。XML の場合は形式は上記とは異なり 2017-08-26T10:36:27.0809765+09:00 のようになります。

即ち、上記「(4) JavaScriptSerializer クラス」と同じ形式の文字列に変換されます(+0900 も同じく省略されています)。

ASP.NET AJAX を利用すると、\/Date(1503712714770)\/ という形式の文字列は、Date 型の JavaScript オブジェクトに自動的に変換されます。具体例は以下の通りです。

記事「ASP.NET AJAX と Web サービス」の「(2) JavaScript (097_ASPNETAjaxAndWebService.js)」のセクションに記載されているコードを以下のように書き換えます。

var serviceProxy;

// プロキシの初期化とコールバック関数の設定
function pageLoad() {
    serviceProxy = new CarService();
    serviceProxy.set_defaultSucceededCallback(Succeeded);
    serviceProxy.set_defaultFailedCallback(Failed);
}

// ボタンクリックで呼び出されるサービスメソッド  
function getToday() {
    serviceProxy.GetToday();
}

// AJAX 通信が成功したときに呼び出され、戻ってきたデータ
// を処置するコールバック関数。
// 引数 data は JSON 文字列ではなく、DateTimeJsonTest 型
// オブジェクト。それから Today プロパティで取得できるの
// は、文字列ではなく Date 型に変換済みのオブジェクト。
function Succeeded(data) {
    $('#output').empty();
    var now = data.Today;  // now は Date 型オブジェクト
    $('#output').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
    );
}

// ・・・以下略・・・

上の getToday() メソッドを「(3) apsx ページ (097_ASPNETAjaxAndWebService.aspx) 」のセクションに記載されているコードと同等な aspx ページから呼び出すと、Date 型オブジェクトが上記コードの now に取得でき、この記事の一番上の画像のとおり表示されます。

MSDN ライブラリの記事「スタンドアロン JSON のシリアル化」の「高度な情報 / DateTime ワイヤ形式」のセクションの説明によると、"UTC からの時差を示す部分(例: +0900)が指定されていない場合、時刻は UTC として逆シリアル化されます" とありますが、上記コードの結果はローカル時刻になります。

ASP.NET AJAX に代えて jQuery ajax を使う場合は自動的に Date オブジェクトには変換してくれませんので、自力でコードを書いて変換することになります。

具体例は以下の通りです。数字の部分を抜き出して JavaScript の Date コンストラクタの引数として渡し、Date オブジェクトを生成すれば ASP.NET AJAX と同じ結果が得られます。

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

    url: "097_jQueryAjaxAndWebService.asmx/GetToday",

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

    success: function (data) {
      if (data.hasOwnProperty('d')) {
          data = data.d;
      }
      $('#output').empty();
      var re = /^\/Date\(([0-9]+)\)\/$/;
      var results = re.exec(data.Today);
      var now = new Date(Number(results[1]));
      $('#output').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
      );
    },

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

(6) ASP.NET Web API

Visual Studio 2010 Professional のテンプレートを使って自動生成される ASP.NET Web API アプリのコードに、以下のメソッドを追加して試してみました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebApi1.Models;

namespace WebApi1.Controllers
{
    public class HeroesController : ApiController
    {
        public DateTimeJsonTest Get()
        {
            DateTimeJsonTest today = new DateTimeJsonTest();
            today.Today = DateTime.Now;
            return today;
        }

        // ・・・中略・・・
    }
}

jQuery ajax を利用して上の Get() メソッドを呼び出すと以下のような JSON 文字列が返されます:

{"Today":"2017-08-26T15:39:32.6330349+09:00"}

DateTime 型の文字列への変換は上の「(5) ASP.NET Web サービス」の場合の XML 形式の応答の場合と同じです。

これは ASP.NET Web API 独自実装なのか、ISO 8601 などに準拠しているのかは調べ切れてません。

なお、この形式で、JavaScript の Date 型へのパースは IE11, Chrome 60.0.3112.113, Firefox 55.0.3 のいずれも可能です。例えば、jQuery ajax を使うと、success オプションに設定したコールバックの引数に上の JSON 文字列を JavaScript オブジェクトに変換して代入してくれますので、それから以下のようにして Date オブジェクトにパースできます。

success: function (data, textStatus, jqXHR) {
    var now = new Date(data.Today);
    $('#heroes').append(
        "<p>" + now.getFullYear() + "年" +
        (now.getMonth() + 1) + "月" + now.getDate() + "日" +
        now.getHours() + "時" + now.getMinutes() + "分" +
        now.getSeconds() + "秒</p>"
    );
},

Tags: , ,

AJAX

jQuery.ajax で JSONP

by WebSurfer 2017年5月3日 14:30

jQuery.ajax を使うと、JSONP(JSON with Padding・・・script タグを使用してクロスドメインでデータを取得する仕組み)を利用してデータを取得するのがかなり簡単にできるという話を書きます。

JSONP の応答を alert に表示

何が簡単になるかというと以下のことを jQuery.ajax が自動的にやってくれることです。

  1. jQuery.ajax を呼び出した時点で script タグを生成し DOM ツリーへ追加。(本来は、src 属性に呼出先の URL を設定した script タグを生成して DOM ツリーに追加するためのスクリプトを自力で書かなければなりません)
  2. 呼出先の URL にコールバックの名前を指定するクエリ文字列 &callback=jQuery... を追加。
  3. 応答が返ってくるとコールバックが呼び出され、引数に設定された JSON 文字列が JavaScript オブジェクトにパースされ、success: function (data) の data に渡される。

以下に具体例を書きます。MVC を例にとっていますが、もちろん Web Forms でも同様なことは可能です。

まず、ブラウザからの要求を受けて応答を返す窓口を作ります。MVC アプリの場合は、以下のようにアクションメソッドを使うのが簡単そうです。

具体的にどういう操作をしているかはコメントに書きましたのでそちらを見てください。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using AdventureWorksLT;
using System.Web.Script.Serialization;

namespace Mvc5App.Controllers
{
  public class AddressesController : Controller
  {
    // サンプルデータベース AdventureWorksLT をベースに
    // Visual Studio のウィザードで作った EDM を利用。
    // AdventureWorksLTEntities は DbContext を継承
    private AdventureWorksLTEntities db = 
                            new AdventureWorksLTEntities();

    // JSONP が呼び出すアクションメソッド
    public ActionResult Jsonp(int? id, string callback)
    {
      if (id == null)
      {
          return new HttpStatusCodeResult(
                                 HttpStatusCode.BadRequest);
      }

      // Address テーブルから指定された AddressID(主キー)
      // でレコードを検索。
      // 当該レコードの AddressID, AddressLine1, City フィー
      // ルドのデータを匿名クラスのオブジェクトとして取得
      var address = (from a in db.Address
         where a.AddressID == id
         select new { a.AddressID, a.AddressLine1, a.City }).
         FirstOrDefault();

      if (address == null)
      {
          return HttpNotFound();
      }

      // 取得したオブジェクトを JSON 文字列にシリアライズし
      // 変数 callback に名前が指定されたコールバックメソッ
      // ドの引数に設定。
      // code は「コールバック(JSON 文字列)」という文字列に
      // なる
      JavaScriptSerializer ser = new JavaScriptSerializer();
      string code = string.Format("{0}({1})", 
                          callback, ser.Serialize(address));

      // 文字列「コールバック(JSON 文字列)」を返す。その際
      // ヘッダに Content-Type: application/x-javascript; が
      // 付与される
      return JavaScript(code);
    }
    // ・・・中略・・・
  }
}

上記のアクションメソッドを、例えば /Addresses/Jsonp?id=25&callback=name で呼び出すと、以下の応答が返ってきます。

name({"AddressID":25,"AddressLine1":"9178 Jumping St.","City":"Dallas"})

呼び出し側(View)のコードは以下のようになります。上にも書きましたが、script 要素を DOM に追加するコードを書く必要はありません。以下のコードだけで jQuery.ajax が自動的にやってくれます。

<input type="text" id="tb1" />
<input type="button" value="検索" onclick="btn_click()" />

@section Scripts {
  <script type="text/javascript">
  //<![CDATA[
    function btn_click() {
      var address = '/Addresses/Jsonp?id=' + $("#tb1").val();

      $.ajax({
          url: address,
          dataType: 'jsonp',
          success: function (data) {
              alert(data.AddressID + ', ' +
                    data.AddressLine1 + ', ' + 
                    data.City);
          }
      });
    }
  //]]>
  </script>    
}

上記には jQuery の外部スクリプトファイルの参照が書いてありませんが、Visual Studio 2015 のテンプレートで自動生成した _Layout.cshtml を利用していますので、デフォルトで取り込まれるようになっています。

_Layout.cshtml を利用していますので、インラインのスクリプトは、上記のように View に @section Scripts { ... } を追加して、その中に書くのが適当です。header タグ内に書くと jQuery の外部スクリプトファイルが取り込まれる前になってしまいますので注意してください。

コールバックのメソッド名は、上にも書きましたが、jQuery.ajax がクエリ文字列として自動的に自動的にデフォルトの名前を追加しますので、それを利用しています。(デフォルトの名前では不都合がある場合は指定することも可能です)

data には、コールバックの引数の JSON 文字列が JavaScript オブジェクトに変換されて渡されます。なので、上記のようにプロパティを使ってデータを取得することができます。

Tags: ,

AJAX

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年2月  >>
28293031123
45678910
11121314151617
18192021222324
25262728123
45678910

View posts in large calendar