- 追加された行はこの色です。
- 削除された行はこの色です。
#title(Microsoft ASP.NET AJAXを使う5)
#navi(.NET プログラミング研究)
#navi(.NETプログラミング研究)
#contents
*Microsoft ASP.NET AJAXを使う5 [#a70e9aed]
**WebサービスのメソッドをJavaScriptから呼び出す [#p124f866]
これまではUpdatePanelコントロールを中心にASP.NET AJAXを説明してきました。それらの方法では、Webサービスを自分で作成する必要がありませんでした。
ここでもう一度原点に返って、自分で作成したWebサービスを利用してAjaxを実現する方法について説明します。ASP.NET AJAXを利用すれば、これも驚くほど簡単です。
ここでは、第78号で紹介した「[[.NETの機能を使用せずに、Ajaxを使用する方法>../78#nodotnet]]」と全く同じ事を行うアプリケーションをASP.NET AJAXを利用して作成します。
***Webサービス [#s3668658]
まずは、Webサービスの作成です。「[[.NETの機能を使用せずに、Ajaxを使用する方法>../78#nodotnet]]」で紹介したWebサービスのクラスに、新たに[[ScriptServiceAttribute属性>MSDN:ScriptServiceAttribute クラス]]を適用します。それ以外は、変更なしです。ファイル名は、"WebService.asmx"とします。
#code(vbnet){{
<%@ WebService Language="VB" Class="WebService" %>
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
<WebService(Namespace := "http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<System.Web.Script.Services.ScriptService> _
Public Class WebService
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function GetYoupackFee(ByVal packSize As Integer) As Integer
'(省略)
End Function
End Class
}}
#code(csharp){{
<%@ WebService Language="C#" Class="WebService" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService
{
[WebMethod]
public int GetYoupackFee(int packSize)
{
//(省略)
}
}
}}
「[[.NETの機能を使用せずに、Ajaxを使用する方法>../78#nodotnet]]」では、Web.configを書き換えてWebサービスにHTTP GETでアクセスできるようにしましたが、その必要はありません。
ただし、Web.configに以下のような設定を追加して、ScriptHandlerFactory HTTPハンドラを登録する必要があります。もしVisual Studio 2008でプロジェクトを作成したか、Visual Studio 2005で「ASP.NET AJAX-Enabled Web Site」テンプレートを使用してプロジェクトを作成したのであれば、この設定はすでにされていますので、気にする必要はありません。
#pre{{
<system.web>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" type="System.Web.Script.Services.ScriptHandlerFactory" validate="false"/>
</httpHandlers>
<system.web>
}}
***ページ [#zcd2385f]
次に、Webサービスを呼び出すページを作成します。前回との違いは、[[ScriptManagerコントロール>MSDN:ScriptManager クラス]]を配置する点です。
そして、ScriptManagerの[[Servicesプロパティ>MSDN:ScriptManager.Services プロパティ]]に先ほど作成したWebサービスを追加します。また、ScriptManagerのScriptsプロパティにJavaScriptファイルを追加します。
#code(vbnet){{
<%@ Page Language="VB" %>
<!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 id="Head1" runat="server">
<title>ゆうパック送料検索</title>
</head>
<body>
<h1>ゆうパック送料検索</h1>
<p>同一都道府県内への配達、重量30kgまで</p>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="ajax.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="WebService.asmx" />
</Services>
</asp:ScriptManager>
<asp:Label
ID="Label1"
runat="server"
Text="荷物の大きさ(縦・横・高さの合計): "
AssociatedControlID="PackageSizeList">
</asp:Label>
<asp:DropDownList
ID="PackageSizeList"
runat="server">
<asp:ListItem Value="0">(選択してください)</asp:ListItem>
<asp:ListItem Value="60">60cmまで</asp:ListItem>
<asp:ListItem Value="80">80cmまで</asp:ListItem>
<asp:ListItem Value="100">100cmまで</asp:ListItem>
<asp:ListItem Value="120">120cmまで</asp:ListItem>
<asp:ListItem Value="140">140cmまで</asp:ListItem>
<asp:ListItem Value="160">160cmまで</asp:ListItem>
<asp:ListItem Value="170">170cmまで</asp:ListItem>
</asp:DropDownList>
<br />
<asp:Label ID="ResultLabel" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
}}
#code(csharp){{
<%@ Page Language="C#" %>
<!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 id="Head1" runat="server">
<title>ゆうパック送料検索</title>
</head>
<body>
<h1>ゆうパック送料検索</h1>
<p>同一都道府県内への配達、重量30kgまで</p>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="ajax.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="WebService.asmx" />
</Services>
</asp:ScriptManager>
<asp:Label
ID="Label1"
runat="server"
Text="荷物の大きさ(縦・横・高さの合計): "
AssociatedControlID="PackageSizeList">
</asp:Label>
<asp:DropDownList
ID="PackageSizeList"
runat="server">
<asp:ListItem Value="0">(選択してください)</asp:ListItem>
<asp:ListItem Value="60">60cmまで</asp:ListItem>
<asp:ListItem Value="80">80cmまで</asp:ListItem>
<asp:ListItem Value="100">100cmまで</asp:ListItem>
<asp:ListItem Value="120">120cmまで</asp:ListItem>
<asp:ListItem Value="140">140cmまで</asp:ListItem>
<asp:ListItem Value="160">160cmまで</asp:ListItem>
<asp:ListItem Value="170">170cmまで</asp:ListItem>
</asp:DropDownList>
<br />
<asp:Label ID="ResultLabel" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
}}
***ServiceReference.InlineScriptプロパティ [#s2d782c2]
この例では、Servicesプロパティに追加する[[ServiceReferenceオブジェクト>MSDN:ServiceReference クラス]]には[[Pathプロパティ>MSDN:ServiceReference.Path プロパティ]]の値しか指定しておらず、[[InlineScriptプロパティ>MSDN:ServiceReference.InlineScript プロパティ]]はデフォルトのfalseのままとしました。
InlineScriptプロパティがfalseのときは、後述する[[プロキシクラス>#proxyclass]]を作成するJavaScriptを外部ファイルとして(<javascript>タグのscr属性に設定して)呼び出します。InlineScriptプロパティがtrueのときは、ページ内にJavaScriptが埋め込まれます。また、InlineScriptプロパティがtrueのときは、Pathプロパティは相対パスである必要があります。
つまり、InlineScriptプロパティがfalseのときは、サーバーへの要求数が増えますが、ブラウザのキャッシュ機能が使われる可能性があり、trueのときは、ページのサイズが大きくなります。
***JavaScript [#i5a7b6ad]
最後に、Webサービスを呼び出すJavaScriptを作成します。前回とほぼ同じ事を行っていますが、劇的にシンプルになることをご確認ください。
#code(javascript){{
var msgElement, listElement;
//アプリケーションのloadイベントハンドラを追加
Sys.Application.add_load(ApplicationLoad)
//アプリケーションのloadイベントハンドラ
function ApplicationLoad(sender, args)
{
msgElement = $get('ResultLabel');
listElement = $get('PackageSizeList');
//DropDownListのonchangeイベントハンドラを追加する
$addHandler(listElement, 'change', onChange);
}
//リストのonchangeイベントハンドラ
function onChange()
{
if (listElement.value == "0")
{
msgElement.innerHTML = "";
return;
}
msgElement.innerHTML = "読み込み中...";
//Webサービスにアクセスする
WebService.GetYoupackFee(listElement.value, OnSucceeded, OnFailed);
}
//成功したとき
function OnSucceeded(result, userContext, methodName)
{
msgElement.innerHTML = "料金は " + result + "円";
}
//失敗したとき
function OnFailed(error, userContext, methodName)
{
if(error != null)
{
msgElement.innerHTML = "エラー: " + error.get_message();
}
}
//スクリプトの読み込みが完了したことをScriptManagerに知らせる
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
}}
loadイベントハンドラでは、DropDownList"PackageSizeList"のonchangeイベントハンドラを追加しています。前回はaddEventListenerを使うか、attachEventを使うかなどを調べていましたが、ASP.NET AJAXでは[[$addHandler>MSDN:Sys.UI.DomEvent の $addHandler メソッド]]を使って一発でできます。
***プロキシクラス [#proxyclass]
onchangeイベントハンドラでWebサービスを呼び出しています。ご覧の通り、まるでWebサービスのメソッドをJavaScriptから直接呼び出しているかのように、Webサービスにアクセスできます。この例ではWebサービスのクラスに名前空間がありませんでしたが、名前空間がある場合は名前空間も付けて呼び出す必要があります(例えば名前空間が「MyNameSpace」であれば、「MyNameSpace.WebService.GetYoupackFee(...)」のように)。
なぜこれだけ簡単にWebサービスメソッドを呼び出せるかというと、ASP.NET AJAXが自動的に[[プロキシクラス>MSDN:生成されたプロキシ クラス]]を作成してくれるからです。この例では、WebServiceクラスがプロキシクラスです。プロキシクラスにはWebサービスメソッドを呼び出すためのメソッドが作成されており、メソッドの名前は対応するWebサービスメソッドと同じです。
[[プロキシクラスのメソッド>MSDN:生成されたプロキシ クラスの Web サービス メソッド]]のパラメータには、まず対応するWebサービスメソッドのパラメータを渡します。上記の例ではWebサービスメソッドのパラメータ数は1つだけですが、複数ある場合はその数だけ渡します。
その次のパラメータには、Webサービスへの要求が成功したときに呼び出されるコールバック関数を指定します。上の例では、OnSucceeded関数がそれです。このパラメータは省略できます。
その次のパラメータには、失敗したときに呼び出されるコールバック関数を指定します。上の例では、OnFailed関数がそれです。このパラメータも省略できます。
上の例では省略しましたが、さらに次のパラメータに、ユーザーコンテキストを指定できます。ここで指定したコンテキストは、成功/失敗コールバック関数で取得できます。Webサービスメソッドがどこから呼び出されたのかを区別したいときなどに有用です。
プロキシクラスの[[timeoutプロパティ>MSDN:生成されたプロキシ クラスの timeout プロパティ]]に、タイムアウトの時間を設定することもできます。
補足:成功コールバック関数、失敗コールバック関数、ユーザーコンテキストは、プロキシクラスの[[defaultSucceededCallbackプロパティ>MSDN:生成されたプロキシ クラスの defaultSucceededCallback プロパティ]]、[[defaultFailedCallbackプロパティ>MSDN:生成されたプロキシ クラスの defaultFailedCallback プロパティ]]、[[defaultUserContextプロパティ>MSDN:生成されたプロキシ クラスの defaultUserContext プロパティ]]に設定することもできます。
[[成功コールバック関数>MSDN:生成されたプロキシ クラスの成功コールバック関数]]では、Webサービスメソッドが返した値(resultパラメータ)、ユーザーコンテキスト(userContextパラメータ)、呼び出されたWebサービスメソッド名(methodNameパラメータ)を取得できます。
[[失敗コールバック関数>MSDN:生成されたプロキシ クラスの失敗コールバック関数]]では、エラー情報をerrorパラメータで取得できます。これは、[[Sys.Net.WebServiceErrorオブジェクト>MSDN:Sys.Net.WebServiceError クラス]]です。例えばエラーメッセージは、上記の例のように、[[messageプロパティ>MSDN:Sys.Net.WebServiceError の message プロパティ]]で取得できます。
***Sys.Net.WebServiceProxyクラス [#saa0da19]
プロキシクラスは、[[Sys.Net.WebServiceProxyクラス>MSDN:Sys.Net.WebServiceProxy クラス]]から派生しています。クライアント側で呼び出すWebサービスのパスが直前まで分からないとき(ScriptManagerのServicesに登録されていないWebサービスを呼び出すとき)には、WebServiceProxyクラスを直接使ってWebサービスにアクセスすることができます。
上記の例を改造して、ScriptManagerのServicesにWebサービスが登録されていないときに、WebService.GetYoupackFeeメソッドを呼び出すスクリプト(メソッドを呼び出す部分のみ)を以下に示します。
#code(javascript){{
Sys.Net.WebServiceProxy.invoke('WebService.asmx',
'GetYoupackFee',
false,
{'packSize':listElement.value},
OnSucceeded,
OnFailed,
null,
60000);
}}
このように[[WebServiceProxy.invokeメソッド>MSDN:Sys.Net.WebServiceProxy の invoke メソッド]]でWebサービスメソッドを呼び出せます。3番目のパラメータは、Webサービスへの要求をGETで行うかを指定します。falseの場合はPOSTとなります。4番目のパラメータには、Webサービスに渡すパラメータを連想配列で指定します。7番目のパラメータはユーザーコンテキストです。8番目のパラメータはタイムアウト時間(単位はミリ秒)です。
**参考 [#c453a28c]
-[[MSDN:ASP.NET AJAX での Web サービス]]
**コメント [#d2c25706]
#comment
//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2008-07-14 (月) 02:15:07,DOBON!,2008-07-14 (月) 02:15:07,DOBON!)