Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Silverlightとサーバーサービスの連携

Silverlight 2で作成する業務アプリケーション入門(6)

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

 Silverlightはブラウザにダウンロードして動作するアプリケーションです。業務アプリケーションでメインとなる、データの更新や検索、他システムとの連携といった処理を行うには、Silverlightとサーバーサービスで連携する必要があります。本稿は、そのための基礎について解説します。

はじめに

 Silverlightはブラウザにダウンロードして動作するアプリケーションです。

 つまり業務アプリケーションでメインとなる、データの更新や検索、他システムとの連携といった処理を記述するためには、Silverlightとサーバーサービスとの間で通信を行い、データをやりとりする必要があります。

 Silverlight 1.0ではJavaScriptしか利用できなかったため、AJAXでよく利用されるXMLHttpRequestオブジェクトを使用してサーバとの通信を行っていました。Silverlight 2.0(以降、断りのない限り「Silverlight」と表記)では.NET Frameworkのサブセットとして動作するため、WebClientHttpRequest/HttpResponseSocketクラスといったネットワークAPIを利用してサーバーサービスとの連携プログラムを簡単に作成することができます。

 今回はSilverlightとサーバーサービスの連携を行うための基礎について解説します。

対象読者

 Silverlight開発初心者。

SilverlightのネットワークAPI

 最初に述べたように、Silverlightでは.NET Frameworkの各種クラスを使用してネットワークプログラムを記述することができます。

 Silverlightでは、次の方法でサーバーサービスとの連携を行うことができます。

  • ASP.NETサービスリファレンスの利用
  • System.Net.WebClientの利用
  • System.Net.HttpRequest/HttpResponseの利用
  • System.Net.Socketの利用

 これらはSilverlightでも.NET Frameworkのサブセットとして定義されています。

 動作はほぼ同じですが、Silverlightではサポートされていないメソッドがあるため、MSDNライブラリで詳しい仕様を確認してください。

ASP.NETサービスリファレンスの利用

 ASP.NETサービスリファレンスは、WSDLというWebサービスの定義情報から.NETのクラスで利用するためのプロキシクラスを作成して、Webサービスを呼び出す方法です。

 SilverlightでもVisual Studioを利用することで、簡単にプロキシクラスを作成してWebサービスを呼び出すことができます。

 それでは、Visual Studioから新しいSilverlightアプリケーションを作成し、SilverlightをホストしているWebSiteにWebServiceを追加してみましょう。

1.プロジェクトの作成

 まずはSilverlightとSilverlightをホストするためのASP.NETのプロジェクトを作成します。

 Visual Studioメニューの[ファイル]-[新規作成]-[プロジェクト]から[Silverlight]を選択し、Silverlightアプリケーションを作成します。その際、プロジェクト名はSilverlightDemoとします。

プロジェクトの作成
プロジェクトの作成

 [OK]ボタンをクリックすると、SilverlightをホストするためのASP.NETページを作成するか訪ねられるので、そのまま[OK]をクリックしてASP.NETページを作成してください。

2.Webサービスの作成

 次にSilverlightから呼び出すためのWebサービスを作成します。

 作成されたSilverlightDemo.Webプロジェクトを右クリックして[追加]-[新しい項目]から[WebService]を選択します。

Webサービスの追加
Webサービスの追加

 Webサービスを作成すると、Visual Studioが自動生成したHello Worldという文字列を返却するHelloWorldメソッドが定義されていますので、今回はこのサンプルを利用します。

3.SilverlightからWebサービスの参照

 Silverlightから「2.」で作成したWebサービスを呼び出すために、サービスリファレンスを作成します。

 SilverlightDemoプロジェクトを右クリックし、サービス参照の追加から「2.」で作成したWebサービスを参照します。

Webサービスの参照
Webサービスの参照

 今回は、SilverightをホストするASP.NETと同じアプリケーションでWebサービスをホストするため、[探索]ボタンをクリックすることで「2.」で作成したWebService1を検索できます。

ここで、「アドレスからメタデータをダウンロードするときに、エラーが発生しました。有効なアドレスを入力していることを確認してください。」といったメッセージが表示された場合は、Silverlightで参照する前に一度WebサービスをVisual Studioからデバック実行して見てください。

 また、Silverlightをホストするドメイン以外のWebサービスを参照する場合は、後述するクロスドメインの制限がありますので注意してください。

4.Webサービスの呼び出し

 「3.」で作成したWebサービスのプロキシクラスからWebService1のHelloWorldメソッドを呼び出します。

 SilverlightプロジェクトのPage.xamlのXAMLにWebサービスを呼び出すためのボタンを記述します([リスト1])。

[リスト1]ボタンの配置(Page.xaml)
<UserControl x:Class="SilverlightDemo.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Height="30" Margin="100,100,100,0"
                Content="サービスの呼び出し" Click="Button_Click"/>
    </Grid>
</UserControl>

 これでWebサービスを呼び出すための準備が終わったので、C#から作成したWebサービスを呼び出してみましょう。

 Page.xaml.csを開き、Pageクラスにボタンクリックのイベントハンドラを作成します([リスト2])。

[リスト2]イベントハンドラの記述(Page.xaml.cs)
private void Button_Click(object sender, RoutedEventArgs e)
{
    // (1) Webサービスプロキシの作成
    var service = new ServiceReference1.WebService1SoapClient();

    // (2) 呼び出し終了時のイベントハンドラの設定
    service.HelloWorldCompleted += (serviceObject, serviceEvent)=>
        {
            if (serviceEvent.Error != null)
                MessageBox.Show("通信エラー:" + serviceEvent.Error.ToString());
            else
                MessageBox.Show(serviceEvent.Result);
        };
    // (3) Webサービスの呼び出し
    service.HelloWorldAsync();
}

 Silverlightアプリケーションでは、Webサービスの呼び出しは非同期(Async)のみサポートされています。このためSilverlightでWebサービスを呼び出す場合は次の手順でサービスを呼び出す必要があります。

  1. Webサービスプロキシの作成
  2. 呼び出し終了時のイベントハンドラの設定
  3. Webサービスの呼び出し

 これでWebサービスを呼び出すためのコードが記述できたので、Visual Studioメニューの[デバック]-[デバックの開始]から[Silverlight]を起動してみましょう。

 起動すると次のような画面が表示されるので、サービスの呼び出しボタンをクリックしてください。メッセージボックスが表示され、HelloWorldが表示されるはずです。

Webサービスの呼び出し結果
Webサービスの呼び出し結果

 自動生成されたプロキシクラスを使うことで、ネットワークプログラムやXMLからのデータの読み出しを意識せず、静的に型付けされたコードからサーバーサービスを呼び出すことができます。

 サーバー側をASP.NETで作成する場合、Silverlightから呼び出すサービスをASP.NETWebサービス/ASP.NET WCFで作成し、サービスリファレンスによる参照を行うことで安全に、簡単にアプリケーションを作成することができます。

ASP.NET Webサービスを呼び出す際の注意点

 ここまででSilverlightからWebサービスを呼び出すことが可能になりました。

 SilverlightからWebサービスを呼び出す際にはいくつかの注意点がありますので、ここで触れておきましょう。

WebServiceの設定情報

 サンプルではVisual Studioのテスト用Webサーバー上にWebサービスを配置しましたが、Silverlightを本番環境に移送する場合は、Webサービスの参照も本番のWebサービスに切り替える必要があります。

 WindowsアプリケーションやASP.NETではapp.configやweb.configといった設定ファイルにWebサービスへの参照情報を記述しましたが、Silverlightでもサービスリファレンスの設定時に作成されるServiceReference.ClientConfigファイルにWebサービスへの参照情報が記述されています。

 本番環境に移送する場合には、このファイルのendpoint情報を変更しSilverlightを再パッケージ(XAP)化する必要があります。このため実際の開発時にはテスト用と本番用の2つのServiceReference.ClientConfigを用意したり、MSBuildのビルド時に定義を置き換えたりといった工夫が必要になると思います。

 MSBuildのビルド時にXMLの定義情報を書き換える方法がNAndu氏の記事『XmlMassUpdateタスクを使用したデプロイ』にまとまっています。ぜひ参考にしてください。

クロスドメインリクエスト

 もう一つ大きな問題になるのが、Silverlightから違うドメインのサービスを呼び出す際に問題になるクロスドメインリクエストの問題です。

 Silverlightの既定ではSilverlightをホストしているドメインにあるサービス以外へのリクエストは禁止されています。もしSilverlightからの接続が許可されていない他ドメインにあるWebサービスを呼び出そうとした場合、実行時にCommunicationExceptionの例外が発生します。

他ドメインへのリクエストの禁止
他ドメインへのリクエストの禁止

 他ドメインに対するリクエストを無制限に許してしまうと、悪意を持ったプログラムは、利用者が気づかないうちに他ドメインのサービスに対し、サービス拒否攻撃(DDoS)やDNS Rebinding、逆トンネルといった攻撃を行ってしまう可能性があります。

 Silverlightでは、Webサービスをホストするドメインのサーバーで、Silverlightからのリクエストを許可することを表すドメイン間ポリシーファイルをサーバーのルートサイトに公開してもらうことで、クロスドメイン間のリクエストが許可される設計になっています。

 つまり業務で他システムのサービスを利用する場合、対象サービスのルートサイトにドメイン間ポリシーファイルが提供されているか、提供してもらうことが可能かを確認・検討することが必要となります。

 また、Visual StudioのテストWebサーバーでSilverlightをホストしている場合、「http://localhost:2591」といったようにポートが自動的に割り振られます。この状態で、「http://localhost」に存在するWebサービスに対してリクエストを発行すると、上記のCommunicationExceptionが発生してしまいます。

 この場合、localhostのルートサイト(既定ではC:\Inetpub\wwwroot)に後述するドメイン間ポリシーファイルを配置する必要があります。

 Visual StudioのテストWebサーバーがバインドするポート番号は次の方法で確認・設定を行うことができます。Visual Studioのソリューションエクスプローラから、該当Webサイトプロジェクトを右クリックして、[プロパティ]-[Web(ポート番号の確認)]を開いてください。

ポート番号の確認
ポート番号の確認

ドメイン間ポリシーファイル

 Silverlightで使用されるドメイン間ポリシーファイルは、Flashで使用されているcrossdomain.xmlとSilverlightで新たに採用されたclientaccesspolicy.xmlの2種類があります(Silverlightではcrossdomain.xmlはすべてのドメインに対しアクセスを許可する/しないの2択しか設定できません)。

 Silverlightではクロスドメインへのリクエストがあった場合、まず始めに対象ホストのルートサイトからclientaccesspolicy.xmlを検索します。clientaccesspolicy.xmlが存在しない場合は続いてcrossdomain.xmlを検索し、発見されたドメイン間ポリシーファイルを元にアクセス制御を行います。読み込まれたドメイン間ポリシーファイルの設定はブラウザセッションの間有効になります。テスト中にドメイン間ポリシーファイルを書き換えても、ブラウザを再起動するまで変更が有効になりませんので注意してください。[リスト3]にclientaccesspolicy.xmlの例を示します。

[リスト3]clientaccesspolicyによるアクセス制限1(clientaccesspolicy.xml)
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy >
      <allow-from http-request-headers="SOAPAction">
        <domain uri="http://localhost:2591"/>
      </allow-from>
      <grant-to>
        <resource path="/webservice1/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 この例では、「http://localhost:2591」ドメインからのSOAPActionについて、webservice1ディレクトリにあるWebサービスについてアクセスが許可されています。

 また、例えばすべてのドメインから、すべての要求に応えるためには[リスト4]のように構成を変更します。

[リスト4]clientaccesspolicyによるアクセス制限2(clientaccesspolicy.xml)
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy >
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 clientaccesspolicy.xmlを配置して動作を確かめてください。問題なくサービスを呼び出すことはできましたか?

ASP.NETサービス以外との連携

 Silverlightでは、前述のサービスリファレンスだけでなく、System.Net.WebClientSystem.Net.HttpRequest/HttpResponseを使ったHTTPの通信もサポートされています。

 例えば、楽天WEB SERVICEでは、登録した開発者が楽天のサービスをプログラムから呼び出せるように、さまざまなサービスAPIが公開されています。今回は楽天のAPIを利用してSilverlightから商品の検索を行うアプリケーションの作成を行います。

 楽天のWebサービスは、http://dynamic.rakuten.co.jp上にホストされており、すべてのドメインからの応答に答えられるようなcrossdomain.xmlが配置されているため、Silverlightから直接Webサービスを呼び出すことができます。

APIの確認と作成するリクエスト文字列

 今回は楽天のAPIから商品検索APIを使用して、ユーザーが指定したキーワードで楽天の商品を検索するアプリケーションを作成します。

 提供APIのページでAPI仕様を確認すると、商品検索を行うためには表1の4項目が必須であることが確認できます。

[表1]アイテム検索のパラメータ
No パラメータ名 説明 設定値
1 developerId 開発者固有のID (楽天WEB SERVICEサイトで取得)
2 operation APIの操作名 ItemSearch
3 keyword UTF8でエンコードした検索キーワード 任意
4 version APIバージョン 2009-02-03

 例えば、Silverlightという検索キーワードで検索を行う場合、[リスト5]のようなリクエストを発行します(developerIdはユーザーごとに取得した物を使用してください)。

[リスト5]アイテム検索に使用するURL
http://api.rakuten.co.jp/rws/1.13/rest?developerId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&operation=ItemSearch&keyword=Silverlight&version=2009-02-03"

 Webブラウザで上記URLをリクエストすると、検索結果のXMLが返されることが確認できると思います。

画面の作成

 検索を行い結果を表示するための画面を作成していきましょう。

 今回は、検索エリアに検索を行うテキストボックスと、検索を実行するボタンを配置し、結果エリアに一覧を表示するためのデータグリッドを配置します。画面の完成イメージは次のようになります。

画面の完成イメージ
画面の完成イメージ

 今回はDataGridを使用するので、Visual StudioからSystem.Windows.Controls.Dataのアセンブリを参照しておきましょう。

System.Windows.Controls.Dataの参照
System.Windows.Controls.Dataの参照

 次に画面のデザインを行います。Visual StudioのソリューションエクスプローラからPage.xamlを右クリックして、[ExpressionBlendで開く]を選択してください。

Expression Blendの起動
Expression Blendの起動

 検索用のテキストボックスとボタンを配置します。

 ツールボックスからテキストボックスとボタンを画面に配置し、テキストボックスに「txtSearch」という名前を設定して、ボタンのイベントハンドラに「Button_Click」を設定します。

テキストボックスとボタンの配置
テキストボックスとボタンの配置

 データグリッドは標準ではExpression Blendのツールボックスに表示されていないので、アセットライブラリからDataGridを選択してツールボックスにDataGridを追加します。

ツールボックスにDataGridを追加
ツールボックスにDataGridを追加

 データグリッドに「商品一覧」という名前を付けます。

DataGridをページに追加
DataGridをページに追加
[リスト6]ボタンの配置(Page.xaml)
<UserControl x:Class="SilverlightDemo.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" mc:Ignorable="d">
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBox Height="30" HorizontalAlignment="Left" Margin="8,20,0,0" VerticalAlignment="Top" Width="150" Text="" TextWrapping="Wrap" x:Name="txtSearch"/>
        <Button Height="30" Margin="200,20,120,0" VerticalAlignment="Top" Content="検索" Click="Button_Click"/>
        <Controls:DataGrid Margin="8,70,8,26" x:Name="商品一覧"/>

    </Grid>
</UserControl>

検索ロジックの作成

 次に、ボタンクリックのイベントハンドラを実装して、実際に楽天WEB SERVICEを呼び出してみましょう。

 SilverlightでHTTPリクエストを行う際には、WebClientクラスか、HttpRequest/HttpResponseクラスを用いてリクエストを行うことができます。ここでは、WebClientクラスのDownloadStringAsyncメソッドを利用してリクエストを行います。

 [リスト7]は[リスト5]で行ったリクエストをプログラムで記述したものです。

[リスト7]楽天アイテム検索APIの呼び出し(Page.xaml.cs)
private void Button_Click(object sender, RoutedEventArgs e)
{
    // (1).リクエストURLの組み立て
    var apiAddress = "http://api.rakuten.co.jp/rws/1.13/rest";
    var developerId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    var operation = "ItemSearch";
    var keyword = HttpUtility.UrlEncode(txtSearch.Text);
    var version = "2009-02-03";
    var requestUri = string.Format(
            "{0}?developerId={1}&operation={2}&keyword={3}&version={4}",
            apiAddress, developerId, operation, keyword, version);

    var client = new WebClient();

    // (2).ダウンロード完了時のイベントハンドラを登録
    client.DownloadStringCompleted += RakutenDownloadStringCompleted;

    // (3).サービスのリクエスト
    client.DownloadStringAsync(new Uri(requestUri), null);
}

// (4).リクエスト内容の表示
private void RakutenDownloadStringCompleted(
    object sender,
    DownloadStringCompletedEventArgs e)
{
    if (webEvent.Error != null)
        MessageBox.Show("通信エラー:" + webEvent.Error);
    else
        MessageBox.Show("結果:" + webEvent.Result);
}

 このプログラムは次の順序で動作します。

  1. リクエストURLの組み立て
  2. ダウンロード完了時のイベントハンドラを登録
  3. サービスのリクエスト
  4. リクエスト内容の表示

 SilverlightのWebClientクラスでもサービスリファレンスと同じように非同期リクエストのみのサポートになるため、リクエストの前に、ページの読み込みが終了タイミングで動作するイベントハンドラを登録してから、DownloadStringAsyncでGETリクエストを開始します。サービスリファレンスと同様にリクエストするURLが許可されていない場合はSystem.Security.SecurityExceptionが発生します。

 [リスト8]は「4. リクエスト内容の表示」の部分を変更して、データグリッドに結果を表示するように変更した例です。

[リスト8]データグリッドに結果データの表示(Page.xaml.cs)
public class 商品
{
    public string 商品名 { get; set; }
    public string 価格 { get; set; }
    public string URL { get; set; }
}

// 4.リクエスト内容の表示
private void RakutenDownloadStringCompleted(
    object sender,
    DownloadStringCompletedEventArgs e)
{
    if (e.Error != null)
    {
        MessageBox.Show(e.Error.ToString());
        return;
    }

    // (5).結果を解析して商品の一覧を作成する。
    var xml = XElement.Parse(e.Result);
    var result = from item in xml.Descendants("Item")
                 select new 商品
                 {
                     商品名 = item.Element("itemName").Value,
                     価格 = item.Element("itemPrice").Value,
                     URL = item.Element("itemUrl").Value,
                 };
    商品一覧.ItemsSource = result.ToList();
}
  1. 結果を解析して商品の一覧を作成する。

 ここでは、Webサービスから取得したXMLを解析して、商品のリストを作成しています。楽天アイテム検索のページで出力XMLの形式を確認すると、Item要素以下に商品の一覧が格納されるため、LINQ to XMLを用いてItem要素以下のitemName、itemPrice、itemUrlを取得し、商品クラスに格納しています。

 その後、DataGridのItemsSourceに作成したリストを結びつけています。

 この時に、LINQ to XMLの結果をそのままItemsSourceに代入すると、DataGridには何も表示されません。これはLINQ to XMLでは検索結果が必要になるまでリストの検索が実行されないためです。DataGridに結果を表示するためには、ItemsSourceの設定時に、ToListメソッドで実体化させたリストを割り当てる必要があります。

その他のWebサービスにアクセスする場合

 SilverlightのWebClientでは、基本認証がサポートされていません。また、前述のドメイン間ポリシーファイルで許可されているサイト以外にはアクセスできません。

 筆者も最初はサンプルにTwitterクライアントを考えていたのですが、Twitterが基本認証を必要とすること、crossdomain.xmlで自サイト以外のアクセスを禁止していることから作成を断念しました。

 このようなサービスを利用したい場合は、SilverlightをホストしているサイトにASP.NET WebServiceを作成し、サーバー側で必要なサービスに対しアクセスし、Silverlight側にはサーバー側でリクエストを行った結果をブリッジするような仕組みを考える必要があります。

まとめ

 今回は、サービスリファレンスとWebClientを使ったサーバーサービスとの連携について解説を行いました。Silverlightでは.NET Frameworkの機能が利用できるため、HTML+Javascriptでは実現できなかったクロスドメインや、複雑だったXMLの解析といった作業を簡単に行えることが理解いただけたのではないでしょうか。

 Silverlightと言うと、動画やアニメーションなどのメディア方向ばかりに注目が集まっている感じがありますが、業務アプリケーションで利用する上では、.NET Frameworkの機能を利用してプログラムが作成できるというメリットこそが一番大きな特徴です。

 ぜひもう一度この連載を確認してください。

 Silverlightを業務アプリケーションで利用する際の助けになれればと願っています。

参考資料

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

  • WINGSプロジェクト かるあ (杉山 洋一)(カルア(スギヤマ ヨウイチ))

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5